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