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