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