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