1 /* GStreamer Matroska muxer/demuxer
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
6 *
7 * matroska-mux.c: matroska file/stream muxer
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
27 */
28
29 /**
30 * SECTION:element-matroskamux
31 * @title: matroskamux
32 *
33 * matroskamux muxes different input streams into a Matroska file.
34 *
35 * ## Example launch line
36 * |[
37 * gst-launch-1.0 -v filesrc location=/path/to/mp3 ! mpegaudioparse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
39 * |[
40 * gst-launch-1.0 -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
42 *
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <math.h>
50 #include <stdio.h>
51 #include <string.h>
52
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
56 #include <gst/pbutils/codec-utils.h>
57
58 #include "gstmatroskaelements.h"
59 #include "matroska-mux.h"
60 #include "matroska-ids.h"
61
62 #define GST_MATROSKA_MUX_CHAPLANG "und"
63
64 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
65 #define GST_CAT_DEFAULT matroskamux_debug
66
67 enum
68 {
69 PROP_0,
70 PROP_WRITING_APP,
71 PROP_DOCTYPE_VERSION,
72 PROP_MIN_INDEX_INTERVAL,
73 PROP_STREAMABLE,
74 PROP_TIMECODESCALE,
75 PROP_MIN_CLUSTER_DURATION,
76 PROP_MAX_CLUSTER_DURATION,
77 PROP_OFFSET_TO_ZERO,
78 PROP_CREATION_TIME,
79 PROP_CLUSTER_TIMESTAMP_OFFSET,
80 };
81
82 #define DEFAULT_DOCTYPE_VERSION 2
83 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
84 #define DEFAULT_MIN_INDEX_INTERVAL 0
85 #define DEFAULT_STREAMABLE FALSE
86 #define DEFAULT_TIMECODESCALE GST_MSECOND
87 #define DEFAULT_MIN_CLUSTER_DURATION 500 * GST_MSECOND
88 #define DEFAULT_MAX_CLUSTER_DURATION 65535 * GST_MSECOND
89 #define DEFAULT_OFFSET_TO_ZERO FALSE
90 #define DEFAULT_CLUSTER_TIMESTAMP_OFFSET 0
91
92 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
93 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
94
95 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
96 GST_PAD_SRC,
97 GST_PAD_ALWAYS,
98 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
99 );
100
101 #define COMMON_VIDEO_CAPS \
102 "width = (int) [ 1, MAX ], " \
103 "height = (int) [ 1, MAX ] "
104
105 /* FIXME:
106 * * require codec data, etc as needed
107 */
108
109 static GstStaticPadTemplate videosink_templ =
110 GST_STATIC_PAD_TEMPLATE ("video_%u",
111 GST_PAD_SINK,
112 GST_PAD_REQUEST,
113 GST_STATIC_CAPS ("video/mpeg, "
114 "mpegversion = (int) { 1, 2, 4 }, "
115 "systemstream = (boolean) false, "
116 COMMON_VIDEO_CAPS "; "
117 "video/x-h264, stream-format = (string) { avc, avc3 }, alignment=au, "
118 COMMON_VIDEO_CAPS "; "
119 "video/x-h265, stream-format = (string) { hvc1, hev1 }, alignment=au, "
120 COMMON_VIDEO_CAPS "; "
121 "video/x-divx, "
122 COMMON_VIDEO_CAPS "; "
123 "video/x-huffyuv, "
124 COMMON_VIDEO_CAPS "; "
125 "video/x-dv, "
126 COMMON_VIDEO_CAPS "; "
127 "video/x-h263, "
128 COMMON_VIDEO_CAPS "; "
129 "video/x-msmpeg, "
130 COMMON_VIDEO_CAPS "; "
131 "image/jpeg, "
132 COMMON_VIDEO_CAPS "; "
133 "video/x-theora; "
134 "video/x-dirac, "
135 COMMON_VIDEO_CAPS "; "
136 "video/x-pn-realvideo, "
137 "rmversion = (int) [1, 4], "
138 COMMON_VIDEO_CAPS "; "
139 "video/x-vp8, "
140 COMMON_VIDEO_CAPS "; "
141 "video/x-vp9, "
142 COMMON_VIDEO_CAPS "; "
143 "video/x-raw, "
144 "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }, "
145 COMMON_VIDEO_CAPS "; "
146 "video/x-prores, "
147 COMMON_VIDEO_CAPS "; "
148 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS "; "
149 "video/x-av1, " COMMON_VIDEO_CAPS ";"
150 "video/x-ffv, ffversion = (int) 1, " COMMON_VIDEO_CAPS)
151 );
152
153 #define COMMON_AUDIO_CAPS \
154 "channels = (int) [ 1, MAX ], " \
155 "rate = (int) [ 1, MAX ]"
156
157 /* FIXME:
158 * * require codec data, etc as needed
159 */
160 static GstStaticPadTemplate audiosink_templ =
161 GST_STATIC_PAD_TEMPLATE ("audio_%u",
162 GST_PAD_SINK,
163 GST_PAD_REQUEST,
164 GST_STATIC_CAPS ("audio/mpeg, "
165 "mpegversion = (int) 1, "
166 "layer = (int) [ 1, 3 ], "
167 COMMON_AUDIO_CAPS "; "
168 "audio/mpeg, "
169 "mpegversion = (int) { 2, 4 }, "
170 "stream-format = (string) raw, "
171 COMMON_AUDIO_CAPS "; "
172 "audio/x-ac3, "
173 COMMON_AUDIO_CAPS "; "
174 "audio/x-eac3, "
175 COMMON_AUDIO_CAPS "; "
176 "audio/x-dts, "
177 COMMON_AUDIO_CAPS "; "
178 "audio/x-vorbis, "
179 COMMON_AUDIO_CAPS "; "
180 "audio/x-flac, "
181 COMMON_AUDIO_CAPS "; "
182 "audio/x-opus; "
183 "audio/x-speex, "
184 COMMON_AUDIO_CAPS "; "
185 "audio/x-raw, "
186 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
187 "layout = (string) interleaved, "
188 COMMON_AUDIO_CAPS ";"
189 "audio/x-tta, "
190 "width = (int) { 8, 16, 24 }, "
191 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
192 "audio/x-pn-realaudio, "
193 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
194 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
195 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
196 COMMON_AUDIO_CAPS ";"
197 "audio/x-alaw, "
198 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
199 "audio/x-mulaw, "
200 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
201 "audio/x-adpcm, "
202 "layout = (string)dvi, "
203 "block_align = (int)[64, 8192], "
204 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
205 "audio/G722, "
206 "channels = (int)1," "rate = (int)16000; "
207 "audio/x-adpcm, "
208 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
209 );
210
211 static GstStaticPadTemplate subtitlesink_templ =
212 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
213 GST_PAD_SINK,
214 GST_PAD_REQUEST,
215 GST_STATIC_CAPS ("subtitle/x-kate; "
216 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
217 "application/x-usf; subpicture/x-dvd; "
218 "application/x-subtitle-unknown")
219 );
220
221 static gpointer parent_class; /* NULL */
222
223 /* Matroska muxer destructor */
224 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
225 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
226 static void gst_matroska_mux_finalize (GObject * object);
227
228 /* Pads collected callback */
229 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
230 GstCollectData * data, GstBuffer * buf, gpointer user_data);
231 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
232 GstCollectData * data, GstEvent * event, gpointer user_data);
233
234 /* pad functions */
235 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
236 GstObject * parent, GstEvent * event);
237 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
238 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
239 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
240
241 /* gst internal change state handler */
242 static GstStateChangeReturn
243 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
244
245 /* gobject bla bla */
246 static void gst_matroska_mux_set_property (GObject * object,
247 guint prop_id, const GValue * value, GParamSpec * pspec);
248 static void gst_matroska_mux_get_property (GObject * object,
249 guint prop_id, GValue * value, GParamSpec * pspec);
250
251 /* reset muxer */
252 static void gst_matroska_mux_reset (GstElement * element);
253
254 /* uid generation */
255 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
256
257 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
258 GstMatroskaTrackContext * context);
259 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
260 GstMatroskaTrackContext * context);
261 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
262 GstMatroskaTrackContext * context);
263 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
264 GstMatroskaTrackContext * context);
265 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
266 GstMatroskaTrackContext * context);
267 static void
268 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
269 gpointer data);
270 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
271 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
272 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
273
274 /* Cannot use boilerplate macros here because we need the full init function
275 * signature with the additional class argument, so we use the right template
276 * for the sink caps */
277 GType
gst_matroska_mux_get_type(void)278 gst_matroska_mux_get_type (void)
279 {
280 static GType object_type; /* 0 */
281
282 if (object_type == 0) {
283 static const GTypeInfo object_info = {
284 sizeof (GstMatroskaMuxClass),
285 NULL, /* base_init */
286 NULL, /* base_finalize */
287 (GClassInitFunc) gst_matroska_mux_class_init,
288 NULL, /* class_finalize */
289 NULL, /* class_data */
290 sizeof (GstMatroskaMux),
291 0, /* n_preallocs */
292 (GInstanceInitFunc) gst_matroska_mux_init
293 };
294 const GInterfaceInfo iface_info = { NULL };
295
296 object_type = g_type_register_static (GST_TYPE_ELEMENT,
297 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
298
299 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
300 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
301 }
302
303 return object_type;
304 }
305
306 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (matroskamux, "matroskamux",
307 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX, matroska_element_init (plugin));
308
309 static void
gst_matroska_mux_class_init(GstMatroskaMuxClass * klass)310 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
311 {
312 GObjectClass *gobject_class;
313 GstElementClass *gstelement_class;
314
315 gobject_class = (GObjectClass *) klass;
316 gstelement_class = (GstElementClass *) klass;
317
318 gst_element_class_add_static_pad_template (gstelement_class,
319 &videosink_templ);
320 gst_element_class_add_static_pad_template (gstelement_class,
321 &audiosink_templ);
322 gst_element_class_add_static_pad_template (gstelement_class,
323 &subtitlesink_templ);
324 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
325 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
326 "Codec/Muxer",
327 "Muxes video/audio/subtitle streams into a matroska stream",
328 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
329
330 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
331 "Matroska muxer");
332
333 gobject_class->finalize = gst_matroska_mux_finalize;
334
335 gobject_class->get_property = gst_matroska_mux_get_property;
336 gobject_class->set_property = gst_matroska_mux_set_property;
337
338 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
339 g_param_spec_string ("writing-app", "Writing application.",
340 "The name the application that creates the matroska file.",
341 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
343 g_param_spec_int ("version", "DocType version",
344 "This parameter determines what Matroska features can be used.",
345 1, 2, DEFAULT_DOCTYPE_VERSION,
346 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
347 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
348 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
349 "entries", "An index entry is created every so many nanoseconds.",
350 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
352 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
353 g_param_spec_boolean ("streamable", "Determines whether output should "
354 "be streamable", "If set to true, the output should be as if it is "
355 "to be streamed and hence no indexes written or duration written.",
356 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
357 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
358 g_param_spec_int64 ("timecodescale", "Timecode Scale",
359 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
360 GST_SECOND, DEFAULT_TIMECODESCALE,
361 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
362 g_object_class_install_property (gobject_class, PROP_MIN_CLUSTER_DURATION,
363 g_param_spec_int64 ("min-cluster-duration", "Minimum cluster duration",
364 "Desired cluster duration as nanoseconds. A new cluster will be "
365 "created irrespective of this property if a force key unit event "
366 "is received. 0 means create a new cluster for each video keyframe "
367 "or for each audio buffer in audio only streams.", 0,
368 G_MAXINT64, DEFAULT_MIN_CLUSTER_DURATION,
369 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 g_object_class_install_property (gobject_class, PROP_MAX_CLUSTER_DURATION,
371 g_param_spec_int64 ("max-cluster-duration", "Maximum cluster duration",
372 "A new cluster will be created if its duration exceeds this value. "
373 "0 means no maximum duration.", 0,
374 G_MAXINT64, DEFAULT_MAX_CLUSTER_DURATION,
375 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
376 g_object_class_install_property (gobject_class, PROP_OFFSET_TO_ZERO,
377 g_param_spec_boolean ("offset-to-zero", "Offset To Zero",
378 "Offsets all streams so that the " "earliest stream starts at 0.",
379 DEFAULT_OFFSET_TO_ZERO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380 g_object_class_install_property (gobject_class, PROP_CREATION_TIME,
381 g_param_spec_boxed ("creation-time", "Creation Time",
382 "Date and time of creation. This will be used for the DateUTC field."
383 " NULL means that the current time will be used.",
384 G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
385
386 /**
387 * GstMatroskaMux:cluster-timestamp-offset:
388 *
389 * An offset to add to all clusters/blocks (in nanoseconds)
390 *
391 * Since: 1.20
392 */
393 g_object_class_install_property (gobject_class, PROP_CLUSTER_TIMESTAMP_OFFSET,
394 g_param_spec_uint64 ("cluster-timestamp-offset",
395 "Cluster timestamp offset",
396 "An offset to add to all clusters/blocks (in nanoseconds)", 0,
397 G_MAXUINT64, DEFAULT_CLUSTER_TIMESTAMP_OFFSET,
398 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
399
400 gstelement_class->change_state =
401 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
402 gstelement_class->request_new_pad =
403 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
404 gstelement_class->release_pad =
405 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
406
407 parent_class = g_type_class_peek_parent (klass);
408 }
409
410 /*
411 * Start of pad option handler code
412 */
413 #define DEFAULT_PAD_FRAME_DURATION TRUE
414
415 enum
416 {
417 PROP_PAD_0,
418 PROP_PAD_FRAME_DURATION
419 };
420
421 typedef struct
422 {
423 GstPad parent;
424 gboolean frame_duration;
425 gboolean frame_duration_user;
426 } GstMatroskamuxPad;
427
428 typedef GstPadClass GstMatroskamuxPadClass;
429
430 GType gst_matroskamux_pad_get_type (void);
431 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
432
433 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
434 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
435 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
436 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
437
438 static void
gst_matroskamux_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)439 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
440 GValue * value, GParamSpec * pspec)
441 {
442 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
443
444 switch (prop_id) {
445 case PROP_PAD_FRAME_DURATION:
446 g_value_set_boolean (value, pad->frame_duration);
447 break;
448 default:
449 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
450 break;
451 }
452 }
453
454 static void
gst_matroskamux_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)455 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
456 const GValue * value, GParamSpec * pspec)
457 {
458 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
459
460 switch (prop_id) {
461 case PROP_PAD_FRAME_DURATION:
462 pad->frame_duration = g_value_get_boolean (value);
463 pad->frame_duration_user = TRUE;
464 break;
465 default:
466 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
467 break;
468 }
469 }
470
471 static void
gst_matroskamux_pad_class_init(GstMatroskamuxPadClass * klass)472 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
473 {
474 GObjectClass *gobject_class = (GObjectClass *) klass;
475
476 gobject_class->set_property = gst_matroskamux_pad_set_property;
477 gobject_class->get_property = gst_matroskamux_pad_get_property;
478
479 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
480 g_param_spec_boolean ("frame-duration", "Frame duration",
481 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
482 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
483 }
484
485 static void
gst_matroskamux_pad_init(GstMatroskamuxPad * pad)486 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
487 {
488 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
489 pad->frame_duration_user = FALSE;
490 }
491
492 /*
493 * End of pad option handler code
494 **/
495
496 static void
gst_matroska_mux_init(GstMatroskaMux * mux,gpointer g_class)497 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
498 {
499 GstPadTemplate *templ;
500
501 templ =
502 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
503 mux->srcpad = gst_pad_new_from_template (templ, "src");
504
505 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
506 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
507 gst_pad_use_fixed_caps (mux->srcpad);
508
509 mux->collect = gst_collect_pads_new ();
510 gst_collect_pads_set_clip_function (mux->collect,
511 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
512 gst_collect_pads_set_buffer_function (mux->collect,
513 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
514 gst_collect_pads_set_event_function (mux->collect,
515 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
516
517 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
518 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
519
520 /* property defaults */
521 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
522 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
523 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
524 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
525 mux->time_scale = DEFAULT_TIMECODESCALE;
526 mux->min_cluster_duration = DEFAULT_MIN_CLUSTER_DURATION;
527 mux->max_cluster_duration = DEFAULT_MAX_CLUSTER_DURATION;
528 mux->cluster_timestamp_offset = DEFAULT_CLUSTER_TIMESTAMP_OFFSET;
529
530 /* initialize internal variables */
531 mux->index = NULL;
532 mux->num_streams = 0;
533 mux->num_a_streams = 0;
534 mux->num_t_streams = 0;
535 mux->num_v_streams = 0;
536 mux->internal_toc = NULL;
537
538 /* initialize remaining variables */
539 gst_matroska_mux_reset (GST_ELEMENT (mux));
540 }
541
542
543 /**
544 * gst_matroska_mux_finalize:
545 * @object: #GstMatroskaMux that should be finalized.
546 *
547 * Finalize matroska muxer.
548 */
549 static void
gst_matroska_mux_finalize(GObject * object)550 gst_matroska_mux_finalize (GObject * object)
551 {
552 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
553
554 gst_event_replace (&mux->force_key_unit_event, NULL);
555
556 gst_object_unref (mux->collect);
557 gst_object_unref (mux->ebml_write);
558 g_free (mux->writing_app);
559 g_clear_pointer (&mux->creation_time, g_date_time_unref);
560
561 if (mux->internal_toc) {
562 gst_toc_unref (mux->internal_toc);
563 mux->internal_toc = NULL;
564 }
565
566 G_OBJECT_CLASS (parent_class)->finalize (object);
567 }
568
569
570 /**
571 * gst_matroska_mux_create_uid:
572 * @mux: #GstMatroskaMux to generate UID for.
573 *
574 * Generate new track UID.
575 *
576 * Returns: New track UID.
577 */
578 static guint64
gst_matroska_mux_create_uid(GstMatroskaMux * mux)579 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
580 {
581 return (((guint64) g_random_int ()) << 32) | g_random_int ();
582 }
583
584
585 /**
586 * gst_matroska_pad_reset:
587 * @collect_pad: the #GstMatroskaPad
588 *
589 * Reset and/or release resources of a matroska collect pad.
590 */
591 static void
gst_matroska_pad_reset(GstMatroskaPad * collect_pad,gboolean full)592 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
593 {
594 gchar *name = NULL;
595 GstMatroskaTrackType type = 0;
596
597 /* free track information */
598 if (collect_pad->track != NULL) {
599 /* retrieve for optional later use */
600 name = collect_pad->track->name;
601 type = collect_pad->track->type;
602 /* extra for video */
603 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
604 GstMatroskaTrackVideoContext *ctx =
605 (GstMatroskaTrackVideoContext *) collect_pad->track;
606
607 if (ctx->dirac_unit) {
608 gst_buffer_unref (ctx->dirac_unit);
609 ctx->dirac_unit = NULL;
610 }
611 }
612 g_free (collect_pad->track->codec_id);
613 g_free (collect_pad->track->codec_name);
614 if (full)
615 g_free (collect_pad->track->name);
616 g_free (collect_pad->track->language);
617 g_free (collect_pad->track->codec_priv);
618 g_free (collect_pad->track);
619 collect_pad->track = NULL;
620 if (collect_pad->tags) {
621 gst_tag_list_unref (collect_pad->tags);
622 collect_pad->tags = NULL;
623 }
624 }
625
626 if (!full && type != 0) {
627 GstMatroskaTrackContext *context;
628
629 /* create a fresh context */
630 switch (type) {
631 case GST_MATROSKA_TRACK_TYPE_VIDEO:
632 context = (GstMatroskaTrackContext *)
633 g_new0 (GstMatroskaTrackVideoContext, 1);
634 break;
635 case GST_MATROSKA_TRACK_TYPE_AUDIO:
636 context = (GstMatroskaTrackContext *)
637 g_new0 (GstMatroskaTrackAudioContext, 1);
638 break;
639 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
640 context = (GstMatroskaTrackContext *)
641 g_new0 (GstMatroskaTrackSubtitleContext, 1);
642 break;
643 default:
644 g_assert_not_reached ();
645 return;
646 }
647
648 context->type = type;
649 context->name = name;
650 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
651 /* TODO: check default values for the context */
652 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
653 collect_pad->track = context;
654 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
655 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
656 collect_pad->tags = gst_tag_list_new_empty ();
657 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
658 }
659 }
660
661 /**
662 * gst_matroska_pad_free:
663 * @collect_pad: the #GstMatroskaPad
664 *
665 * Release resources of a matroska collect pad.
666 */
667 static void
gst_matroska_pad_free(GstPad * collect_pad)668 gst_matroska_pad_free (GstPad * collect_pad)
669 {
670 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
671 }
672
673
674 /**
675 * gst_matroska_mux_reset:
676 * @element: #GstMatroskaMux that should be reset.
677 *
678 * Reset matroska muxer back to initial state.
679 */
680 static void
gst_matroska_mux_reset(GstElement * element)681 gst_matroska_mux_reset (GstElement * element)
682 {
683 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
684 GSList *walk;
685
686 /* reset EBML write */
687 gst_ebml_write_reset (mux->ebml_write);
688
689 /* reset input */
690 mux->state = GST_MATROSKA_MUX_STATE_START;
691
692 /* clean up existing streams */
693
694 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
695 GstMatroskaPad *collect_pad;
696
697 collect_pad = (GstMatroskaPad *) walk->data;
698
699 /* reset collect pad to pristine state */
700 gst_matroska_pad_reset (collect_pad, FALSE);
701 }
702
703 /* reset indexes */
704 mux->num_indexes = 0;
705 g_free (mux->index);
706 mux->index = NULL;
707
708 /* reset timers */
709 mux->duration = 0;
710
711 /* reset cluster */
712 mux->cluster = 0;
713 mux->cluster_time = 0;
714 mux->cluster_pos = 0;
715 mux->prev_cluster_size = 0;
716
717 /* reset tags */
718 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
719
720 mux->tags_pos = 0;
721
722 /* reset chapters */
723 gst_toc_setter_reset (GST_TOC_SETTER (mux));
724 if (mux->internal_toc) {
725 gst_toc_unref (mux->internal_toc);
726 mux->internal_toc = NULL;
727 }
728
729 mux->chapters_pos = 0;
730 }
731
732 /**
733 * gst_matroska_mux_handle_src_event:
734 * @pad: Pad which received the event.
735 * @event: Received event.
736 *
737 * handle events - copied from oggmux without understanding
738 *
739 * Returns: %TRUE on success.
740 */
741 static gboolean
gst_matroska_mux_handle_src_event(GstPad * pad,GstObject * parent,GstEvent * event)742 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
743 GstEvent * event)
744 {
745 GstEventType type;
746
747 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
748
749 switch (type) {
750 case GST_EVENT_SEEK:
751 /* disable seeking for now */
752 return FALSE;
753 default:
754 break;
755 }
756
757 return gst_pad_event_default (pad, parent, event);
758 }
759
760
761 static void
gst_matroska_mux_free_codec_priv(GstMatroskaTrackContext * context)762 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
763 {
764 if (context->codec_priv != NULL) {
765 g_free (context->codec_priv);
766 context->codec_priv = NULL;
767 context->codec_priv_size = 0;
768 }
769 }
770
771 static void
gst_matroska_mux_build_vobsub_private(GstMatroskaTrackContext * context,const guint * clut)772 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
773 const guint * clut)
774 {
775 gchar *clutv[17];
776 gchar *sclut;
777 gint i;
778 guint32 col;
779 gdouble y, u, v;
780 guint8 r, g, b;
781
782 /* produce comma-separated list in hex format */
783 for (i = 0; i < 16; ++i) {
784 col = clut[i];
785 /* replicate vobsub's slightly off RGB conversion calculation */
786 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
787 u = ((col >> 8) & 0xff) - 128;
788 v = (col & 0xff) - 128;
789 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
790 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
791 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
792 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
793 }
794 clutv[i] = NULL;
795 sclut = g_strjoinv (",", clutv);
796
797 /* build codec private; only palette for now */
798 gst_matroska_mux_free_codec_priv (context);
799 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
800 /* include terminating 0 */
801 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
802 g_free (sclut);
803 for (i = 0; i < 16; ++i) {
804 g_free (clutv[i]);
805 }
806 }
807
808
809 /**
810 * gst_matroska_mux_handle_sink_event:
811 * @pad: Pad which received the event.
812 * @event: Received event.
813 *
814 * handle events - informational ones like tags
815 *
816 * Returns: %TRUE on success.
817 */
818 static gboolean
gst_matroska_mux_handle_sink_event(GstCollectPads * pads,GstCollectData * data,GstEvent * event,gpointer user_data)819 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
820 GstCollectData * data, GstEvent * event, gpointer user_data)
821 {
822 GstMatroskaPad *collect_pad;
823 GstMatroskaTrackContext *context;
824 GstMatroskaMux *mux;
825 GstPad *pad;
826 GstTagList *list;
827 gboolean ret = TRUE;
828
829 mux = GST_MATROSKA_MUX (user_data);
830 collect_pad = (GstMatroskaPad *) data;
831 pad = data->pad;
832 context = collect_pad->track;
833 g_assert (context);
834
835 switch (GST_EVENT_TYPE (event)) {
836 case GST_EVENT_CAPS:{
837 GstCaps *caps;
838
839 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
840 gst_event_parse_caps (event, &caps);
841
842 ret = collect_pad->capsfunc (pad, caps);
843 gst_event_unref (event);
844 event = NULL;
845 break;
846 }
847 case GST_EVENT_TAG:{
848 gchar *lang = NULL;
849
850 GST_DEBUG_OBJECT (mux, "received tag event");
851 gst_event_parse_tag (event, &list);
852
853 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
854 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
855 const gchar *lang_code;
856
857 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
858 if (lang_code) {
859 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
860 g_free (context->language);
861 context->language = g_strdup (lang_code);
862 } else {
863 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
864 }
865 g_free (lang);
866 }
867
868 /* FIXME: what about stream-specific tags? */
869 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
870 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
871 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
872 } else {
873 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
874 }
875
876 gst_event_unref (event);
877 /* handled this, don't want collectpads to forward it downstream */
878 event = NULL;
879 ret = TRUE;
880 break;
881 }
882 case GST_EVENT_TOC:{
883 GstToc *toc, *old_toc;
884
885 if (mux->chapters_pos > 0)
886 break;
887
888 GST_DEBUG_OBJECT (mux, "received toc event");
889 gst_event_parse_toc (event, &toc, NULL);
890
891 if (toc != NULL) {
892 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
893 if (old_toc != NULL) {
894 if (old_toc != toc)
895 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
896 gst_toc_unref (old_toc);
897 }
898
899 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
900 gst_toc_unref (toc);
901 }
902
903 gst_event_unref (event);
904 /* handled this, don't want collectpads to forward it downstream */
905 event = NULL;
906 break;
907 }
908 case GST_EVENT_CUSTOM_DOWNSTREAM:
909 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
910 const GstStructure *structure;
911
912 structure = gst_event_get_structure (event);
913 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
914 gst_event_replace (&mux->force_key_unit_event, NULL);
915 mux->force_key_unit_event = event;
916 event = NULL;
917 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
918 !strcmp ("dvd-spu-clut-change",
919 gst_structure_get_string (structure, "event"))) {
920 gchar name[16];
921 gint i, value;
922 guint clut[16];
923
924 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
925 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
926 GST_DEBUG_OBJECT (pad, "... discarding");
927 break;
928 }
929 /* first transform event data into table form */
930 for (i = 0; i < 16; i++) {
931 g_snprintf (name, sizeof (name), "clut%02d", i);
932 if (!gst_structure_get_int (structure, name, &value)) {
933 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
934 "contain %s field", name);
935 goto break_hard;
936 }
937 clut[i] = value;
938 }
939
940 /* transform into private data for stream; text form */
941 gst_matroska_mux_build_vobsub_private (context, clut);
942 }
943 }
944 /* fall through */
945 default:
946 break;
947 }
948
949 break_hard:
950 if (event != NULL)
951 return gst_collect_pads_event_default (pads, data, event, FALSE);
952
953 return ret;
954 }
955
956 static void
gst_matroska_mux_set_codec_id(GstMatroskaTrackContext * context,const char * id)957 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
958 const char *id)
959 {
960 g_assert (context && id);
961 g_free (context->codec_id);
962 context->codec_id = g_strdup (id);
963 }
964
965 static gboolean
check_field(GQuark field_id,const GValue * value,gpointer user_data)966 check_field (GQuark field_id, const GValue * value, gpointer user_data)
967 {
968 GstStructure *structure = (GstStructure *) user_data;
969 const gchar *name = gst_structure_get_name (structure);
970
971 if ((g_strcmp0 (name, "video/x-h264") == 0 &&
972 !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
973 "avc3")) || (g_strcmp0 (name, "video/x-h265") == 0
974 && !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
975 "hev1"))
976 ) {
977 /* While in theory, matroska only supports avc1 / hvc1, and doesn't support codec_data
978 * changes, in practice most decoders will use in-band SPS / PPS (avc3 / hev1), if the
979 * input stream is avc3 / hev1 we let the new codec_data slide to support "smart" encoding.
980 *
981 * We don't warn here as we already warned elsewhere.
982 */
983 if (field_id == g_quark_from_static_string ("codec_data")) {
984 return FALSE;
985 } else if (field_id == g_quark_from_static_string ("tier")) {
986 return FALSE;
987 } else if (field_id == g_quark_from_static_string ("profile")) {
988 return FALSE;
989 } else if (field_id == g_quark_from_static_string ("level")) {
990 return FALSE;
991 }
992 } else if (gst_structure_has_name (structure, "video/x-vp8")
993 || gst_structure_has_name (structure, "video/x-vp9")) {
994 /* We do not use profile and streamheader for VPX so let it change
995 * mid stream */
996 if (field_id == g_quark_from_static_string ("streamheader"))
997 return FALSE;
998 else if (field_id == g_quark_from_static_string ("profile"))
999 return FALSE;
1000 }
1001
1002 /* This fields aren't used and are not retained into the bitstream so we can
1003 * discard them. */
1004 if (g_str_has_prefix (gst_structure_get_name (structure), "video/")) {
1005 if (field_id == g_quark_from_static_string ("chroma-site"))
1006 return FALSE;
1007 else if (field_id == g_quark_from_static_string ("chroma-format"))
1008 return FALSE;
1009 else if (field_id == g_quark_from_static_string ("bit-depth-luma"))
1010 return FALSE;
1011
1012 /* Remove pixel-aspect-ratio field if it contains 1/1 as that's considered
1013 * equivalent to not having the field but are not considered equivalent
1014 * by the generic caps functions
1015 */
1016 if (field_id == g_quark_from_static_string ("pixel-aspect-ratio")) {
1017 gint par_n = gst_value_get_fraction_numerator (value);
1018 gint par_d = gst_value_get_fraction_denominator (value);
1019
1020 if (par_n == 1 && par_d == 1)
1021 return FALSE;
1022 }
1023
1024 /* Remove multiview-mode=mono and multiview-flags=0 fields as those are
1025 * equivalent with not having the fields but are not considered equivalent
1026 * by the generic caps functions.
1027 */
1028 if (field_id == g_quark_from_static_string ("multiview-mode")) {
1029 const gchar *s = g_value_get_string (value);
1030
1031 if (g_strcmp0 (s, "mono") == 0)
1032 return FALSE;
1033 }
1034
1035 if (field_id == g_quark_from_static_string ("multiview-flags")) {
1036 guint multiview_flags = gst_value_get_flagset_flags (value);
1037
1038 if (multiview_flags == 0)
1039 return FALSE;
1040 }
1041 }
1042
1043 return TRUE;
1044 }
1045
1046 static gboolean
check_new_caps(GstMatroskaTrackVideoContext * videocontext,GstCaps * old_caps,GstCaps * new_caps)1047 check_new_caps (GstMatroskaTrackVideoContext * videocontext, GstCaps * old_caps,
1048 GstCaps * new_caps)
1049 {
1050 GstStructure *old_s, *new_s;
1051 gboolean ret;
1052
1053 old_caps = gst_caps_copy (old_caps);
1054 new_caps = gst_caps_copy (new_caps);
1055
1056 new_s = gst_caps_get_structure (new_caps, 0);
1057 old_s = gst_caps_get_structure (old_caps, 0);
1058
1059 gst_structure_filter_and_map_in_place (new_s,
1060 (GstStructureFilterMapFunc) check_field, new_s);
1061 gst_structure_filter_and_map_in_place (old_s,
1062 (GstStructureFilterMapFunc) check_field, old_s);
1063
1064 ret = gst_caps_is_subset (new_caps, old_caps);
1065
1066 gst_caps_unref (new_caps);
1067 gst_caps_unref (old_caps);
1068
1069 return ret;
1070 }
1071
1072 /**
1073 * gst_matroska_mux_video_pad_setcaps:
1074 * @pad: Pad which got the caps.
1075 * @caps: New caps.
1076 *
1077 * Setcaps function for video sink pad.
1078 *
1079 * Returns: %TRUE on success.
1080 */
1081 static gboolean
gst_matroska_mux_video_pad_setcaps(GstPad * pad,GstCaps * caps)1082 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
1083 {
1084 GstMatroskaTrackContext *context = NULL;
1085 GstMatroskaTrackVideoContext *videocontext;
1086 GstMatroskaMux *mux;
1087 GstMatroskaPad *collect_pad;
1088 GstStructure *structure;
1089 const gchar *mimetype;
1090 const gchar *interlace_mode, *s;
1091 const GValue *value = NULL;
1092 GstBuffer *codec_buf = NULL;
1093 gint width, height, pixel_width, pixel_height;
1094 gint fps_d, fps_n;
1095 guint multiview_flags;
1096 GstCaps *old_caps;
1097
1098 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1099
1100 /* find context */
1101 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1102 g_assert (collect_pad);
1103 context = collect_pad->track;
1104 g_assert (context);
1105 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
1106 videocontext = (GstMatroskaTrackVideoContext *) context;
1107
1108 if ((old_caps = gst_pad_get_current_caps (pad))) {
1109 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1110 && !check_new_caps (videocontext, old_caps, caps)) {
1111 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1112 ("Caps changes are not supported by Matroska\nCurrent: `%"
1113 GST_PTR_FORMAT "`\nNew: `%" GST_PTR_FORMAT "`", old_caps, caps));
1114 gst_caps_unref (old_caps);
1115 goto refuse_caps;
1116 }
1117 gst_caps_unref (old_caps);
1118 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
1119 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1120 ("Caps on pad %" GST_PTR_FORMAT
1121 " arrived late. Headers were already written", pad));
1122 goto refuse_caps;
1123 }
1124
1125 /* gst -> matroska ID'ing */
1126 structure = gst_caps_get_structure (caps, 0);
1127
1128 mimetype = gst_structure_get_name (structure);
1129
1130 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
1131 if (interlace_mode != NULL) {
1132 if (strcmp (interlace_mode, "progressive") == 0)
1133 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE;
1134 else
1135 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_INTERLACED;
1136 } else {
1137 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_UNKNOWN;
1138 }
1139
1140 if (!strcmp (mimetype, "video/x-theora")) {
1141 /* we'll extract the details later from the theora identification header */
1142 goto skip_details;
1143 }
1144
1145 /* get general properties */
1146 /* spec says it is mandatory */
1147 if (!gst_structure_get_int (structure, "width", &width) ||
1148 !gst_structure_get_int (structure, "height", &height))
1149 goto refuse_caps;
1150
1151 videocontext->pixel_width = width;
1152 videocontext->pixel_height = height;
1153
1154 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
1155 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
1156 && fps_n > 0) {
1157 context->default_duration =
1158 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
1159 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
1160 GST_TIME_ARGS (context->default_duration));
1161 } else {
1162 context->default_duration = 0;
1163 }
1164 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
1165 &pixel_width, &pixel_height)) {
1166 if (pixel_width > pixel_height) {
1167 videocontext->display_width = width * pixel_width / pixel_height;
1168 videocontext->display_height = height;
1169 } else if (pixel_width < pixel_height) {
1170 videocontext->display_width = width;
1171 videocontext->display_height = height * pixel_height / pixel_width;
1172 } else {
1173 videocontext->display_width = 0;
1174 videocontext->display_height = 0;
1175 }
1176 } else {
1177 videocontext->display_width = 0;
1178 videocontext->display_height = 0;
1179 }
1180
1181 if ((s = gst_structure_get_string (structure, "colorimetry"))) {
1182 if (!gst_video_colorimetry_from_string (&videocontext->colorimetry, s)) {
1183 GST_WARNING_OBJECT (pad, "Could not parse colorimetry %s", s);
1184 }
1185 }
1186
1187 if ((s = gst_structure_get_string (structure, "mastering-display-info"))) {
1188 if (!gst_video_mastering_display_info_from_string
1189 (&videocontext->mastering_display_info, s)) {
1190 GST_WARNING_OBJECT (pad, "Could not parse mastering-display-metadata %s",
1191 s);
1192 } else {
1193 videocontext->mastering_display_info_present = TRUE;
1194 }
1195 }
1196
1197 if ((s = gst_structure_get_string (structure, "content-light-level"))) {
1198 if (!gst_video_content_light_level_from_string
1199 (&videocontext->content_light_level, s))
1200 GST_WARNING_OBJECT (pad, "Could not parse content-light-level %s", s);
1201 }
1202
1203 /* Collect stereoscopic info, if any */
1204 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1205 videocontext->multiview_mode =
1206 gst_video_multiview_mode_from_caps_string (s);
1207 gst_structure_get_flagset (structure, "multiview-flags", &multiview_flags,
1208 NULL);
1209 videocontext->multiview_flags = multiview_flags;
1210
1211
1212 skip_details:
1213
1214 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1215 videocontext->fourcc = 0;
1216
1217 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1218 * data and other settings
1219 * - add new formats
1220 */
1221
1222 /* extract codec_data, may turn out needed */
1223 value = gst_structure_get_value (structure, "codec_data");
1224 if (value)
1225 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1226
1227 /* find type */
1228 if (!strcmp (mimetype, "video/x-raw")) {
1229 const gchar *fstr;
1230 gst_matroska_mux_set_codec_id (context,
1231 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1232 fstr = gst_structure_get_string (structure, "format");
1233 if (fstr) {
1234 if (strlen (fstr) == 4)
1235 videocontext->fourcc = GST_STR_FOURCC (fstr);
1236 else if (!strcmp (fstr, "GRAY8"))
1237 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1238 else if (!strcmp (fstr, "BGR"))
1239 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1240 else if (!strcmp (fstr, "RGB"))
1241 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1242 }
1243 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1244 ||!strcmp (mimetype, "video/x-divx")
1245 || !strcmp (mimetype, "video/x-dv")
1246 || !strcmp (mimetype, "video/x-h263")
1247 || !strcmp (mimetype, "video/x-msmpeg")
1248 || !strcmp (mimetype, "video/x-wmv")
1249 || !strcmp (mimetype, "image/jpeg")) {
1250 gst_riff_strf_vids *bih;
1251 gint size = sizeof (gst_riff_strf_vids);
1252 guint32 fourcc = 0;
1253
1254 if (!strcmp (mimetype, "video/x-huffyuv"))
1255 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1256 else if (!strcmp (mimetype, "video/x-dv"))
1257 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1258 else if (!strcmp (mimetype, "video/x-h263"))
1259 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1260 else if (!strcmp (mimetype, "video/x-divx")) {
1261 gint divxversion;
1262
1263 gst_structure_get_int (structure, "divxversion", &divxversion);
1264 switch (divxversion) {
1265 case 3:
1266 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1267 break;
1268 case 4:
1269 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1270 break;
1271 case 5:
1272 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1273 break;
1274 }
1275 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1276 gint msmpegversion;
1277
1278 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1279 switch (msmpegversion) {
1280 case 41:
1281 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1282 break;
1283 case 42:
1284 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1285 break;
1286 case 43:
1287 goto msmpeg43;
1288 break;
1289 }
1290 } else if (!strcmp (mimetype, "video/x-wmv")) {
1291 gint wmvversion;
1292 const gchar *fstr;
1293
1294 fstr = gst_structure_get_string (structure, "format");
1295 if (fstr && strlen (fstr) == 4) {
1296 fourcc = GST_STR_FOURCC (fstr);
1297 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1298 if (wmvversion == 2) {
1299 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1300 } else if (wmvversion == 1) {
1301 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1302 } else if (wmvversion == 3) {
1303 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1304 }
1305 }
1306 } else if (!strcmp (mimetype, "image/jpeg")) {
1307 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1308 }
1309
1310 if (!fourcc)
1311 goto refuse_caps;
1312
1313 bih = g_new0 (gst_riff_strf_vids, 1);
1314 GST_WRITE_UINT32_LE (&bih->size, size);
1315 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1316 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1317 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1318 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1319 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1320 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1321 videocontext->pixel_height * 3);
1322
1323 /* process codec private/initialization data, if any */
1324 if (codec_buf) {
1325 size += gst_buffer_get_size (codec_buf);
1326 bih = g_realloc (bih, size);
1327 GST_WRITE_UINT32_LE (&bih->size, size);
1328 gst_buffer_extract (codec_buf, 0,
1329 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1330 }
1331
1332 gst_matroska_mux_set_codec_id (context,
1333 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1334 gst_matroska_mux_free_codec_priv (context);
1335 context->codec_priv = (gpointer) bih;
1336 context->codec_priv_size = size;
1337 context->dts_only = TRUE;
1338 } else if (!strcmp (mimetype, "video/x-h264")) {
1339 gst_matroska_mux_set_codec_id (context,
1340 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1341 gst_matroska_mux_free_codec_priv (context);
1342
1343 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1344 "avc3")) {
1345 GST_WARNING_OBJECT (mux,
1346 "avc3 is not officially supported, only use this format for smart encoding");
1347 }
1348
1349 /* Create avcC header */
1350 if (codec_buf != NULL) {
1351 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1352 context->codec_priv = g_malloc0 (context->codec_priv_size);
1353 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1354 }
1355 } else if (!strcmp (mimetype, "video/x-h265")) {
1356 gst_matroska_mux_set_codec_id (context,
1357 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1358 gst_matroska_mux_free_codec_priv (context);
1359
1360 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1361 "hev1")) {
1362 GST_WARNING_OBJECT (mux,
1363 "hev1 is not officially supported, only use this format for smart encoding");
1364 }
1365
1366 /* Create hvcC header */
1367 if (codec_buf != NULL) {
1368 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1369 context->codec_priv = g_malloc0 (context->codec_priv_size);
1370 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1371 }
1372 } else if (!strcmp (mimetype, "video/x-theora")) {
1373 const GValue *streamheader;
1374
1375 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1376
1377 gst_matroska_mux_free_codec_priv (context);
1378
1379 streamheader = gst_structure_get_value (structure, "streamheader");
1380 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1381 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1382 ("theora stream headers missing or malformed"));
1383 goto refuse_caps;
1384 }
1385 } else if (!strcmp (mimetype, "video/x-dirac")) {
1386 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1387 } else if (!strcmp (mimetype, "video/x-vp8")) {
1388 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1389 } else if (!strcmp (mimetype, "video/x-vp9")) {
1390 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1391 } else if (!strcmp (mimetype, "video/x-av1")) {
1392 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_AV1);
1393 gst_matroska_mux_free_codec_priv (context);
1394 /* Create av1C header */
1395 if (codec_buf != NULL)
1396 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1397 &context->codec_priv, &context->codec_priv_size);
1398 } else if (!strcmp (mimetype, "video/x-ffv")) {
1399 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_FFV1);
1400 gst_matroska_mux_free_codec_priv (context);
1401 if (codec_buf != NULL)
1402 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1403 &context->codec_priv, &context->codec_priv_size);
1404 } else if (!strcmp (mimetype, "video/mpeg")) {
1405 gint mpegversion;
1406
1407 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1408 switch (mpegversion) {
1409 case 1:
1410 gst_matroska_mux_set_codec_id (context,
1411 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1412 break;
1413 case 2:
1414 gst_matroska_mux_set_codec_id (context,
1415 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1416 break;
1417 case 4:
1418 gst_matroska_mux_set_codec_id (context,
1419 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1420 break;
1421 default:
1422 goto refuse_caps;
1423 }
1424
1425 /* global headers may be in codec data */
1426 if (codec_buf != NULL) {
1427 gst_matroska_mux_free_codec_priv (context);
1428 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1429 context->codec_priv = g_malloc0 (context->codec_priv_size);
1430 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1431 }
1432 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1433 msmpeg43:
1434 /* can only make it here if preceding case verified it was version 3 */
1435 gst_matroska_mux_set_codec_id (context,
1436 GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1437 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1438 gint rmversion;
1439 const GValue *mdpr_data;
1440
1441 gst_structure_get_int (structure, "rmversion", &rmversion);
1442 switch (rmversion) {
1443 case 1:
1444 gst_matroska_mux_set_codec_id (context,
1445 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1446 break;
1447 case 2:
1448 gst_matroska_mux_set_codec_id (context,
1449 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1450 break;
1451 case 3:
1452 gst_matroska_mux_set_codec_id (context,
1453 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1454 break;
1455 case 4:
1456 gst_matroska_mux_set_codec_id (context,
1457 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1458 break;
1459 default:
1460 goto refuse_caps;
1461 }
1462
1463 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1464 if (mdpr_data != NULL) {
1465 guint8 *priv_data = NULL;
1466 guint priv_data_size = 0;
1467
1468 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1469
1470 priv_data_size = gst_buffer_get_size (codec_data_buf);
1471 priv_data = g_malloc0 (priv_data_size);
1472
1473 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1474
1475 gst_matroska_mux_free_codec_priv (context);
1476 context->codec_priv = priv_data;
1477 context->codec_priv_size = priv_data_size;
1478 }
1479 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1480 const gchar *variant;
1481
1482 gst_matroska_mux_free_codec_priv (context);
1483
1484 variant = gst_structure_get_string (structure, "format");
1485 if (!variant || !g_strcmp0 (variant, "standard"))
1486 context->codec_priv = g_strdup ("apcn");
1487 else if (!g_strcmp0 (variant, "hq"))
1488 context->codec_priv = g_strdup ("apch");
1489 else if (!g_strcmp0 (variant, "lt"))
1490 context->codec_priv = g_strdup ("apcs");
1491 else if (!g_strcmp0 (variant, "proxy"))
1492 context->codec_priv = g_strdup ("apco");
1493 else if (!g_strcmp0 (variant, "4444"))
1494 context->codec_priv = g_strdup ("ap4h");
1495 else {
1496 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1497
1498 goto refuse_caps;
1499 }
1500
1501 context->codec_priv_size = sizeof (guint32);
1502 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1503 }
1504
1505 return TRUE;
1506
1507 /* ERRORS */
1508 refuse_caps:
1509 {
1510 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1511 GST_PAD_NAME (pad), caps);
1512 return FALSE;
1513 }
1514 }
1515
1516 /* N > 0 to expect a particular number of headers, negative if the
1517 number of headers is variable */
1518 static gboolean
xiphN_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context,GstBuffer ** p_buf0,int N)1519 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1520 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1521 {
1522 GstBuffer **buf = NULL;
1523 GArray *bufarr;
1524 guint8 *priv_data;
1525 guint bufi, i, offset, priv_data_size;
1526
1527 if (streamheader == NULL)
1528 goto no_stream_headers;
1529
1530 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1531 goto wrong_type;
1532
1533 bufarr = g_value_peek_pointer (streamheader);
1534 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1535 goto wrong_count;
1536 if (N > 0 && bufarr->len != N)
1537 goto wrong_count;
1538
1539 context->xiph_headers_to_skip = bufarr->len;
1540
1541 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1542 for (i = 0; i < bufarr->len; i++) {
1543 GValue *bufval = &g_array_index (bufarr, GValue, i);
1544
1545 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1546 g_free (buf);
1547 goto wrong_content_type;
1548 }
1549
1550 buf[i] = g_value_peek_pointer (bufval);
1551 }
1552
1553 priv_data_size = 1;
1554 if (bufarr->len > 0) {
1555 for (i = 0; i < bufarr->len - 1; i++) {
1556 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1557 }
1558 }
1559
1560 for (i = 0; i < bufarr->len; ++i) {
1561 priv_data_size += gst_buffer_get_size (buf[i]);
1562 }
1563
1564 priv_data = g_malloc0 (priv_data_size);
1565
1566 priv_data[0] = bufarr->len - 1;
1567 offset = 1;
1568
1569 if (bufarr->len > 0) {
1570 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1571 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1572 priv_data[offset++] = 0xff;
1573 }
1574 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1575 }
1576 }
1577
1578 for (i = 0; i < bufarr->len; ++i) {
1579 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1580 offset += gst_buffer_get_size (buf[i]);
1581 }
1582
1583 gst_matroska_mux_free_codec_priv (context);
1584 context->codec_priv = priv_data;
1585 context->codec_priv_size = priv_data_size;
1586
1587 if (p_buf0)
1588 *p_buf0 = gst_buffer_ref (buf[0]);
1589
1590 g_free (buf);
1591
1592 return TRUE;
1593
1594 /* ERRORS */
1595 no_stream_headers:
1596 {
1597 GST_WARNING ("required streamheaders missing in sink caps!");
1598 return FALSE;
1599 }
1600 wrong_type:
1601 {
1602 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1603 G_VALUE_TYPE_NAME (streamheader));
1604 return FALSE;
1605 }
1606 wrong_count:
1607 {
1608 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1609 return FALSE;
1610 }
1611 wrong_content_type:
1612 {
1613 GST_WARNING ("streamheaders array does not contain GstBuffers");
1614 return FALSE;
1615 }
1616 }
1617
1618 static gboolean
vorbis_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1619 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1620 GstMatroskaTrackContext * context)
1621 {
1622 GstBuffer *buf0 = NULL;
1623
1624 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1625 return FALSE;
1626
1627 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1628 GST_WARNING ("First vorbis header too small, ignoring");
1629 } else {
1630 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1631 GstMatroskaTrackAudioContext *audiocontext;
1632 GstMapInfo map;
1633 guint8 *hdr;
1634
1635 gst_buffer_map (buf0, &map, GST_MAP_READ);
1636 hdr = map.data + 1 + 6 + 4;
1637 audiocontext = (GstMatroskaTrackAudioContext *) context;
1638 audiocontext->channels = GST_READ_UINT8 (hdr);
1639 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1640 gst_buffer_unmap (buf0, &map);
1641 }
1642 }
1643
1644 if (buf0)
1645 gst_buffer_unref (buf0);
1646
1647 return TRUE;
1648 }
1649
1650 static gboolean
theora_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1651 theora_streamheader_to_codecdata (const GValue * streamheader,
1652 GstMatroskaTrackContext * context)
1653 {
1654 GstBuffer *buf0 = NULL;
1655
1656 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1657 return FALSE;
1658
1659 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1660 GST_WARNING ("First theora header too small, ignoring");
1661 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1662 GST_WARNING ("First header not a theora identification header, ignoring");
1663 } else {
1664 GstMatroskaTrackVideoContext *videocontext;
1665 guint fps_num, fps_denom, par_num, par_denom;
1666 GstMapInfo map;
1667 guint8 *hdr;
1668
1669 gst_buffer_map (buf0, &map, GST_MAP_READ);
1670 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1671
1672 videocontext = (GstMatroskaTrackVideoContext *) context;
1673 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1674 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1675 hdr += 3 + 3 + 1 + 1;
1676 fps_num = GST_READ_UINT32_BE (hdr);
1677 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1678 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1679 fps_denom, fps_num);
1680 hdr += 4 + 4;
1681 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1682 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1683 if (par_num > 0 && par_denom > 0) {
1684 if (par_num > par_denom) {
1685 videocontext->display_width =
1686 videocontext->pixel_width * par_num / par_denom;
1687 videocontext->display_height = videocontext->pixel_height;
1688 } else if (par_num < par_denom) {
1689 videocontext->display_width = videocontext->pixel_width;
1690 videocontext->display_height =
1691 videocontext->pixel_height * par_denom / par_num;
1692 } else {
1693 videocontext->display_width = 0;
1694 videocontext->display_height = 0;
1695 }
1696 } else {
1697 videocontext->display_width = 0;
1698 videocontext->display_height = 0;
1699 }
1700
1701 gst_buffer_unmap (buf0, &map);
1702 }
1703
1704 if (buf0)
1705 gst_buffer_unref (buf0);
1706
1707 return TRUE;
1708 }
1709
1710 static gboolean
kate_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1711 kate_streamheader_to_codecdata (const GValue * streamheader,
1712 GstMatroskaTrackContext * context)
1713 {
1714 GstBuffer *buf0 = NULL;
1715
1716 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1717 return FALSE;
1718
1719 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1720 GST_WARNING ("First kate header too small, ignoring");
1721 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1722 GST_WARNING ("First header not a kate identification header, ignoring");
1723 }
1724
1725 if (buf0)
1726 gst_buffer_unref (buf0);
1727
1728 return TRUE;
1729 }
1730
1731 static gboolean
flac_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1732 flac_streamheader_to_codecdata (const GValue * streamheader,
1733 GstMatroskaTrackContext * context)
1734 {
1735 GArray *bufarr;
1736 gint i;
1737 GValue *bufval;
1738 GstBuffer *buffer;
1739
1740 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1741 GST_WARNING ("No or invalid streamheader field in the caps");
1742 return FALSE;
1743 }
1744
1745 bufarr = g_value_peek_pointer (streamheader);
1746 if (bufarr->len < 2) {
1747 GST_WARNING ("Too few headers in streamheader field");
1748 return FALSE;
1749 }
1750
1751 context->xiph_headers_to_skip = bufarr->len + 1;
1752
1753 bufval = &g_array_index (bufarr, GValue, 0);
1754 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1755 GST_WARNING ("streamheaders array does not contain GstBuffers");
1756 return FALSE;
1757 }
1758
1759 buffer = g_value_peek_pointer (bufval);
1760
1761 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1762 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1763 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1764 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1765 GST_WARNING ("Invalid streamheader for FLAC");
1766 return FALSE;
1767 }
1768
1769 gst_matroska_mux_free_codec_priv (context);
1770 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1771 context->codec_priv = g_malloc (context->codec_priv_size);
1772 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1773
1774 for (i = 1; i < bufarr->len; i++) {
1775 guint old_size;
1776 bufval = &g_array_index (bufarr, GValue, i);
1777
1778 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1779 gst_matroska_mux_free_codec_priv (context);
1780 GST_WARNING ("streamheaders array does not contain GstBuffers");
1781 return FALSE;
1782 }
1783
1784 buffer = g_value_peek_pointer (bufval);
1785
1786 old_size = context->codec_priv_size;
1787 context->codec_priv_size += gst_buffer_get_size (buffer);
1788
1789 context->codec_priv = g_realloc (context->codec_priv,
1790 context->codec_priv_size);
1791 gst_buffer_extract (buffer, 0,
1792 (guint8 *) context->codec_priv + old_size, -1);
1793 }
1794
1795 return TRUE;
1796 }
1797
1798 static gboolean
speex_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1799 speex_streamheader_to_codecdata (const GValue * streamheader,
1800 GstMatroskaTrackContext * context)
1801 {
1802 GArray *bufarr;
1803 GValue *bufval;
1804 GstBuffer *buffer;
1805 guint old_size;
1806
1807 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1808 GST_WARNING ("No or invalid streamheader field in the caps");
1809 return FALSE;
1810 }
1811
1812 bufarr = g_value_peek_pointer (streamheader);
1813 if (bufarr->len != 2) {
1814 GST_WARNING ("Too few headers in streamheader field");
1815 return FALSE;
1816 }
1817
1818 context->xiph_headers_to_skip = bufarr->len + 1;
1819
1820 bufval = &g_array_index (bufarr, GValue, 0);
1821 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1822 GST_WARNING ("streamheaders array does not contain GstBuffers");
1823 return FALSE;
1824 }
1825
1826 buffer = g_value_peek_pointer (bufval);
1827
1828 if (gst_buffer_get_size (buffer) < 80
1829 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1830 GST_WARNING ("Invalid streamheader for Speex");
1831 return FALSE;
1832 }
1833
1834 gst_matroska_mux_free_codec_priv (context);
1835 context->codec_priv_size = gst_buffer_get_size (buffer);
1836 context->codec_priv = g_malloc (context->codec_priv_size);
1837 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1838
1839 bufval = &g_array_index (bufarr, GValue, 1);
1840
1841 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1842 gst_matroska_mux_free_codec_priv (context);
1843 GST_WARNING ("streamheaders array does not contain GstBuffers");
1844 return FALSE;
1845 }
1846
1847 buffer = g_value_peek_pointer (bufval);
1848
1849 old_size = context->codec_priv_size;
1850 context->codec_priv_size += gst_buffer_get_size (buffer);
1851 context->codec_priv = g_realloc (context->codec_priv,
1852 context->codec_priv_size);
1853 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1854
1855 return TRUE;
1856 }
1857
1858 static gboolean
opus_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1859 opus_streamheader_to_codecdata (const GValue * streamheader,
1860 GstMatroskaTrackContext * context)
1861 {
1862 GArray *bufarr;
1863 GValue *bufval;
1864 GstBuffer *buf;
1865
1866 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1867 goto wrong_type;
1868
1869 bufarr = g_value_peek_pointer (streamheader);
1870 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1871 goto wrong_count;
1872
1873 /* Opus headers are not in-band */
1874 context->xiph_headers_to_skip = 0;
1875
1876 bufval = &g_array_index (bufarr, GValue, 0);
1877 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1878 goto wrong_content_type;
1879 }
1880 buf = g_value_peek_pointer (bufval);
1881
1882 gst_matroska_mux_free_codec_priv (context);
1883
1884 context->codec_priv_size = gst_buffer_get_size (buf);
1885 context->codec_priv = g_malloc0 (context->codec_priv_size);
1886 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1887
1888 context->codec_delay =
1889 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1890 context->codec_delay =
1891 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1892 context->seek_preroll = 80 * GST_MSECOND;
1893
1894 return TRUE;
1895
1896 /* ERRORS */
1897 wrong_type:
1898 {
1899 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1900 G_VALUE_TYPE_NAME (streamheader));
1901 return FALSE;
1902 }
1903 wrong_count:
1904 {
1905 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1906 return FALSE;
1907 }
1908 wrong_content_type:
1909 {
1910 GST_WARNING ("streamheaders array does not contain GstBuffers");
1911 return FALSE;
1912 }
1913 }
1914
1915 static gboolean
opus_make_codecdata(GstMatroskaTrackContext * context,GstCaps * caps)1916 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1917 {
1918 guint32 rate;
1919 guint8 channels;
1920 guint8 channel_mapping_family;
1921 guint8 stream_count, coupled_count, channel_mapping[256];
1922 GstBuffer *buffer;
1923 GstMapInfo map;
1924
1925 /* Opus headers are not in-band */
1926 context->xiph_headers_to_skip = 0;
1927
1928 context->codec_delay = 0;
1929 context->seek_preroll = 80 * GST_MSECOND;
1930
1931 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1932 &channel_mapping_family, &stream_count, &coupled_count,
1933 channel_mapping)) {
1934 GST_WARNING ("Failed to parse caps for Opus");
1935 return FALSE;
1936 }
1937
1938 buffer =
1939 gst_codec_utils_opus_create_header (rate, channels,
1940 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1941 0);
1942 if (!buffer) {
1943 GST_WARNING ("Failed to create Opus header from caps");
1944 return FALSE;
1945 }
1946
1947 gst_buffer_map (buffer, &map, GST_MAP_READ);
1948 context->codec_priv_size = map.size;
1949 context->codec_priv = g_malloc (context->codec_priv_size);
1950 memcpy (context->codec_priv, map.data, map.size);
1951 gst_buffer_unmap (buffer, &map);
1952 gst_buffer_unref (buffer);
1953
1954 return TRUE;
1955 }
1956
1957 /**
1958 * gst_matroska_mux_audio_pad_setcaps:
1959 * @pad: Pad which got the caps.
1960 * @caps: New caps.
1961 *
1962 * Setcaps function for audio sink pad.
1963 *
1964 * Returns: %TRUE on success.
1965 */
1966 static gboolean
gst_matroska_mux_audio_pad_setcaps(GstPad * pad,GstCaps * caps)1967 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1968 {
1969 GstMatroskaTrackContext *context = NULL;
1970 GstMatroskaTrackAudioContext *audiocontext;
1971 GstMatroskaMux *mux;
1972 GstMatroskaPad *collect_pad;
1973 const gchar *mimetype;
1974 gint samplerate = 0, channels = 0;
1975 GstStructure *structure;
1976 const GValue *codec_data = NULL;
1977 GstBuffer *buf = NULL;
1978 const gchar *stream_format = NULL;
1979 GstCaps *old_caps;
1980
1981 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1982
1983 if ((old_caps = gst_pad_get_current_caps (pad))) {
1984 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1985 && !gst_caps_is_equal (caps, old_caps)) {
1986 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1987 ("Caps changes are not supported by Matroska"));
1988 gst_caps_unref (old_caps);
1989 goto refuse_caps;
1990 }
1991 gst_caps_unref (old_caps);
1992 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
1993 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1994 ("Caps on pad %" GST_PTR_FORMAT
1995 " arrived late. Headers were already written", pad));
1996 goto refuse_caps;
1997 }
1998
1999 /* find context */
2000 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2001 g_assert (collect_pad);
2002 context = collect_pad->track;
2003 g_assert (context);
2004 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
2005 audiocontext = (GstMatroskaTrackAudioContext *) context;
2006
2007 structure = gst_caps_get_structure (caps, 0);
2008 mimetype = gst_structure_get_name (structure);
2009
2010 /* general setup */
2011 gst_structure_get_int (structure, "rate", &samplerate);
2012 gst_structure_get_int (structure, "channels", &channels);
2013
2014 audiocontext->samplerate = samplerate;
2015 audiocontext->channels = channels;
2016 audiocontext->bitdepth = 0;
2017 context->default_duration = 0;
2018
2019 codec_data = gst_structure_get_value (structure, "codec_data");
2020 if (codec_data)
2021 buf = gst_value_get_buffer (codec_data);
2022
2023 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
2024 * data and other settings
2025 * - add new formats
2026 */
2027
2028 if (!strcmp (mimetype, "audio/mpeg")) {
2029 gint mpegversion = 0;
2030
2031 gst_structure_get_int (structure, "mpegversion", &mpegversion);
2032 switch (mpegversion) {
2033 case 1:{
2034 gint layer;
2035 gint version = 1;
2036 gint spf;
2037
2038 gst_structure_get_int (structure, "layer", &layer);
2039
2040 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
2041 GST_WARNING_OBJECT (mux,
2042 "Unable to determine MPEG audio version, assuming 1");
2043 version = 1;
2044 }
2045
2046 if (layer == 1)
2047 spf = 384;
2048 else if (layer == 2)
2049 spf = 1152;
2050 else if (version == 2)
2051 spf = 576;
2052 else
2053 spf = 1152;
2054
2055 context->default_duration =
2056 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
2057
2058 switch (layer) {
2059 case 1:
2060 gst_matroska_mux_set_codec_id (context,
2061 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
2062 break;
2063 case 2:
2064 gst_matroska_mux_set_codec_id (context,
2065 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
2066 break;
2067 case 3:
2068 gst_matroska_mux_set_codec_id (context,
2069 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
2070 break;
2071 default:
2072 goto refuse_caps;
2073 }
2074 break;
2075 }
2076 case 2:
2077 case 4:
2078 stream_format = gst_structure_get_string (structure, "stream-format");
2079 /* check this is raw aac */
2080 if (stream_format) {
2081 if (strcmp (stream_format, "raw") != 0) {
2082 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
2083 stream_format);
2084 }
2085 } else {
2086 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
2087 "assuming 'raw'");
2088 }
2089
2090 if (buf) {
2091 gst_matroska_mux_set_codec_id (context,
2092 GST_MATROSKA_CODEC_ID_AUDIO_AAC);
2093 context->codec_priv_size = gst_buffer_get_size (buf);
2094 context->codec_priv = g_malloc (context->codec_priv_size);
2095 gst_buffer_extract (buf, 0, context->codec_priv,
2096 context->codec_priv_size);
2097 } else {
2098 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
2099 goto refuse_caps;
2100 }
2101 break;
2102 default:
2103 goto refuse_caps;
2104 }
2105 } else if (!strcmp (mimetype, "audio/x-raw")) {
2106 GstAudioInfo info;
2107
2108 gst_audio_info_init (&info);
2109 if (!gst_audio_info_from_caps (&info, caps)) {
2110 GST_DEBUG_OBJECT (mux,
2111 "broken caps, rejected by gst_audio_info_from_caps");
2112 goto refuse_caps;
2113 }
2114
2115 switch (GST_AUDIO_INFO_FORMAT (&info)) {
2116 case GST_AUDIO_FORMAT_U8:
2117 case GST_AUDIO_FORMAT_S16BE:
2118 case GST_AUDIO_FORMAT_S16LE:
2119 case GST_AUDIO_FORMAT_S24BE:
2120 case GST_AUDIO_FORMAT_S24LE:
2121 case GST_AUDIO_FORMAT_S32BE:
2122 case GST_AUDIO_FORMAT_S32LE:
2123 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
2124 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
2125 goto refuse_caps;
2126 }
2127 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
2128 gst_matroska_mux_set_codec_id (context,
2129 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
2130 else
2131 gst_matroska_mux_set_codec_id (context,
2132 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
2133 break;
2134 case GST_AUDIO_FORMAT_F32LE:
2135 case GST_AUDIO_FORMAT_F64LE:
2136 gst_matroska_mux_set_codec_id (context,
2137 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
2138 break;
2139
2140 default:
2141 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
2142 goto refuse_caps;
2143 }
2144
2145 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
2146 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
2147 const GValue *streamheader;
2148
2149 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
2150
2151 gst_matroska_mux_free_codec_priv (context);
2152
2153 streamheader = gst_structure_get_value (structure, "streamheader");
2154 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
2155 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2156 ("vorbis stream headers missing or malformed"));
2157 goto refuse_caps;
2158 }
2159 } else if (!strcmp (mimetype, "audio/x-flac")) {
2160 const GValue *streamheader;
2161
2162 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
2163
2164 gst_matroska_mux_free_codec_priv (context);
2165
2166 streamheader = gst_structure_get_value (structure, "streamheader");
2167 if (!flac_streamheader_to_codecdata (streamheader, context)) {
2168 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2169 ("flac stream headers missing or malformed"));
2170 goto refuse_caps;
2171 }
2172 } else if (!strcmp (mimetype, "audio/x-speex")) {
2173 const GValue *streamheader;
2174
2175 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
2176 gst_matroska_mux_free_codec_priv (context);
2177
2178 streamheader = gst_structure_get_value (structure, "streamheader");
2179 if (!speex_streamheader_to_codecdata (streamheader, context)) {
2180 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2181 ("speex stream headers missing or malformed"));
2182 goto refuse_caps;
2183 }
2184 } else if (!strcmp (mimetype, "audio/x-opus")) {
2185 const GValue *streamheader;
2186
2187 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
2188
2189 streamheader = gst_structure_get_value (structure, "streamheader");
2190 if (streamheader) {
2191 gst_matroska_mux_free_codec_priv (context);
2192 if (!opus_streamheader_to_codecdata (streamheader, context)) {
2193 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2194 ("opus stream headers missing or malformed"));
2195 goto refuse_caps;
2196 }
2197 } else {
2198 /* no streamheader, but we need to have one, so we make one up
2199 based on caps */
2200 gst_matroska_mux_free_codec_priv (context);
2201 if (!opus_make_codecdata (context, caps)) {
2202 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2203 ("opus stream headers missing or malformed"));
2204 goto refuse_caps;
2205 }
2206 }
2207 } else if (!strcmp (mimetype, "audio/x-ac3")) {
2208 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
2209 } else if (!strcmp (mimetype, "audio/x-eac3")) {
2210 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
2211 } else if (!strcmp (mimetype, "audio/x-dts")) {
2212 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
2213 } else if (!strcmp (mimetype, "audio/x-tta")) {
2214 gint width;
2215
2216 /* TTA frame duration */
2217 context->default_duration = 1.04489795918367346939 * GST_SECOND;
2218
2219 gst_structure_get_int (structure, "width", &width);
2220 audiocontext->bitdepth = width;
2221 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
2222
2223 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
2224 gint raversion;
2225 const GValue *mdpr_data;
2226
2227 gst_structure_get_int (structure, "raversion", &raversion);
2228 switch (raversion) {
2229 case 1:
2230 gst_matroska_mux_set_codec_id (context,
2231 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
2232 break;
2233 case 2:
2234 gst_matroska_mux_set_codec_id (context,
2235 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2236 break;
2237 case 8:
2238 gst_matroska_mux_set_codec_id (context,
2239 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2240 break;
2241 default:
2242 goto refuse_caps;
2243 }
2244
2245 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2246 if (mdpr_data != NULL) {
2247 guint8 *priv_data = NULL;
2248 guint priv_data_size = 0;
2249
2250 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2251
2252 priv_data_size = gst_buffer_get_size (codec_data_buf);
2253 priv_data = g_malloc0 (priv_data_size);
2254
2255 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2256
2257 gst_matroska_mux_free_codec_priv (context);
2258
2259 context->codec_priv = priv_data;
2260 context->codec_priv_size = priv_data_size;
2261 }
2262
2263 } else if (!strcmp (mimetype, "audio/x-wma")
2264 || !strcmp (mimetype, "audio/x-alaw")
2265 || !strcmp (mimetype, "audio/x-mulaw")
2266 || !strcmp (mimetype, "audio/x-adpcm")
2267 || !strcmp (mimetype, "audio/G722")) {
2268 guint8 *codec_priv;
2269 guint codec_priv_size;
2270 guint16 format = 0;
2271 gint block_align = 0;
2272 gint bitrate = 0;
2273
2274 if (samplerate == 0 || channels == 0) {
2275 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2276 goto refuse_caps;
2277 }
2278
2279 if (!strcmp (mimetype, "audio/x-wma")) {
2280 gint wmaversion;
2281 gint depth;
2282
2283 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2284 || !gst_structure_get_int (structure, "block_align", &block_align)
2285 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2286 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2287 " on WMA caps");
2288 goto refuse_caps;
2289 }
2290
2291 switch (wmaversion) {
2292 case 1:
2293 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2294 break;
2295 case 2:
2296 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2297 break;
2298 case 3:
2299 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2300 break;
2301 default:
2302 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2303 goto refuse_caps;
2304 }
2305
2306 if (gst_structure_get_int (structure, "depth", &depth))
2307 audiocontext->bitdepth = depth;
2308 } else if (!strcmp (mimetype, "audio/x-alaw")
2309 || !strcmp (mimetype, "audio/x-mulaw")) {
2310 audiocontext->bitdepth = 8;
2311 if (!strcmp (mimetype, "audio/x-alaw"))
2312 format = GST_RIFF_WAVE_FORMAT_ALAW;
2313 else
2314 format = GST_RIFF_WAVE_FORMAT_MULAW;
2315
2316 block_align = channels;
2317 bitrate = block_align * samplerate;
2318 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2319 const char *layout;
2320
2321 layout = gst_structure_get_string (structure, "layout");
2322 if (!layout) {
2323 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2324 goto refuse_caps;
2325 }
2326
2327 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2328 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2329 goto refuse_caps;
2330 }
2331
2332 if (!strcmp (layout, "dvi")) {
2333 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2334 } else if (!strcmp (layout, "g726")) {
2335 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2336 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2337 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2338 goto refuse_caps;
2339 }
2340 } else {
2341 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2342 goto refuse_caps;
2343 }
2344
2345 } else if (!strcmp (mimetype, "audio/G722")) {
2346 format = GST_RIFF_WAVE_FORMAT_ADPCM_G722;
2347 }
2348 g_assert (format != 0);
2349
2350 codec_priv_size = WAVEFORMATEX_SIZE;
2351 if (buf)
2352 codec_priv_size += gst_buffer_get_size (buf);
2353
2354 /* serialize waveformatex structure */
2355 codec_priv = g_malloc0 (codec_priv_size);
2356 GST_WRITE_UINT16_LE (codec_priv, format);
2357 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2358 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2359 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2360 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2361 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2362 if (buf)
2363 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2364 else
2365 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2366
2367 /* process codec private/initialization data, if any */
2368 if (buf) {
2369 gst_buffer_extract (buf, 0,
2370 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2371 }
2372
2373 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2374 gst_matroska_mux_free_codec_priv (context);
2375 context->codec_priv = (gpointer) codec_priv;
2376 context->codec_priv_size = codec_priv_size;
2377 }
2378
2379 return TRUE;
2380
2381 /* ERRORS */
2382 refuse_caps:
2383 {
2384 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2385 GST_PAD_NAME (pad), caps);
2386 return FALSE;
2387 }
2388 }
2389
2390 /* we probably don't have the data at start,
2391 * so have to reserve (a maximum) space to write this at the end.
2392 * bit spacy, but some formats can hold quite some */
2393 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2394
2395 /**
2396 * gst_matroska_mux_subtitle_pad_setcaps:
2397 * @pad: Pad which got the caps.
2398 * @caps: New caps.
2399 *
2400 * Setcaps function for subtitle sink pad.
2401 *
2402 * Returns: %TRUE on success.
2403 */
2404 static gboolean
gst_matroska_mux_subtitle_pad_setcaps(GstPad * pad,GstCaps * caps)2405 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2406 {
2407 /* There is now (at least) one such alement (kateenc), and I'm going
2408 to handle it here and claim it works when it can be piped back
2409 through GStreamer and VLC */
2410
2411 GstMatroskaTrackContext *context = NULL;
2412 GstMatroskaTrackSubtitleContext *scontext;
2413 GstMatroskaMux *mux;
2414 GstMatroskaPad *collect_pad;
2415 GstCollectData *data;
2416 const gchar *mimetype;
2417 GstStructure *structure;
2418 const GValue *value = NULL;
2419 GstBuffer *buf = NULL;
2420 gboolean ret = TRUE;
2421 GstCaps *old_caps;
2422
2423 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2424
2425 if ((old_caps = gst_pad_get_current_caps (pad))) {
2426 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
2427 && !gst_caps_is_equal (caps, old_caps)) {
2428 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2429 ("Caps changes are not supported by Matroska"));
2430 gst_caps_unref (old_caps);
2431 goto refuse_caps;
2432 }
2433 gst_caps_unref (old_caps);
2434 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
2435 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2436 ("Caps on pad %" GST_PTR_FORMAT
2437 " arrived late. Headers were already written", pad));
2438 goto refuse_caps;
2439 }
2440
2441 /* find context */
2442 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2443 g_assert (collect_pad);
2444 data = (GstCollectData *) (collect_pad);
2445
2446 context = collect_pad->track;
2447 g_assert (context);
2448 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2449 scontext = (GstMatroskaTrackSubtitleContext *) context;
2450
2451 structure = gst_caps_get_structure (caps, 0);
2452 mimetype = gst_structure_get_name (structure);
2453
2454 /* general setup */
2455 scontext->check_utf8 = 1;
2456 scontext->invalid_utf8 = 0;
2457 context->default_duration = 0;
2458
2459 if (!strcmp (mimetype, "subtitle/x-kate")) {
2460 const GValue *streamheader;
2461
2462 gst_matroska_mux_set_codec_id (context,
2463 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2464
2465 gst_matroska_mux_free_codec_priv (context);
2466
2467 streamheader = gst_structure_get_value (structure, "streamheader");
2468 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2469 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2470 ("kate stream headers missing or malformed"));
2471 ret = FALSE;
2472 goto exit;
2473 }
2474 } else if (!strcmp (mimetype, "text/x-raw")) {
2475 gst_matroska_mux_set_codec_id (context,
2476 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2477 } else if (!strcmp (mimetype, "application/x-ssa")) {
2478 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2479 } else if (!strcmp (mimetype, "application/x-ass")) {
2480 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2481 } else if (!strcmp (mimetype, "application/x-usf")) {
2482 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2483 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2484 gst_matroska_mux_set_codec_id (context,
2485 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2486 } else {
2487 ret = FALSE;
2488 goto exit;
2489 }
2490
2491 /* maybe some private data, e.g. vobsub */
2492 value = gst_structure_get_value (structure, "codec_data");
2493 if (value)
2494 buf = gst_value_get_buffer (value);
2495 if (buf != NULL) {
2496 GstMapInfo map;
2497 guint8 *priv_data = NULL;
2498
2499 gst_buffer_map (buf, &map, GST_MAP_READ);
2500
2501 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2502 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2503 " exceeded maximum (%d); discarding", pad,
2504 SUBTITLE_MAX_CODEC_PRIVATE);
2505 gst_buffer_unmap (buf, &map);
2506 return TRUE;
2507 }
2508
2509 gst_matroska_mux_free_codec_priv (context);
2510
2511 priv_data = g_malloc0 (map.size);
2512 memcpy (priv_data, map.data, map.size);
2513 context->codec_priv = priv_data;
2514 context->codec_priv_size = map.size;
2515 gst_buffer_unmap (buf, &map);
2516 }
2517
2518 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2519 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2520
2521 /* This pad is sparse. Now that we have caps on it, we can tell collectpads
2522 * not to actually wait for data when muxing */
2523 GST_COLLECT_PADS_STREAM_LOCK (mux->collect);
2524 GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_LOCKED);
2525 gst_collect_pads_set_waiting (mux->collect, data, FALSE);
2526 GST_COLLECT_PADS_STREAM_UNLOCK (mux->collect);
2527
2528 exit:
2529
2530 return ret;
2531
2532 /* ERRORS */
2533 refuse_caps:
2534 {
2535 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2536 GST_PAD_NAME (pad), caps);
2537 return FALSE;
2538 }
2539 }
2540
2541
2542 /**
2543 * gst_matroska_mux_request_new_pad:
2544 * @element: #GstMatroskaMux.
2545 * @templ: #GstPadTemplate.
2546 * @pad_name: New pad name.
2547 *
2548 * Request pad function for sink templates.
2549 *
2550 * Returns: New #GstPad.
2551 */
2552 static GstPad *
gst_matroska_mux_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)2553 gst_matroska_mux_request_new_pad (GstElement * element,
2554 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2555 {
2556 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2557 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2558 GstMatroskaPad *collect_pad;
2559 GstMatroskamuxPad *newpad;
2560 gchar *name = NULL;
2561 const gchar *pad_name = NULL;
2562 GstMatroskaCapsFunc capsfunc = NULL;
2563 GstMatroskaTrackContext *context = NULL;
2564 gint pad_id;
2565 const gchar *id = NULL;
2566
2567 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2568 /* don't mix named and unnamed pads, if the pad already exists we fail when
2569 * trying to add it */
2570 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2571 pad_name = req_name;
2572 } else {
2573 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2574 pad_name = name;
2575 }
2576 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2577 context = (GstMatroskaTrackContext *)
2578 g_new0 (GstMatroskaTrackAudioContext, 1);
2579 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2580 context->name = g_strdup ("Audio");
2581 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2582 /* don't mix named and unnamed pads, if the pad already exists we fail when
2583 * trying to add it */
2584 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2585 pad_name = req_name;
2586 } else {
2587 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2588 pad_name = name;
2589 }
2590 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2591 context = (GstMatroskaTrackContext *)
2592 g_new0 (GstMatroskaTrackVideoContext, 1);
2593 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2594 context->name = g_strdup ("Video");
2595 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2596 /* don't mix named and unnamed pads, if the pad already exists we fail when
2597 * trying to add it */
2598 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2599 pad_name = req_name;
2600 } else {
2601 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2602 pad_name = name;
2603 }
2604 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2605 context = (GstMatroskaTrackContext *)
2606 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2607 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2608 context->name = g_strdup ("Subtitle");
2609 /* setcaps may only provide proper one a lot later */
2610 id = "S_SUB_UNKNOWN";
2611 } else {
2612 GST_WARNING_OBJECT (mux, "This is not our template!");
2613 return NULL;
2614 }
2615
2616 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2617 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2618
2619 gst_matroskamux_pad_init (newpad);
2620 collect_pad = (GstMatroskaPad *)
2621 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2622 sizeof (GstMatroskaPad),
2623 (GstCollectDataDestroyNotify) gst_matroska_pad_free, TRUE);
2624
2625 collect_pad->mux = mux;
2626 collect_pad->track = context;
2627 gst_matroska_pad_reset (collect_pad, FALSE);
2628 if (id)
2629 gst_matroska_mux_set_codec_id (collect_pad->track, id);
2630 collect_pad->track->dts_only = FALSE;
2631
2632 collect_pad->capsfunc = capsfunc;
2633 gst_pad_set_active (GST_PAD (newpad), TRUE);
2634 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2635 goto pad_add_failed;
2636
2637 g_free (name);
2638
2639 mux->num_streams++;
2640
2641 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2642
2643 return GST_PAD (newpad);
2644
2645 /* ERROR cases */
2646 pad_add_failed:
2647 {
2648 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2649 g_free (name);
2650 gst_object_unref (newpad);
2651 return NULL;
2652 }
2653 }
2654
2655 /**
2656 * gst_matroska_mux_release_pad:
2657 * @element: #GstMatroskaMux.
2658 * @pad: Pad to release.
2659 *
2660 * Release a previously requested pad.
2661 */
2662 static void
gst_matroska_mux_release_pad(GstElement * element,GstPad * pad)2663 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2664 {
2665 GstMatroskaMux *mux;
2666 GSList *walk;
2667
2668 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2669
2670 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2671 GstCollectData *cdata = (GstCollectData *) walk->data;
2672 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2673
2674 if (cdata->pad == pad) {
2675 /*
2676 * observed duration, this will remain GST_CLOCK_TIME_NONE
2677 * only if the pad is reset
2678 */
2679 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2680
2681 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2682 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2683 collected_duration =
2684 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2685 }
2686
2687 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2688 && mux->duration < collected_duration)
2689 mux->duration = collected_duration;
2690
2691 break;
2692 }
2693 }
2694
2695 gst_collect_pads_remove_pad (mux->collect, pad);
2696 if (gst_element_remove_pad (element, pad))
2697 mux->num_streams--;
2698 }
2699
2700 static void
gst_matroska_mux_write_mastering_metadata(GstMatroskaMux * mux,GstMatroskaTrackVideoContext * videocontext)2701 gst_matroska_mux_write_mastering_metadata (GstMatroskaMux * mux,
2702 GstMatroskaTrackVideoContext * videocontext)
2703 {
2704 GstEbmlWrite *ebml = mux->ebml_write;
2705 guint64 master;
2706 GstVideoMasteringDisplayInfo *minfo = &videocontext->mastering_display_info;
2707 gdouble value;
2708 const gdouble chroma_scale = 50000;
2709 const gdouble luma_scale = 50000;
2710
2711 if (!videocontext->mastering_display_info_present)
2712 return;
2713
2714 master =
2715 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_MASTERINGMETADATA);
2716
2717 value = (gdouble) minfo->display_primaries[0].x / chroma_scale;
2718 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYX, value);
2719
2720 value = (gdouble) minfo->display_primaries[0].y / chroma_scale;
2721 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYY, value);
2722
2723 value = (gdouble) minfo->display_primaries[1].x / chroma_scale;
2724 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYX, value);
2725
2726 value = (gdouble) minfo->display_primaries[1].y / chroma_scale;
2727 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYY, value);
2728
2729 value = (gdouble) minfo->display_primaries[2].x / chroma_scale;
2730 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYX, value);
2731
2732 value = (gdouble) minfo->display_primaries[2].y / chroma_scale;
2733 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYY, value);
2734
2735 value = (gdouble) minfo->white_point.x / chroma_scale;
2736 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYX, value);
2737
2738 value = (gdouble) minfo->white_point.y / chroma_scale;
2739 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYY, value);
2740
2741 value = (gdouble) minfo->max_display_mastering_luminance / luma_scale;
2742 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMAX, value);
2743
2744 value = (gdouble) minfo->min_display_mastering_luminance / luma_scale;
2745 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMIN, value);
2746
2747 gst_ebml_write_master_finish (ebml, master);
2748 return;
2749 }
2750
2751 static void
gst_matroska_mux_write_colour(GstMatroskaMux * mux,GstMatroskaTrackVideoContext * videocontext)2752 gst_matroska_mux_write_colour (GstMatroskaMux * mux,
2753 GstMatroskaTrackVideoContext * videocontext)
2754 {
2755 GstEbmlWrite *ebml = mux->ebml_write;
2756 guint64 master;
2757 guint matrix_id = 0;
2758 guint range_id = 0;
2759 guint transfer_id = 0;
2760 guint primaries_id = 0;
2761
2762 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_VIDEOCOLOUR);
2763
2764 switch (videocontext->colorimetry.range) {
2765 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
2766 range_id = 0;
2767 break;
2768 case GST_VIDEO_COLOR_RANGE_16_235:
2769 range_id = 1;
2770 break;
2771 case GST_VIDEO_COLOR_RANGE_0_255:
2772 range_id = 2;
2773 }
2774
2775 matrix_id = gst_video_color_matrix_to_iso (videocontext->colorimetry.matrix);
2776 transfer_id =
2777 gst_video_transfer_function_to_iso (videocontext->colorimetry.transfer);
2778 primaries_id =
2779 gst_video_color_primaries_to_iso (videocontext->colorimetry.primaries);
2780
2781 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEORANGE, range_id);
2782 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS,
2783 matrix_id);
2784 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS,
2785 transfer_id);
2786 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPRIMARIES, primaries_id);
2787 if (videocontext->content_light_level.max_content_light_level &&
2788 videocontext->content_light_level.max_frame_average_light_level) {
2789 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXCLL,
2790 videocontext->content_light_level.max_content_light_level);
2791 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXFALL,
2792 videocontext->content_light_level.max_frame_average_light_level);
2793 }
2794
2795 gst_matroska_mux_write_mastering_metadata (mux, videocontext);
2796 gst_ebml_write_master_finish (ebml, master);
2797 }
2798
2799 /**
2800 * gst_matroska_mux_track_header:
2801 * @mux: #GstMatroskaMux
2802 * @context: Tack context.
2803 *
2804 * Write a track header.
2805 */
2806 static void
gst_matroska_mux_track_header(GstMatroskaMux * mux,GstMatroskaTrackContext * context)2807 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2808 GstMatroskaTrackContext * context)
2809 {
2810 GstEbmlWrite *ebml = mux->ebml_write;
2811 guint64 master;
2812
2813 /* TODO: check if everything necessary is written and check default values */
2814
2815 /* track type goes before the type-specific stuff */
2816 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2817 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2818
2819 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2820 if (context->default_duration) {
2821 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2822 context->default_duration);
2823 }
2824 if (context->language) {
2825 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2826 context->language);
2827 }
2828
2829 /* FIXME: until we have a nice way of getting the codecname
2830 * out of the caps, I'm not going to enable this. Too much
2831 * (useless, double, boring) work... */
2832 /* TODO: Use value from tags if any */
2833 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2834 context->codec_name); */
2835 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2836
2837 /* type-specific stuff */
2838 switch (context->type) {
2839 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2840 GstMatroskaTrackVideoContext *videocontext =
2841 (GstMatroskaTrackVideoContext *) context;
2842
2843 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2844 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2845 videocontext->pixel_width);
2846 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2847 videocontext->pixel_height);
2848 if (videocontext->display_width && videocontext->display_height) {
2849 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2850 videocontext->display_width);
2851 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2852 videocontext->display_height);
2853 }
2854 switch (videocontext->interlace_mode) {
2855 case GST_MATROSKA_INTERLACE_MODE_INTERLACED:
2856 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2857 break;
2858 case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE:
2859 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 2);
2860 break;
2861 default:
2862 break;
2863 }
2864
2865 if (videocontext->fourcc) {
2866 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2867
2868 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2869 (gpointer) & fcc_le, 4);
2870 }
2871 gst_matroska_mux_write_colour (mux, videocontext);
2872 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2873 guint64 stereo_mode = 0;
2874
2875 switch (videocontext->multiview_mode) {
2876 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2877 break;
2878 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2879 if (videocontext->multiview_flags &
2880 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2881 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2882 else
2883 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2884 break;
2885 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2886 if (videocontext->multiview_flags &
2887 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2888 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2889 else
2890 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2891 break;
2892 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2893 if (videocontext->multiview_flags &
2894 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2895 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2896 else
2897 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2898 break;
2899 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2900 if (videocontext->multiview_flags &
2901 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2902 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2903 else
2904 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2905 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2906 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2907 GST_FIXME_OBJECT (mux,
2908 "Frame-by-frame stereoscopic mode not fully implemented");
2909 break;
2910 default:
2911 GST_WARNING_OBJECT (mux,
2912 "Multiview mode %d not supported in Matroska/WebM",
2913 videocontext->multiview_mode);
2914 break;
2915 }
2916
2917 if (stereo_mode != 0)
2918 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2919 stereo_mode);
2920 }
2921 gst_ebml_write_master_finish (ebml, master);
2922
2923 break;
2924 }
2925
2926 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2927 GstMatroskaTrackAudioContext *audiocontext =
2928 (GstMatroskaTrackAudioContext *) context;
2929
2930 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2931 if (audiocontext->samplerate != 8000)
2932 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2933 audiocontext->samplerate);
2934 if (audiocontext->channels != 1)
2935 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2936 audiocontext->channels);
2937 if (audiocontext->bitdepth) {
2938 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2939 audiocontext->bitdepth);
2940 }
2941
2942 gst_ebml_write_master_finish (ebml, master);
2943
2944 break;
2945 }
2946
2947 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2948 break;
2949 }
2950 default:
2951 /* doesn't need type-specific data */
2952 break;
2953 }
2954
2955 GST_DEBUG_OBJECT (mux, "Wrote track header. Codec %s", context->codec_id);
2956
2957 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2958 if (context->codec_priv)
2959 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2960 context->codec_priv, context->codec_priv_size);
2961
2962 if (context->seek_preroll) {
2963 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2964 context->seek_preroll);
2965 }
2966
2967 if (context->codec_delay) {
2968 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2969 context->codec_delay);
2970 }
2971 }
2972
2973 static void
gst_matroska_mux_write_chapter_title(const gchar * title,GstEbmlWrite * ebml)2974 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2975 {
2976 guint64 title_master;
2977
2978 title_master =
2979 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2980
2981 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2982 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2983 GST_MATROSKA_MUX_CHAPLANG);
2984
2985 gst_ebml_write_master_finish (ebml, title_master);
2986 }
2987
2988 static GstTocEntry *
gst_matroska_mux_write_chapter(GstMatroskaMux * mux,GstTocEntry * edition,GstTocEntry * entry,GstEbmlWrite * ebml,guint64 * master_chapters,guint64 * master_edition)2989 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2990 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2991 guint64 * master_edition)
2992 {
2993 guint64 master_chapteratom;
2994 GList *cur;
2995 guint count, i;
2996 gchar *title;
2997 gint64 start, stop;
2998 guint64 uid;
2999 gchar s_uid[32];
3000 GstTocEntry *internal_chapter, *internal_nested;
3001 GstTagList *tags;
3002
3003 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
3004 *master_chapters =
3005 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
3006
3007 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
3008 /* create uid for the parent */
3009 *master_edition =
3010 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
3011
3012 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID,
3013 g_ascii_strtoull (gst_toc_entry_get_uid (edition), NULL, 10));
3014 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
3015 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
3016 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
3017 }
3018
3019 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
3020 tags = gst_toc_entry_get_tags (entry);
3021 if (tags != NULL) {
3022 tags = gst_tag_list_copy (tags);
3023 }
3024
3025 /* build internal chapter */
3026 uid = gst_matroska_mux_create_uid (mux);
3027 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
3028 internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
3029
3030 /* Write the chapter entry */
3031 master_chapteratom =
3032 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
3033
3034 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
3035 /* Store the user provided UID in the ChapterStringUID */
3036 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPTERSTRINGUID,
3037 gst_toc_entry_get_uid (entry));
3038 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
3039 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
3040 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
3041 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
3042
3043 /* write current ChapterDisplays before the nested chapters */
3044 if (G_LIKELY (tags != NULL)) {
3045 count = gst_tag_list_get_tag_size (tags, GST_TAG_TITLE);
3046
3047 for (i = 0; i < count; ++i) {
3048 gst_tag_list_get_string_index (tags, GST_TAG_TITLE, i, &title);
3049 /* FIXME: handle ChapterLanguage entries */
3050 gst_matroska_mux_write_chapter_title (title, ebml);
3051 g_free (title);
3052 }
3053
3054 /* remove title tag */
3055 if (G_LIKELY (count > 0))
3056 gst_tag_list_remove_tag (tags, GST_TAG_TITLE);
3057
3058 gst_toc_entry_set_tags (internal_chapter, tags);
3059 }
3060
3061 /* Write nested chapters */
3062 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3063 cur = cur->next) {
3064 internal_nested = gst_matroska_mux_write_chapter (mux, NULL, cur->data,
3065 ebml, NULL, NULL);
3066
3067 gst_toc_entry_append_sub_entry (internal_chapter, internal_nested);
3068 }
3069
3070 gst_ebml_write_master_finish (ebml, master_chapteratom);
3071
3072 return internal_chapter;
3073 }
3074
3075 static GstTocEntry *
gst_matroska_mux_write_chapter_edition(GstMatroskaMux * mux,GstTocEntry * edition,GList * chapters,GstEbmlWrite * ebml,guint64 * master_chapters)3076 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
3077 GstTocEntry * edition, GList * chapters, GstEbmlWrite * ebml,
3078 guint64 * master_chapters)
3079 {
3080 guint64 master_edition = 0;
3081 gchar s_uid[32];
3082 GList *cur;
3083 GstTocEntry *internal_edition, *internal_chapter;
3084 GstTagList *tags = NULL;
3085
3086 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT,
3087 gst_matroska_mux_create_uid (mux));
3088
3089 if (edition != NULL) {
3090 /* Edition entry defined, get its tags */
3091 tags = gst_toc_entry_get_tags (edition);
3092 if (tags != NULL) {
3093 tags = gst_tag_list_copy (tags);
3094 }
3095 }
3096
3097 internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
3098 if (tags != NULL) {
3099 gst_toc_entry_set_tags (internal_edition, tags);
3100 }
3101
3102 for (cur = g_list_first (chapters); cur != NULL; cur = cur->next) {
3103 internal_chapter = gst_matroska_mux_write_chapter (mux, internal_edition,
3104 cur->data, ebml, master_chapters, &master_edition);
3105
3106 gst_toc_entry_append_sub_entry (internal_edition, internal_chapter);
3107 }
3108
3109 if (G_LIKELY (master_edition != 0))
3110 gst_ebml_write_master_finish (ebml, master_edition);
3111
3112 return internal_edition;
3113 }
3114
3115 /**
3116 * gst_matroska_mux_start:
3117 * @mux: #GstMatroskaMux
3118 *
3119 * Start a new matroska file (write headers etc...)
3120 */
3121 static void
gst_matroska_mux_start(GstMatroskaMux * mux,GstMatroskaPad * first_pad,GstBuffer * first_pad_buf)3122 gst_matroska_mux_start (GstMatroskaMux * mux, GstMatroskaPad * first_pad,
3123 GstBuffer * first_pad_buf)
3124 {
3125 GstEbmlWrite *ebml = mux->ebml_write;
3126 const gchar *doctype;
3127 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
3128 GST_MATROSKA_ID_TRACKS,
3129 GST_MATROSKA_ID_CHAPTERS,
3130 GST_MATROSKA_ID_CUES,
3131 GST_MATROSKA_ID_TAGS,
3132 0
3133 };
3134 const gchar *media_type;
3135 gboolean audio_only;
3136 guint64 master, child;
3137 GSList *collected;
3138 int i;
3139 guint tracknum = 1;
3140 GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
3141 GstClockTime duration = 0;
3142 guint32 segment_uid[4];
3143 gint64 time;
3144 gchar s_id[32];
3145 GstToc *toc;
3146
3147 /* if not streaming, check if downstream is seekable */
3148 if (!mux->ebml_write->streamable) {
3149 gboolean seekable;
3150 GstQuery *query;
3151
3152 query = gst_query_new_seeking (GST_FORMAT_BYTES);
3153 if (gst_pad_peer_query (mux->srcpad, query)) {
3154 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
3155 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
3156 } else {
3157 /* assume seeking is not supported if query not handled downstream */
3158 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
3159 seekable = FALSE;
3160 }
3161 if (!seekable) {
3162 mux->ebml_write->streamable = TRUE;
3163 g_object_notify (G_OBJECT (mux), "streamable");
3164 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
3165 "streamable=false. Will ignore that and create streamable output "
3166 "instead");
3167 }
3168 gst_query_unref (query);
3169 }
3170
3171 /* stream-start (FIXME: create id based on input ids) */
3172 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
3173 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
3174
3175 /* output caps */
3176 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
3177 if (mux->is_webm) {
3178 media_type = (audio_only) ? "audio/webm" : "video/webm";
3179 } else {
3180 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
3181 }
3182 ebml->caps = gst_caps_new_empty_simple (media_type);
3183 gst_pad_set_caps (mux->srcpad, ebml->caps);
3184 /* we start with a EBML header */
3185 doctype = mux->doctype;
3186 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
3187 doctype, mux->doctype_version);
3188 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
3189
3190 /* the rest of the header is cached */
3191 gst_ebml_write_set_cache (ebml, 0x1000);
3192
3193 /* start a segment */
3194 mux->segment_pos =
3195 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
3196 mux->segment_master = ebml->pos;
3197
3198 if (!mux->ebml_write->streamable) {
3199 /* seekhead (table of contents) - we set the positions later */
3200 mux->seekhead_pos = ebml->pos;
3201 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
3202 for (i = 0; seekhead_id[i] != 0; i++) {
3203 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
3204 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
3205 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
3206 gst_ebml_write_master_finish (ebml, child);
3207 }
3208 gst_ebml_write_master_finish (ebml, master);
3209 }
3210
3211 if (mux->ebml_write->streamable) {
3212 const GstTagList *tags;
3213 gboolean has_main_tags;
3214
3215 /* tags */
3216 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3217 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3218
3219 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
3220 guint64 master_tags, master_tag;
3221
3222 GST_DEBUG_OBJECT (mux, "Writing tags");
3223
3224 mux->tags_pos = ebml->pos;
3225 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3226 if (has_main_tags) {
3227 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3228 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3229 gst_ebml_write_master_finish (ebml, master_tag);
3230 }
3231 gst_matroska_mux_write_streams_tags (mux);
3232 gst_ebml_write_master_finish (ebml, master_tags);
3233 }
3234 }
3235
3236 /* segment info */
3237 mux->info_pos = ebml->pos;
3238 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
3239
3240 /* WebM does not support SegmentUID field on SegmentInfo */
3241 if (!mux->is_webm) {
3242 for (i = 0; i < 4; i++) {
3243 segment_uid[i] = g_random_int ();
3244 }
3245 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
3246 (guint8 *) segment_uid, 16);
3247 }
3248
3249 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
3250 mux->duration_pos = ebml->pos;
3251 /* get duration */
3252 if (!mux->ebml_write->streamable) {
3253 for (collected = mux->collect->data; collected;
3254 collected = g_slist_next (collected)) {
3255 GstMatroskaPad *collect_pad;
3256 GstPad *thepad;
3257 gint64 trackduration;
3258
3259 collect_pad = (GstMatroskaPad *) collected->data;
3260 thepad = collect_pad->collect.pad;
3261
3262 /* Query the total length of the track. */
3263 GST_DEBUG_OBJECT (thepad, "querying peer duration");
3264 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
3265 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
3266 GST_TIME_ARGS (trackduration));
3267 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
3268 duration = (GstClockTime) trackduration;
3269 }
3270 }
3271 }
3272 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3273 gst_guint64_to_gdouble (duration) /
3274 gst_guint64_to_gdouble (mux->time_scale));
3275 }
3276 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
3277 "GStreamer matroskamux version " PACKAGE_VERSION);
3278 if (mux->writing_app && mux->writing_app[0]) {
3279 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
3280 }
3281 if (mux->creation_time != NULL) {
3282 time = g_date_time_to_unix (mux->creation_time) * GST_SECOND;
3283 time += g_date_time_get_microsecond (mux->creation_time) * GST_USECOND;
3284 } else {
3285 time = g_get_real_time () * GST_USECOND;
3286 }
3287 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time);
3288 gst_ebml_write_master_finish (ebml, master);
3289
3290 /* tracks */
3291 mux->tracks_pos = ebml->pos;
3292 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
3293
3294 for (collected = mux->collect->data; collected;
3295 collected = g_slist_next (collected)) {
3296 GstMatroskaPad *collect_pad;
3297 GstBuffer *buf;
3298
3299 collect_pad = (GstMatroskaPad *) collected->data;
3300
3301 /* This will cause an error at a later time */
3302 if (collect_pad->track->codec_id == NULL)
3303 continue;
3304
3305 /* Find the smallest timestamp so we can offset all streams by this to
3306 * start at 0 */
3307 if (mux->offset_to_zero) {
3308 GstClockTime ts;
3309
3310 if (collect_pad == first_pad)
3311 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3312 else
3313 buf = gst_collect_pads_peek (mux->collect, collected->data);
3314
3315 if (buf) {
3316 ts = gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3317
3318 if (earliest_time == GST_CLOCK_TIME_NONE)
3319 earliest_time = ts;
3320 else if (ts != GST_CLOCK_TIME_NONE && ts < earliest_time)
3321 earliest_time = ts;
3322 }
3323
3324 if (buf)
3325 gst_buffer_unref (buf);
3326 }
3327
3328 /* For audio tracks, use the first buffers duration as the default
3329 * duration if we didn't get any better idea from the caps event already
3330 */
3331 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO &&
3332 collect_pad->track->default_duration == 0) {
3333 if (collect_pad == first_pad)
3334 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3335 else
3336 buf = gst_collect_pads_peek (mux->collect, collected->data);
3337
3338 if (buf && GST_BUFFER_DURATION_IS_VALID (buf))
3339 collect_pad->track->default_duration =
3340 GST_BUFFER_DURATION (buf) + collect_pad->track->codec_delay;
3341 if (buf)
3342 gst_buffer_unref (buf);
3343 }
3344
3345 collect_pad->track->num = tracknum++;
3346 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
3347 gst_matroska_mux_track_header (mux, collect_pad->track);
3348 gst_ebml_write_master_finish (ebml, child);
3349 /* some remaining pad/track setup */
3350 collect_pad->default_duration_scaled =
3351 gst_util_uint64_scale (collect_pad->track->default_duration,
3352 1, mux->time_scale);
3353 }
3354 gst_ebml_write_master_finish (ebml, master);
3355
3356 mux->earliest_time = earliest_time == GST_CLOCK_TIME_NONE ? 0 : earliest_time;
3357
3358 /* chapters */
3359 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3360 if (toc != NULL && !mux->ebml_write->streamable) {
3361 guint64 master_chapters = 0;
3362 GstTocEntry *internal_edition;
3363 GList *cur, *chapters;
3364
3365 GST_DEBUG ("Writing chapters");
3366
3367 /* There are two UIDs for Chapters:
3368 * - The ChapterUID is a mandatory unsigned integer which internally
3369 * refers to a given chapter. Except for the title & language which use
3370 * dedicated fields, this UID can also be used to add tags to the Chapter.
3371 * The tags come in a separate section of the container.
3372 * - The ChapterStringUID is an optional UTF-8 string which also uniquely
3373 * refers to a chapter but from an external perspective. It can act as a
3374 * "WebVTT cue identifier" which "can be used to reference a specific cue,
3375 * for example from script or CSS".
3376 *
3377 * The ChapterUID will be generated and checked for unicity, while the
3378 * ChapterStringUID will receive the user defined UID.
3379 *
3380 * In order to be able to refer to chapters from the tags section,
3381 * we must maintain an internal Toc tree with the generated ChapterUID
3382 * (see gst_matroska_mux_write_toc_entry_tags) */
3383
3384 /* Check whether we have editions or chapters at the root level. */
3385 cur = gst_toc_get_entries (toc);
3386 if (cur != NULL) {
3387 mux->chapters_pos = ebml->pos;
3388
3389 mux->internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
3390
3391 if (gst_toc_entry_get_entry_type (cur->data) ==
3392 GST_TOC_ENTRY_TYPE_EDITION) {
3393 /* Editions at the root level */
3394 for (; cur != NULL; cur = cur->next) {
3395 chapters = gst_toc_entry_get_sub_entries (cur->data);
3396 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3397 cur->data, chapters, ebml, &master_chapters);
3398 gst_toc_append_entry (mux->internal_toc, internal_edition);
3399 }
3400 } else {
3401 /* Chapters at the root level */
3402 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3403 NULL, cur, ebml, &master_chapters);
3404 gst_toc_append_entry (mux->internal_toc, internal_edition);
3405 }
3406
3407 /* close master element if any edition was written */
3408 if (G_LIKELY (master_chapters != 0))
3409 gst_ebml_write_master_finish (ebml, master_chapters);
3410 }
3411 }
3412
3413 /* lastly, flush the cache */
3414 gst_ebml_write_flush_cache (ebml, FALSE, 0);
3415
3416 if (toc != NULL)
3417 gst_toc_unref (toc);
3418 }
3419
3420 /* TODO: more sensible tag mappings */
3421 static const struct
3422 {
3423 const gchar *matroska_tagname;
3424 const gchar *gstreamer_tagname;
3425 }
3426 gst_matroska_tag_conv[] = {
3427 {
3428 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
3429 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
3430 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
3431 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
3432 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
3433 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
3434 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
3435 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
3436 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
3437 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
3438 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
3439 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
3440 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
3441 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
3442 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
3443 };
3444
3445 /* Every stagefright implementation on android up to and including 6.0.1 is using
3446 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
3447 so before outputting tags and tag elements we better make sure that there are
3448 actually tags we are going to write */
3449 static gboolean
gst_matroska_mux_tag_list_is_empty(const GstTagList * list)3450 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3451 {
3452 int i;
3453 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3454 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3455 int i;
3456 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3457 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3458 if (strcmp (tagname_gst, tag) == 0) {
3459 GValue src = { 0, };
3460 gchar *dest;
3461
3462 if (!gst_tag_list_copy_value (&src, list, tag))
3463 break;
3464 dest = gst_value_serialize (&src);
3465
3466 g_value_unset (&src);
3467 if (dest) {
3468 g_free (dest);
3469 return FALSE;
3470 }
3471 }
3472 }
3473 }
3474 return TRUE;
3475 }
3476
3477 static void
gst_matroska_mux_write_simple_tag(const GstTagList * list,const gchar * tag,gpointer data)3478 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3479 gpointer data)
3480 {
3481 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3482 guint i;
3483 guint64 simpletag_master;
3484
3485 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3486 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3487 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3488
3489 if (strcmp (tagname_gst, tag) == 0) {
3490 GValue src = { 0, };
3491 gchar *dest;
3492
3493 if (!gst_tag_list_copy_value (&src, list, tag))
3494 break;
3495 if ((dest = gst_value_serialize (&src))) {
3496
3497 simpletag_master = gst_ebml_write_master_start (ebml,
3498 GST_MATROSKA_ID_SIMPLETAG);
3499 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3500 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3501 gst_ebml_write_master_finish (ebml, simpletag_master);
3502 g_free (dest);
3503 } else {
3504 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3505 }
3506 g_value_unset (&src);
3507 break;
3508 }
3509 }
3510 }
3511
3512 static void
gst_matroska_mux_write_stream_tags(GstMatroskaMux * mux,GstMatroskaPad * mpad)3513 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3514 {
3515 guint64 master_tag, master_targets;
3516 GstEbmlWrite *ebml;
3517
3518 ebml = mux->ebml_write;
3519
3520 if (G_UNLIKELY (mpad->tags == NULL
3521 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3522 return;
3523
3524 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3525 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3526
3527 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3528
3529 gst_ebml_write_master_finish (ebml, master_targets);
3530 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3531 gst_ebml_write_master_finish (ebml, master_tag);
3532 }
3533
3534 static void
gst_matroska_mux_write_streams_tags(GstMatroskaMux * mux)3535 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3536 {
3537 GSList *walk;
3538
3539 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3540 GstMatroskaPad *collect_pad;
3541
3542 collect_pad = (GstMatroskaPad *) walk->data;
3543
3544 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3545 }
3546 }
3547
3548 static gboolean
gst_matroska_mux_streams_have_tags(GstMatroskaMux * mux)3549 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3550 {
3551 GSList *walk;
3552
3553 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3554 GstMatroskaPad *collect_pad;
3555
3556 collect_pad = (GstMatroskaPad *) walk->data;
3557 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3558 return TRUE;
3559 }
3560 return FALSE;
3561 }
3562
3563 static void
gst_matroska_mux_write_toc_entry_tags(GstMatroskaMux * mux,const GstTocEntry * entry,guint64 * master_tags,gboolean * has_tags)3564 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3565 const GstTocEntry * entry, guint64 * master_tags, gboolean * has_tags)
3566 {
3567 guint64 master_tag, master_targets;
3568 GstEbmlWrite *ebml;
3569 GList *cur;
3570 const GstTagList *tags;
3571
3572 ebml = mux->ebml_write;
3573
3574 tags = gst_toc_entry_get_tags (entry);
3575 if (G_UNLIKELY (tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags))) {
3576 *has_tags = TRUE;
3577
3578 if (*master_tags == 0) {
3579 mux->tags_pos = ebml->pos;
3580 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3581 }
3582
3583 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3584 master_targets =
3585 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3586
3587 if (gst_toc_entry_get_entry_type (entry) == GST_TOC_ENTRY_TYPE_EDITION)
3588 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3589 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3590 else
3591 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3592 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3593
3594 gst_ebml_write_master_finish (ebml, master_targets);
3595 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3596 gst_ebml_write_master_finish (ebml, master_tag);
3597 }
3598
3599 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3600 cur = cur->next) {
3601 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags,
3602 has_tags);
3603 }
3604 }
3605
3606 /**
3607 * gst_matroska_mux_finish:
3608 * @mux: #GstMatroskaMux
3609 *
3610 * Finish a new matroska file (write index etc...)
3611 */
3612 static void
gst_matroska_mux_finish(GstMatroskaMux * mux)3613 gst_matroska_mux_finish (GstMatroskaMux * mux)
3614 {
3615 GstEbmlWrite *ebml = mux->ebml_write;
3616 guint64 pos;
3617 guint64 duration = 0;
3618 GSList *collected;
3619 const GstTagList *tags, *toc_tags;
3620 const GstToc *toc;
3621 gboolean has_main_tags, toc_has_tags = FALSE;
3622 GList *cur;
3623
3624 /* finish last cluster */
3625 if (mux->cluster) {
3626 gst_ebml_write_master_finish (ebml, mux->cluster);
3627 }
3628
3629 /* cues */
3630 if (mux->index != NULL) {
3631 guint n;
3632 guint64 master, pointentry_master, trackpos_master;
3633
3634 mux->cues_pos = ebml->pos;
3635 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3636 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3637
3638 for (n = 0; n < mux->num_indexes; n++) {
3639 GstMatroskaIndex *idx = &mux->index[n];
3640
3641 pointentry_master = gst_ebml_write_master_start (ebml,
3642 GST_MATROSKA_ID_POINTENTRY);
3643 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3644 idx->time / mux->time_scale);
3645 trackpos_master = gst_ebml_write_master_start (ebml,
3646 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3647 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3648 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3649 idx->pos - mux->segment_master);
3650 gst_ebml_write_master_finish (ebml, trackpos_master);
3651 gst_ebml_write_master_finish (ebml, pointentry_master);
3652 }
3653
3654 gst_ebml_write_master_finish (ebml, master);
3655 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3656 }
3657
3658 /* tags */
3659 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3660 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3661 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3662
3663 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux) || toc != NULL) {
3664 guint64 master_tags = 0, master_tag;
3665
3666 GST_DEBUG_OBJECT (mux, "Writing tags");
3667
3668 if (has_main_tags) {
3669 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3670 mux->tags_pos = ebml->pos;
3671 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3672 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3673
3674 if (tags != NULL)
3675 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3676 if (mux->internal_toc != NULL) {
3677 toc_tags = gst_toc_get_tags (mux->internal_toc);
3678 toc_has_tags = (toc_tags != NULL);
3679 gst_tag_list_foreach (toc_tags, gst_matroska_mux_write_simple_tag,
3680 ebml);
3681 }
3682
3683 gst_ebml_write_master_finish (ebml, master_tag);
3684 }
3685
3686 if (mux->internal_toc != NULL) {
3687 for (cur = gst_toc_get_entries (mux->internal_toc); cur != NULL;
3688 cur = cur->next) {
3689 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags,
3690 &toc_has_tags);
3691 }
3692 }
3693
3694 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3695 mux->tags_pos = ebml->pos;
3696 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3697 }
3698 gst_matroska_mux_write_streams_tags (mux);
3699
3700 if (master_tags != 0)
3701 gst_ebml_write_master_finish (ebml, master_tags);
3702 }
3703
3704 /* update seekhead. We know that:
3705 * - a seekhead contains 5 entries.
3706 * - order of entries is as above.
3707 * - a seekhead has a 4-byte header + 8-byte length
3708 * - each entry is 2-byte master, 2-byte ID pointer,
3709 * 2-byte length pointer, all 8/1-byte length, 4-
3710 * byte ID and 8-byte length pointer, where the
3711 * length pointer starts at 20.
3712 * - all entries are local to the segment (so pos - segment_master).
3713 * - so each entry is at 12 + 20 + num * 28. */
3714 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3715 mux->info_pos - mux->segment_master);
3716 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3717 mux->tracks_pos - mux->segment_master);
3718 if (toc != NULL && mux->chapters_pos > 0) {
3719 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3720 mux->chapters_pos - mux->segment_master);
3721 } else {
3722 /* void'ify */
3723 guint64 my_pos = ebml->pos;
3724
3725 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3726 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3727 gst_ebml_write_seek (ebml, my_pos);
3728 }
3729 if (mux->index != NULL) {
3730 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3731 mux->cues_pos - mux->segment_master);
3732 } else {
3733 /* void'ify */
3734 guint64 my_pos = ebml->pos;
3735
3736 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3737 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3738 gst_ebml_write_seek (ebml, my_pos);
3739 }
3740
3741 if (mux->tags_pos != 0 || toc_has_tags) {
3742 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3743 mux->tags_pos - mux->segment_master);
3744 } else {
3745 /* void'ify */
3746 guint64 my_pos = ebml->pos;
3747
3748 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3749 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3750 gst_ebml_write_seek (ebml, my_pos);
3751 }
3752
3753 if (toc != NULL) {
3754 gst_toc_unref (toc);
3755 }
3756
3757 /* loop tracks:
3758 * - first get the overall duration
3759 * (a released track may have left a duration in here)
3760 * - write some track header data for subtitles
3761 */
3762 duration = mux->duration;
3763 pos = ebml->pos;
3764 for (collected = mux->collect->data; collected;
3765 collected = g_slist_next (collected)) {
3766 GstMatroskaPad *collect_pad;
3767 /*
3768 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3769 * since this means buffer without timestamps that is not possible
3770 */
3771 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3772
3773 collect_pad = (GstMatroskaPad *) collected->data;
3774
3775 GST_DEBUG_OBJECT (mux,
3776 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3777 " end ts %" GST_TIME_FORMAT, collect_pad,
3778 GST_TIME_ARGS (collect_pad->start_ts),
3779 GST_TIME_ARGS (collect_pad->end_ts));
3780
3781 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3782 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3783 collected_duration =
3784 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3785 GST_DEBUG_OBJECT (collect_pad->collect.pad,
3786 "final track duration: %" GST_TIME_FORMAT,
3787 GST_TIME_ARGS (collected_duration));
3788 } else {
3789 GST_WARNING_OBJECT (collect_pad->collect.pad,
3790 "unable to get final track duration");
3791 }
3792 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3793 duration < collected_duration)
3794 duration = collected_duration;
3795
3796 }
3797
3798 /* seek back (optional, but do anyway) */
3799 gst_ebml_write_seek (ebml, pos);
3800
3801 /* update duration */
3802 if (duration != 0) {
3803 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3804 GST_TIME_ARGS (duration));
3805 pos = mux->ebml_write->pos;
3806 gst_ebml_write_seek (ebml, mux->duration_pos);
3807 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3808 gst_guint64_to_gdouble (duration) /
3809 gst_guint64_to_gdouble (mux->time_scale));
3810 gst_ebml_write_seek (ebml, pos);
3811 } else {
3812 /* void'ify */
3813 guint64 my_pos = ebml->pos;
3814
3815 gst_ebml_write_seek (ebml, mux->duration_pos);
3816 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3817 gst_ebml_write_seek (ebml, my_pos);
3818 }
3819 GST_DEBUG_OBJECT (mux, "finishing segment");
3820 /* finish segment - this also writes element length */
3821 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3822 }
3823
3824 /**
3825 * gst_matroska_mux_buffer_header:
3826 * @track: Track context.
3827 * @relative_timestamp: relative timestamp of the buffer
3828 * @flags: Buffer flags.
3829 *
3830 * Create a buffer containing buffer header.
3831 *
3832 * Returns: New buffer.
3833 */
3834 static GstBuffer *
gst_matroska_mux_create_buffer_header(GstMatroskaTrackContext * track,gint16 relative_timestamp,int flags)3835 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3836 gint16 relative_timestamp, int flags)
3837 {
3838 GstBuffer *hdr;
3839 guint8 *data = g_malloc (4);
3840
3841 hdr = gst_buffer_new_wrapped (data, 4);
3842 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3843 data[0] = track->num | 0x80;
3844 /* time relative to clustertime */
3845 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3846
3847 /* flags */
3848 data[3] = flags;
3849
3850 return hdr;
3851 }
3852
3853 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3854 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3855 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3856
3857 static GstBuffer *
gst_matroska_mux_handle_dirac_packet(GstMatroskaMux * mux,GstMatroskaPad * collect_pad,GstBuffer * buf)3858 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3859 GstMatroskaPad * collect_pad, GstBuffer * buf)
3860 {
3861 GstMatroskaTrackVideoContext *ctx =
3862 (GstMatroskaTrackVideoContext *) collect_pad->track;
3863 GstMapInfo map;
3864 guint8 *data;
3865 gsize size;
3866 guint8 parse_code;
3867 guint32 next_parse_offset;
3868 GstBuffer *ret = NULL;
3869 gboolean is_muxing_unit = FALSE;
3870
3871 gst_buffer_map (buf, &map, GST_MAP_READ);
3872 data = map.data;
3873 size = map.size;
3874
3875 if (size < 13) {
3876 gst_buffer_unmap (buf, &map);
3877 gst_buffer_unref (buf);
3878 return ret;
3879 }
3880
3881 /* Check if this buffer contains a picture or end-of-sequence packet */
3882 while (size >= 13) {
3883 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3884 gst_buffer_unmap (buf, &map);
3885 gst_buffer_unref (buf);
3886 return ret;
3887 }
3888
3889 parse_code = GST_READ_UINT8 (data + 4);
3890 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3891 if (ctx->dirac_unit) {
3892 gst_buffer_unref (ctx->dirac_unit);
3893 ctx->dirac_unit = NULL;
3894 }
3895 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3896 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3897 is_muxing_unit = TRUE;
3898 break;
3899 }
3900
3901 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3902
3903 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3904 break;
3905
3906 data += next_parse_offset;
3907 size -= next_parse_offset;
3908 }
3909
3910 if (ctx->dirac_unit)
3911 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3912 else
3913 ctx->dirac_unit = gst_buffer_ref (buf);
3914
3915 gst_buffer_unmap (buf, &map);
3916
3917 if (is_muxing_unit) {
3918 ret = gst_buffer_make_writable (ctx->dirac_unit);
3919 ctx->dirac_unit = NULL;
3920 gst_buffer_copy_into (ret, buf,
3921 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3922 gst_buffer_unref (buf);
3923 } else {
3924 gst_buffer_unref (buf);
3925 ret = NULL;
3926 }
3927
3928 return ret;
3929 }
3930
3931 static void
gst_matroska_mux_stop_streamheader(GstMatroskaMux * mux)3932 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3933 {
3934 GstCaps *caps;
3935 GstStructure *s;
3936 GValue streamheader = { 0 };
3937 GValue bufval = { 0 };
3938 GstBuffer *streamheader_buffer;
3939 GstEbmlWrite *ebml = mux->ebml_write;
3940
3941 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3942 caps = gst_caps_copy (mux->ebml_write->caps);
3943 s = gst_caps_get_structure (caps, 0);
3944 g_value_init (&streamheader, GST_TYPE_ARRAY);
3945 g_value_init (&bufval, GST_TYPE_BUFFER);
3946 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3947 gst_value_set_buffer (&bufval, streamheader_buffer);
3948 gst_value_array_append_value (&streamheader, &bufval);
3949 g_value_unset (&bufval);
3950 gst_structure_set_value (s, "streamheader", &streamheader);
3951 g_value_unset (&streamheader);
3952 gst_caps_replace (&ebml->caps, caps);
3953 gst_buffer_unref (streamheader_buffer);
3954 gst_pad_set_caps (mux->srcpad, caps);
3955 gst_caps_unref (caps);
3956 }
3957
3958 /**
3959 * gst_matroska_mux_write_data:
3960 * @mux: #GstMatroskaMux
3961 * @collect_pad: #GstMatroskaPad with the data
3962 *
3963 * Write collected data (called from gst_matroska_mux_collected).
3964 *
3965 * Returns: Result of the gst_pad_push issued to write the data.
3966 */
3967 static GstFlowReturn
gst_matroska_mux_write_data(GstMatroskaMux * mux,GstMatroskaPad * collect_pad,GstBuffer * buf)3968 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3969 GstBuffer * buf)
3970 {
3971 GstEbmlWrite *ebml = mux->ebml_write;
3972 GstBuffer *hdr;
3973 guint64 blockgroup;
3974 gboolean write_duration;
3975 guint64 cluster_time_scaled;
3976 gint16 relative_timestamp;
3977 gint64 relative_timestamp64;
3978 guint64 block_duration, duration_diff = 0;
3979 gboolean is_video_keyframe = FALSE;
3980 gboolean is_video_invisible = FALSE;
3981 gboolean is_audio_only = FALSE;
3982 gboolean is_min_duration_reached = FALSE;
3983 gboolean is_max_duration_exceeded = FALSE;
3984 GstMatroskamuxPad *pad;
3985 gint flags = 0;
3986 GstClockTime buffer_timestamp;
3987 GstAudioClippingMeta *cmeta = NULL;
3988
3989 /* write data */
3990 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3991
3992 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3993 if (collect_pad->track->xiph_headers_to_skip > 0) {
3994 --collect_pad->track->xiph_headers_to_skip;
3995 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_HEADER)) {
3996 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3997 gst_buffer_unref (buf);
3998 return GST_FLOW_OK;
3999 }
4000 }
4001
4002 /* for dirac we have to queue up everything up to a picture unit */
4003 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) {
4004 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
4005 if (!buf)
4006 return GST_FLOW_OK;
4007 } else if (!strcmp (collect_pad->track->codec_id,
4008 GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
4009 /* Remove the 'Frame container atom' header' */
4010 buf = gst_buffer_make_writable (buf);
4011 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
4012 }
4013
4014 buffer_timestamp =
4015 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
4016 if (buffer_timestamp >= mux->earliest_time) {
4017 buffer_timestamp -= mux->earliest_time;
4018 } else {
4019 buffer_timestamp = 0;
4020 }
4021
4022 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
4023 * this would wreak havoc with time stored in matroska file */
4024 /* TODO: maybe calculate a timestamp by using the previous timestamp
4025 * and default duration */
4026 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4027 GST_WARNING_OBJECT (collect_pad->collect.pad,
4028 "Invalid buffer timestamp; dropping buffer");
4029 gst_buffer_unref (buf);
4030 return GST_FLOW_OK;
4031 }
4032
4033 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4034 && collect_pad->track->codec_delay) {
4035 /* All timestamps should include the codec delay */
4036 if (buffer_timestamp > collect_pad->track->codec_delay) {
4037 buffer_timestamp += collect_pad->track->codec_delay;
4038 } else {
4039 buffer_timestamp = 0;
4040 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
4041 }
4042 }
4043
4044 /* set the timestamp for outgoing buffers */
4045 ebml->timestamp = buffer_timestamp;
4046
4047 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
4048 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
4049 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
4050 GST_TIME_ARGS (buffer_timestamp));
4051 is_video_keyframe = TRUE;
4052 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
4053 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
4054 || !strcmp (collect_pad->track->codec_id,
4055 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
4056 GST_LOG_OBJECT (mux,
4057 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
4058 GST_TIME_ARGS (buffer_timestamp));
4059 is_video_invisible = TRUE;
4060 }
4061 }
4062
4063 /* From this point on we use the buffer_timestamp to do cluster and other
4064 * related arithmetic, so apply the timestamp offset if we have one */
4065 buffer_timestamp += mux->cluster_timestamp_offset;
4066
4067 is_audio_only = (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
4068 (mux->num_streams == 1);
4069 is_min_duration_reached = (mux->min_cluster_duration == 0
4070 || (buffer_timestamp > mux->cluster_time
4071 && (buffer_timestamp - mux->cluster_time) >=
4072 mux->min_cluster_duration));
4073 is_max_duration_exceeded = (mux->max_cluster_duration > 0
4074 && buffer_timestamp > mux->cluster_time
4075 && (buffer_timestamp - mux->cluster_time) >=
4076 MIN (G_MAXINT16 * mux->time_scale, mux->max_cluster_duration));
4077
4078 if (mux->cluster) {
4079 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
4080 * or when we may be reaching the limit of the relative timestamp */
4081 if (is_max_duration_exceeded || (is_video_keyframe
4082 && is_min_duration_reached) || mux->force_key_unit_event
4083 || (is_audio_only && is_min_duration_reached)) {
4084 if (!mux->ebml_write->streamable)
4085 gst_ebml_write_master_finish (ebml, mux->cluster);
4086
4087 /* Forward the GstForceKeyUnit event after finishing the cluster */
4088 if (mux->force_key_unit_event) {
4089 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
4090 mux->force_key_unit_event = NULL;
4091 }
4092 cluster_time_scaled =
4093 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4094
4095 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
4096 mux->cluster_pos = ebml->pos;
4097 gst_ebml_write_set_cache (ebml, 0x20);
4098 mux->cluster =
4099 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4100 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4101 cluster_time_scaled);
4102 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
4103 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
4104 gst_ebml_write_flush_cache (ebml, is_video_keyframe
4105 || is_audio_only, buffer_timestamp);
4106 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
4107 mux->prev_cluster_size);
4108 /* cluster_time needs to be identical in value to what's stored in the
4109 * matroska so we need to have it with the same precision as what's
4110 * possible with the set timecodescale rather than just using the
4111 * buffer_timestamp.
4112 * If this is not done the rounding of relative_timestamp will be
4113 * incorrect and possibly making the timestamps get out of order if tw
4114 * buffers arrive at the same millisecond (assuming default timecodescale
4115 * of 1ms) */
4116 mux->cluster_time =
4117 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4118 }
4119 } else {
4120 /* first cluster */
4121 cluster_time_scaled =
4122 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4123 mux->cluster_pos = ebml->pos;
4124 gst_ebml_write_set_cache (ebml, 0x20);
4125 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4126 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4127 cluster_time_scaled);
4128 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
4129 /* cluster_time needs to be identical in value to what's stored in the
4130 * matroska so we need to have it with the same precision as what's
4131 * possible with the set timecodescale rather than just using the
4132 * buffer_timestamp.
4133 * If this is not done the rounding of relative_timestamp will be
4134 * incorrect and possibly making the timestamps get out of order if tw
4135 * buffers arrive at the same millisecond (assuming default timecodescale
4136 * of 1ms) */
4137 mux->cluster_time =
4138 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4139 }
4140
4141 /* We currently write index entries for all video tracks or for the audio
4142 * track in a single-track audio file. This could be improved by keeping the
4143 * index only for the *first* video track. */
4144
4145 /* TODO: index is useful for every track, should contain the number of
4146 * the block in the cluster which contains the timestamp, should also work
4147 * for files with multiple audio tracks.
4148 */
4149 if (!mux->ebml_write->streamable && (is_video_keyframe || is_audio_only)) {
4150 gint last_idx = -1;
4151
4152 if (mux->min_index_interval != 0) {
4153 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
4154 if (mux->index[last_idx].track == collect_pad->track->num)
4155 break;
4156 }
4157 }
4158
4159 if (last_idx < 0 || mux->min_index_interval == 0 ||
4160 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
4161 >= mux->min_index_interval)) {
4162 GstMatroskaIndex *idx;
4163
4164 if (mux->num_indexes % 32 == 0) {
4165 mux->index = g_renew (GstMatroskaIndex, mux->index,
4166 mux->num_indexes + 32);
4167 }
4168 idx = &mux->index[mux->num_indexes++];
4169
4170 idx->pos = mux->cluster_pos;
4171 idx->time = buffer_timestamp;
4172 idx->track = collect_pad->track->num;
4173 }
4174 }
4175
4176 /* Check if the duration differs from the default duration. */
4177 write_duration = FALSE;
4178 block_duration = 0;
4179 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
4180 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
4181 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
4182
4183 /* small difference should be ok. */
4184 if (block_duration > collect_pad->default_duration_scaled + 1 ||
4185 block_duration < collect_pad->default_duration_scaled - 1) {
4186 write_duration = TRUE;
4187 }
4188 }
4189
4190 /* write the block, for doctype v2 use SimpleBlock if possible
4191 * one slice (*breath*).
4192 * FIXME: Need to do correct lacing! */
4193 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
4194 if (relative_timestamp64 >= 0) {
4195 /* round the timestamp */
4196 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
4197 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
4198 mux->time_scale);
4199 } else {
4200 /* round the timestamp */
4201 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
4202 relative_timestamp =
4203 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
4204 mux->time_scale));
4205 }
4206
4207 if (is_video_invisible)
4208 flags |= 0x08;
4209
4210 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
4211 cmeta = gst_buffer_get_audio_clipping_meta (buf);
4212 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
4213
4214 /* Start clipping is done via header and CodecDelay */
4215 if (cmeta && !cmeta->end)
4216 cmeta = NULL;
4217 }
4218
4219 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
4220 if (is_video_keyframe)
4221 flags |= 0x80;
4222
4223 hdr =
4224 gst_matroska_mux_create_buffer_header (collect_pad->track,
4225 relative_timestamp, flags);
4226 gst_ebml_write_set_cache (ebml, 0x40);
4227 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
4228 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4229 gst_ebml_write_buffer (ebml, hdr);
4230 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4231 gst_ebml_write_buffer (ebml, buf);
4232
4233 return gst_ebml_last_write_result (ebml);
4234 } else {
4235 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
4236 /* write and call order slightly unnatural,
4237 * but avoids seek and minizes pushing */
4238 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
4239 hdr =
4240 gst_matroska_mux_create_buffer_header (collect_pad->track,
4241 relative_timestamp, flags);
4242 if (write_duration)
4243 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
4244
4245 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4246 && cmeta) {
4247 /* Start clipping is done via header and CodecDelay */
4248 if (cmeta->end) {
4249 guint64 end =
4250 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
4251 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
4252 }
4253 }
4254
4255 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
4256 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4257 gst_ebml_write_buffer (ebml, hdr);
4258 gst_ebml_write_master_finish_full (ebml, blockgroup,
4259 gst_buffer_get_size (buf));
4260 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4261 gst_ebml_write_buffer (ebml, buf);
4262
4263 return gst_ebml_last_write_result (ebml);
4264 }
4265 }
4266
4267 /**
4268 * gst_matroska_mux_handle_buffer:
4269 * @pads: #GstCollectPads
4270 * @uuser_data: #GstMatroskaMux
4271 *
4272 * Collectpads callback.
4273 *
4274 * Returns: #GstFlowReturn
4275 */
4276 static GstFlowReturn
gst_matroska_mux_handle_buffer(GstCollectPads * pads,GstCollectData * data,GstBuffer * buf,gpointer user_data)4277 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
4278 GstBuffer * buf, gpointer user_data)
4279 {
4280 GstClockTime buffer_timestamp;
4281 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
4282 GstEbmlWrite *ebml = mux->ebml_write;
4283 GstMatroskaPad *best = (GstMatroskaPad *) data;
4284 GstFlowReturn ret = GST_FLOW_OK;
4285 GST_DEBUG_OBJECT (mux, "Collected pads");
4286
4287 /* start with a header */
4288 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
4289 if (mux->collect->data == NULL) {
4290 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
4291 ("No input streams configured"));
4292 return GST_FLOW_ERROR;
4293 }
4294 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
4295 gst_ebml_start_streamheader (ebml);
4296 gst_matroska_mux_start (mux, best, buf);
4297 gst_matroska_mux_stop_streamheader (mux);
4298 mux->state = GST_MATROSKA_MUX_STATE_DATA;
4299 }
4300
4301 /* if there is no best pad, we have reached EOS */
4302 if (best == NULL) {
4303 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
4304 if (!mux->ebml_write->streamable) {
4305 gst_matroska_mux_finish (mux);
4306 } else {
4307 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
4308 }
4309 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
4310 ret = GST_FLOW_EOS;
4311 goto exit;
4312 }
4313
4314 if (best->track->codec_id == NULL) {
4315 GST_ERROR_OBJECT (best->collect.pad, "No codec-id for pad");
4316 ret = GST_FLOW_NOT_NEGOTIATED;
4317 goto exit;
4318 }
4319
4320 /* if we have a best stream, should also have a buffer */
4321 g_assert (buf);
4322
4323 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
4324 if (buffer_timestamp >= mux->earliest_time) {
4325 buffer_timestamp -= mux->earliest_time;
4326 } else {
4327 GST_ERROR_OBJECT (mux,
4328 "PTS before first PTS (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
4329 GST_TIME_ARGS (buffer_timestamp), GST_TIME_ARGS (mux->earliest_time));
4330 buffer_timestamp = 0;
4331 }
4332
4333 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
4334 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
4335 GST_TIME_ARGS (buffer_timestamp),
4336 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
4337
4338 /* make note of first and last encountered timestamps, so we can calculate
4339 * the actual duration later when we send an updated header on eos */
4340 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4341 GstClockTime start_ts = buffer_timestamp;
4342 GstClockTime end_ts = start_ts;
4343
4344 if (GST_BUFFER_DURATION_IS_VALID (buf))
4345 end_ts += GST_BUFFER_DURATION (buf);
4346 else if (best->track->default_duration)
4347 end_ts += best->track->default_duration;
4348
4349 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
4350 best->end_ts = end_ts;
4351
4352 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
4353 start_ts < best->start_ts))
4354 best->start_ts = start_ts;
4355 }
4356
4357 /* write one buffer */
4358 ret = gst_matroska_mux_write_data (mux, best, buf);
4359
4360 exit:
4361 return ret;
4362 }
4363
4364
4365 /**
4366 * gst_matroska_mux_change_state:
4367 * @element: #GstMatroskaMux
4368 * @transition: State change transition.
4369 *
4370 * Change the muxer state.
4371 *
4372 * Returns: #GstStateChangeReturn
4373 */
4374 static GstStateChangeReturn
gst_matroska_mux_change_state(GstElement * element,GstStateChange transition)4375 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
4376 {
4377 GstStateChangeReturn ret;
4378 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
4379
4380 switch (transition) {
4381 case GST_STATE_CHANGE_NULL_TO_READY:
4382 break;
4383 case GST_STATE_CHANGE_READY_TO_PAUSED:
4384 gst_collect_pads_start (mux->collect);
4385 break;
4386 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4387 break;
4388 case GST_STATE_CHANGE_PAUSED_TO_READY:
4389 gst_collect_pads_stop (mux->collect);
4390 break;
4391 default:
4392 break;
4393 }
4394
4395 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4396
4397 switch (transition) {
4398 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4399 break;
4400 case GST_STATE_CHANGE_PAUSED_TO_READY:
4401 gst_matroska_mux_reset (GST_ELEMENT (mux));
4402 break;
4403 case GST_STATE_CHANGE_READY_TO_NULL:
4404 break;
4405 default:
4406 break;
4407 }
4408
4409 return ret;
4410 }
4411
4412 static void
gst_matroska_mux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4413 gst_matroska_mux_set_property (GObject * object,
4414 guint prop_id, const GValue * value, GParamSpec * pspec)
4415 {
4416 GstMatroskaMux *mux;
4417
4418 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4419 mux = GST_MATROSKA_MUX (object);
4420
4421 switch (prop_id) {
4422 case PROP_WRITING_APP:
4423 if (!g_value_get_string (value)) {
4424 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
4425 break;
4426 }
4427 g_free (mux->writing_app);
4428 mux->writing_app = g_value_dup_string (value);
4429 break;
4430 case PROP_DOCTYPE_VERSION:
4431 mux->doctype_version = g_value_get_int (value);
4432 break;
4433 case PROP_MIN_INDEX_INTERVAL:
4434 mux->min_index_interval = g_value_get_int64 (value);
4435 break;
4436 case PROP_STREAMABLE:
4437 mux->ebml_write->streamable = g_value_get_boolean (value);
4438 break;
4439 case PROP_TIMECODESCALE:
4440 mux->time_scale = g_value_get_int64 (value);
4441 break;
4442 case PROP_MIN_CLUSTER_DURATION:
4443 mux->min_cluster_duration = g_value_get_int64 (value);
4444 break;
4445 case PROP_MAX_CLUSTER_DURATION:
4446 mux->max_cluster_duration = g_value_get_int64 (value);
4447 break;
4448 case PROP_OFFSET_TO_ZERO:
4449 mux->offset_to_zero = g_value_get_boolean (value);
4450 break;
4451 case PROP_CREATION_TIME:
4452 g_clear_pointer (&mux->creation_time, g_date_time_unref);
4453 mux->creation_time = g_value_dup_boxed (value);
4454 break;
4455 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4456 mux->cluster_timestamp_offset = g_value_get_uint64 (value);
4457 break;
4458 default:
4459 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4460 break;
4461 }
4462 }
4463
4464 static void
gst_matroska_mux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4465 gst_matroska_mux_get_property (GObject * object,
4466 guint prop_id, GValue * value, GParamSpec * pspec)
4467 {
4468 GstMatroskaMux *mux;
4469
4470 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4471 mux = GST_MATROSKA_MUX (object);
4472
4473 switch (prop_id) {
4474 case PROP_WRITING_APP:
4475 g_value_set_string (value, mux->writing_app);
4476 break;
4477 case PROP_DOCTYPE_VERSION:
4478 g_value_set_int (value, mux->doctype_version);
4479 break;
4480 case PROP_MIN_INDEX_INTERVAL:
4481 g_value_set_int64 (value, mux->min_index_interval);
4482 break;
4483 case PROP_STREAMABLE:
4484 g_value_set_boolean (value, mux->ebml_write->streamable);
4485 break;
4486 case PROP_TIMECODESCALE:
4487 g_value_set_int64 (value, mux->time_scale);
4488 break;
4489 case PROP_MIN_CLUSTER_DURATION:
4490 g_value_set_int64 (value, mux->min_cluster_duration);
4491 break;
4492 case PROP_MAX_CLUSTER_DURATION:
4493 g_value_set_int64 (value, mux->max_cluster_duration);
4494 break;
4495 case PROP_OFFSET_TO_ZERO:
4496 g_value_set_boolean (value, mux->offset_to_zero);
4497 break;
4498 case PROP_CREATION_TIME:
4499 g_value_set_boxed (value, mux->creation_time);
4500 break;
4501 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4502 g_value_set_uint64 (value, mux->cluster_timestamp_offset);
4503 break;
4504 default:
4505 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4506 break;
4507 }
4508 }
4509