matroskamux: Add support for E-AC3
[platform/upstream/gst-plugins-good.git] / 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  *
6  * matroska-mux.c: matroska file/stream muxer
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /* TODO: - check everywhere that we don't write invalid values
25  *       - make sure timestamps are correctly scaled everywhere
26  */
27
28 /**
29  * SECTION:element-matroskamux
30  *
31  * matroskamux muxes different input streams into a Matroska file.
32  *
33  * <refsect2>
34  * <title>Example launch line</title>
35  * |[
36  * gst-launch -v filesrc location=/path/to/mp3 ! mp3parse ! matroskamux name=mux ! filesink location=test.mkv  filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
37  * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
38  * |[
39  * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
40  * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
41  * </refsect2>
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include <math.h>
49 #include <string.h>
50
51 #include <gst/riff/riff-media.h>
52 #include <gst/tag/tag.h>
53
54 #include "matroska-mux.h"
55 #include "matroska-ids.h"
56
57 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
58 #define GST_CAT_DEFAULT matroskamux_debug
59
60 enum
61 {
62   ARG_0,
63   ARG_WRITING_APP,
64   ARG_DOCTYPE_VERSION,
65   ARG_MIN_INDEX_INTERVAL,
66   ARG_STREAMABLE
67 };
68
69 #define  DEFAULT_DOCTYPE_VERSION         2
70 #define  DEFAULT_WRITING_APP             "GStreamer Matroska muxer"
71 #define  DEFAULT_MIN_INDEX_INTERVAL      0
72 #define  DEFAULT_STREAMABLE              FALSE
73
74 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
75 #define WAVEFORMATEX_SIZE  (2 + sizeof (gst_riff_strf_auds))
76
77 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
78     GST_PAD_SRC,
79     GST_PAD_ALWAYS,
80     GST_STATIC_CAPS ("video/x-matroska")
81     );
82
83 #define COMMON_VIDEO_CAPS \
84   "width = (int) [ 16, 4096 ], " \
85   "height = (int) [ 16, 4096 ], " \
86   "framerate = (fraction) [ 0, MAX ]"
87
88 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
89   "width = (int) [ 16, 4096 ], " \
90   "height = (int) [ 16, 4096 ] "
91
92 /* FIXME: 
93  * * require codec data, etc as needed
94  */
95
96 static GstStaticPadTemplate videosink_templ =
97     GST_STATIC_PAD_TEMPLATE ("video_%d",
98     GST_PAD_SINK,
99     GST_PAD_REQUEST,
100     GST_STATIC_CAPS ("video/mpeg, "
101         "mpegversion = (int) { 1, 2, 4 }, "
102         "systemstream = (boolean) false, "
103         COMMON_VIDEO_CAPS "; "
104         "video/x-h264, "
105         COMMON_VIDEO_CAPS "; "
106         "video/x-divx, "
107         COMMON_VIDEO_CAPS "; "
108         "video/x-xvid, "
109         COMMON_VIDEO_CAPS "; "
110         "video/x-huffyuv, "
111         COMMON_VIDEO_CAPS "; "
112         "video/x-dv, "
113         COMMON_VIDEO_CAPS "; "
114         "video/x-h263, "
115         COMMON_VIDEO_CAPS "; "
116         "video/x-msmpeg, "
117         COMMON_VIDEO_CAPS "; "
118         "image/jpeg, "
119         COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
120         "video/x-theora; "
121         "video/x-dirac, "
122         COMMON_VIDEO_CAPS "; "
123         "video/x-pn-realvideo, "
124         "rmversion = (int) [1, 4], "
125         COMMON_VIDEO_CAPS "; "
126         "video/x-vp8, "
127         COMMON_VIDEO_CAPS "; "
128         "video/x-raw-yuv, "
129         "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
130         COMMON_VIDEO_CAPS "; "
131         "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
132     );
133
134 #define COMMON_AUDIO_CAPS \
135   "channels = (int) [ 1, MAX ], " \
136   "rate = (int) [ 1, MAX ]"
137
138 /* FIXME:
139  * * require codec data, etc as needed
140  */
141 static GstStaticPadTemplate audiosink_templ =
142     GST_STATIC_PAD_TEMPLATE ("audio_%d",
143     GST_PAD_SINK,
144     GST_PAD_REQUEST,
145     GST_STATIC_CAPS ("audio/mpeg, "
146         "mpegversion = (int) 1, "
147         "layer = (int) [ 1, 3 ], "
148         "stream-format = (string) { raw }, "
149         COMMON_AUDIO_CAPS "; "
150         "audio/mpeg, "
151         "mpegversion = (int) { 2, 4 }, "
152         COMMON_AUDIO_CAPS "; "
153         "audio/x-ac3, "
154         COMMON_AUDIO_CAPS "; "
155         "audio/x-eac3, "
156         COMMON_AUDIO_CAPS "; "
157         "audio/x-dts, "
158         COMMON_AUDIO_CAPS "; "
159         "audio/x-vorbis, "
160         COMMON_AUDIO_CAPS "; "
161         "audio/x-flac, "
162         COMMON_AUDIO_CAPS "; "
163         "audio/x-speex, "
164         COMMON_AUDIO_CAPS "; "
165         "audio/x-raw-int, "
166         "width = (int) 8, "
167         "depth = (int) 8, "
168         "signed = (boolean) false, "
169         COMMON_AUDIO_CAPS ";"
170         "audio/x-raw-int, "
171         "width = (int) 16, "
172         "depth = (int) 16, "
173         "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
174         "signed = (boolean) true, "
175         COMMON_AUDIO_CAPS ";"
176         "audio/x-raw-int, "
177         "width = (int) 24, "
178         "depth = (int) 24, "
179         "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
180         "signed = (boolean) true, "
181         COMMON_AUDIO_CAPS ";"
182         "audio/x-raw-int, "
183         "width = (int) 32, "
184         "depth = (int) 32, "
185         "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
186         "signed = (boolean) true, "
187         COMMON_AUDIO_CAPS ";"
188         "audio/x-raw-float, "
189         "width = (int) [ 32, 64 ], "
190         "endianness = (int) LITTLE_ENDIAN, "
191         COMMON_AUDIO_CAPS ";"
192         "audio/x-tta, "
193         "width = (int) { 8, 16, 24 }, "
194         "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
195         "audio/x-pn-realaudio, "
196         "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
197         "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
198         "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
199         COMMON_AUDIO_CAPS)
200     );
201
202 static GstStaticPadTemplate subtitlesink_templ =
203 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
204     GST_PAD_SINK,
205     GST_PAD_REQUEST,
206     GST_STATIC_CAPS_ANY);
207
208 static GArray *used_uids;
209 G_LOCK_DEFINE_STATIC (used_uids);
210
211 static void gst_matroska_mux_add_interfaces (GType type);
212
213 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
214     GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
215
216 /* Matroska muxer destructor */
217 static void gst_matroska_mux_finalize (GObject * object);
218
219 /* Pads collected callback */
220 static GstFlowReturn
221 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
222
223 /* pad functions */
224 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
225     GstEvent * event);
226 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
227     GstPadTemplate * templ, const gchar * name);
228 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
229
230 /* gst internal change state handler */
231 static GstStateChangeReturn
232 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
233
234 /* gobject bla bla */
235 static void gst_matroska_mux_set_property (GObject * object,
236     guint prop_id, const GValue * value, GParamSpec * pspec);
237 static void gst_matroska_mux_get_property (GObject * object,
238     guint prop_id, GValue * value, GParamSpec * pspec);
239
240 /* reset muxer */
241 static void gst_matroska_mux_reset (GstElement * element);
242
243 /* uid generation */
244 static guint64 gst_matroska_mux_create_uid ();
245
246 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
247     GstMatroskaTrackContext * context);
248 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
249     GstMatroskaTrackContext * context);
250 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
251     GstMatroskaTrackContext * context);
252 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
253     GstMatroskaTrackContext * context);
254 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
255     GstMatroskaTrackContext * context);
256
257 static void
258 gst_matroska_mux_add_interfaces (GType type)
259 {
260   static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
261
262   g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
263 }
264
265 static void
266 gst_matroska_mux_base_init (gpointer g_class)
267 {
268 }
269
270 static void
271 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
272 {
273   GObjectClass *gobject_class;
274   GstElementClass *gstelement_class;
275
276   gobject_class = (GObjectClass *) klass;
277   gstelement_class = (GstElementClass *) klass;
278
279   gst_element_class_add_pad_template (gstelement_class,
280       gst_static_pad_template_get (&videosink_templ));
281   gst_element_class_add_pad_template (gstelement_class,
282       gst_static_pad_template_get (&audiosink_templ));
283   gst_element_class_add_pad_template (gstelement_class,
284       gst_static_pad_template_get (&subtitlesink_templ));
285   gst_element_class_add_pad_template (gstelement_class,
286       gst_static_pad_template_get (&src_templ));
287   gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
288       "Codec/Muxer",
289       "Muxes video/audio/subtitle streams into a matroska stream",
290       "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
291
292   GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
293       "Matroska muxer");
294
295   gobject_class->finalize = gst_matroska_mux_finalize;
296
297   gobject_class->get_property = gst_matroska_mux_get_property;
298   gobject_class->set_property = gst_matroska_mux_set_property;
299
300   g_object_class_install_property (gobject_class, ARG_WRITING_APP,
301       g_param_spec_string ("writing-app", "Writing application.",
302           "The name the application that creates the matroska file.",
303           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
304   g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
305       g_param_spec_int ("version", "DocType version",
306           "This parameter determines what Matroska features can be used.",
307           1, 2, DEFAULT_DOCTYPE_VERSION,
308           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
309   g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
310       g_param_spec_int64 ("min-index-interval", "Minimum time between index "
311           "entries", "An index entry is created every so many nanoseconds.",
312           0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
313           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314   g_object_class_install_property (gobject_class, ARG_STREAMABLE,
315       g_param_spec_boolean ("streamable", "Determines whether output should "
316           "be streamable", "If set to true, the output should be as if it is "
317           "to be streamed and hence no indexes written or duration written.",
318           DEFAULT_STREAMABLE,
319           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
320
321   gstelement_class->change_state =
322       GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
323   gstelement_class->request_new_pad =
324       GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
325   gstelement_class->release_pad =
326       GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
327 }
328
329
330 /**
331  * gst_matroska_mux_init:
332  * @mux: #GstMatroskaMux that should be initialized.
333  * @g_class: Class of the muxer.
334  *
335  * Matroska muxer constructor.
336  */
337 static void
338 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
339 {
340   GstPadTemplate *templ;
341
342   templ =
343       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
344   mux->srcpad = gst_pad_new_from_template (templ, "src");
345
346   gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
347   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
348
349   mux->collect = gst_collect_pads_new ();
350   gst_collect_pads_set_function (mux->collect,
351       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
352       mux);
353
354   mux->ebml_write = gst_ebml_write_new (mux->srcpad);
355   mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
356
357   /* property defaults */
358   mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
359   mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
360   mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
361   mux->streamable = DEFAULT_STREAMABLE;
362
363   /* initialize internal variables */
364   mux->index = NULL;
365   mux->num_streams = 0;
366   mux->num_a_streams = 0;
367   mux->num_t_streams = 0;
368   mux->num_v_streams = 0;
369
370   /* initialize remaining variables */
371   gst_matroska_mux_reset (GST_ELEMENT (mux));
372 }
373
374
375 /**
376  * gst_matroska_mux_finalize:
377  * @object: #GstMatroskaMux that should be finalized.
378  *
379  * Finalize matroska muxer.
380  */
381 static void
382 gst_matroska_mux_finalize (GObject * object)
383 {
384   GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
385
386   gst_object_unref (mux->collect);
387   gst_object_unref (mux->ebml_write);
388   if (mux->writing_app)
389     g_free (mux->writing_app);
390
391   G_OBJECT_CLASS (parent_class)->finalize (object);
392 }
393
394
395 /**
396  * gst_matroska_mux_create_uid:
397  *
398  * Generate new unused track UID.
399  *
400  * Returns: New track UID.
401  */
402 static guint64
403 gst_matroska_mux_create_uid (void)
404 {
405   guint64 uid = 0;
406
407   G_LOCK (used_uids);
408
409   if (!used_uids)
410     used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
411
412   while (!uid) {
413     guint i;
414
415     uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
416     for (i = 0; i < used_uids->len; i++) {
417       if (g_array_index (used_uids, guint64, i) == uid) {
418         uid = 0;
419         break;
420       }
421     }
422     g_array_append_val (used_uids, uid);
423   }
424
425   G_UNLOCK (used_uids);
426   return uid;
427 }
428
429
430 /**
431  * gst_matroska_pad_reset:
432  * @collect_pad: the #GstMatroskaPad
433  *
434  * Reset and/or release resources of a matroska collect pad.
435  */
436 static void
437 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
438 {
439   gchar *name = NULL;
440   GstMatroskaTrackType type = 0;
441
442   /* free track information */
443   if (collect_pad->track != NULL) {
444     /* retrieve for optional later use */
445     name = collect_pad->track->name;
446     type = collect_pad->track->type;
447     /* extra for video */
448     if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
449       GstMatroskaTrackVideoContext *ctx =
450           (GstMatroskaTrackVideoContext *) collect_pad->track;
451
452       if (ctx->dirac_unit) {
453         gst_buffer_unref (ctx->dirac_unit);
454         ctx->dirac_unit = NULL;
455       }
456     }
457     g_free (collect_pad->track->codec_id);
458     g_free (collect_pad->track->codec_name);
459     if (full)
460       g_free (collect_pad->track->name);
461     g_free (collect_pad->track->language);
462     g_free (collect_pad->track->codec_priv);
463     g_free (collect_pad->track);
464     collect_pad->track = NULL;
465   }
466
467   /* free cached buffer */
468   if (collect_pad->buffer != NULL) {
469     gst_buffer_unref (collect_pad->buffer);
470     collect_pad->buffer = NULL;
471   }
472
473   if (!full && type != 0) {
474     GstMatroskaTrackContext *context;
475
476     /* create a fresh context */
477     switch (type) {
478       case GST_MATROSKA_TRACK_TYPE_VIDEO:
479         context = (GstMatroskaTrackContext *)
480             g_new0 (GstMatroskaTrackVideoContext, 1);
481         break;
482       case GST_MATROSKA_TRACK_TYPE_AUDIO:
483         context = (GstMatroskaTrackContext *)
484             g_new0 (GstMatroskaTrackAudioContext, 1);
485         break;
486       case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
487         context = (GstMatroskaTrackContext *)
488             g_new0 (GstMatroskaTrackSubtitleContext, 1);
489         break;
490       default:
491         g_assert_not_reached ();
492         break;
493     }
494
495     context->type = type;
496     context->name = name;
497     /* TODO: check default values for the context */
498     context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
499     collect_pad->track = context;
500     collect_pad->buffer = NULL;
501     collect_pad->duration = 0;
502     collect_pad->start_ts = GST_CLOCK_TIME_NONE;
503     collect_pad->end_ts = GST_CLOCK_TIME_NONE;
504   }
505 }
506
507 /**
508  * gst_matroska_pad_free:
509  * @collect_pad: the #GstMatroskaPad
510  *
511  * Release resources of a matroska collect pad.
512  */
513 static void
514 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
515 {
516   gst_matroska_pad_reset (collect_pad, TRUE);
517 }
518
519
520 /**
521  * gst_matroska_mux_reset:
522  * @element: #GstMatroskaMux that should be reseted.
523  *
524  * Reset matroska muxer back to initial state.
525  */
526 static void
527 gst_matroska_mux_reset (GstElement * element)
528 {
529   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
530   GSList *walk;
531
532   /* reset EBML write */
533   gst_ebml_write_reset (mux->ebml_write);
534
535   /* reset input */
536   mux->state = GST_MATROSKA_MUX_STATE_START;
537
538   /* clean up existing streams */
539
540   for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
541     GstMatroskaPad *collect_pad;
542
543     collect_pad = (GstMatroskaPad *) walk->data;
544
545     /* reset collect pad to pristine state */
546     gst_matroska_pad_reset (collect_pad, FALSE);
547   }
548
549   /* reset indexes */
550   mux->num_indexes = 0;
551   g_free (mux->index);
552   mux->index = NULL;
553
554   /* reset timers */
555   mux->time_scale = GST_MSECOND;
556   mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
557   mux->duration = 0;
558
559   /* reset cluster */
560   mux->cluster = 0;
561   mux->cluster_time = 0;
562   mux->cluster_pos = 0;
563   mux->prev_cluster_size = 0;
564
565   /* reset tags */
566   gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
567 }
568
569 /**
570  * gst_matroska_mux_handle_src_event:
571  * @pad: Pad which received the event.
572  * @event: Received event.
573  *
574  * handle events - copied from oggmux without understanding 
575  *
576  * Returns: #TRUE on success.
577  */
578 static gboolean
579 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
580 {
581   GstEventType type;
582
583   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
584
585   switch (type) {
586     case GST_EVENT_SEEK:
587       /* disable seeking for now */
588       return FALSE;
589     default:
590       break;
591   }
592
593   return gst_pad_event_default (pad, event);
594 }
595
596 /**
597  * gst_matroska_mux_handle_sink_event:
598  * @pad: Pad which received the event.
599  * @event: Received event.
600  *
601  * handle events - informational ones like tags
602  *
603  * Returns: #TRUE on success.
604  */
605 static gboolean
606 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
607 {
608   GstMatroskaTrackContext *context;
609   GstMatroskaPad *collect_pad;
610   GstMatroskaMux *mux;
611   GstTagList *list;
612   gboolean ret = TRUE;
613
614   mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
615
616   switch (GST_EVENT_TYPE (event)) {
617     case GST_EVENT_TAG:{
618       gchar *lang = NULL;
619
620       GST_DEBUG_OBJECT (mux, "received tag event");
621       gst_event_parse_tag (event, &list);
622
623       collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
624       g_assert (collect_pad);
625       context = collect_pad->track;
626       g_assert (context);
627
628       /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
629       if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
630         const gchar *lang_code;
631
632         lang_code = gst_tag_get_language_code_iso_639_2B (lang);
633         if (lang_code) {
634           GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
635           context->language = g_strdup (lang_code);
636         } else {
637           GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
638         }
639         g_free (lang);
640       }
641
642       /* FIXME: what about stream-specific tags? */
643       gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
644           gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
645
646       /* handled this, don't want collectpads to forward it downstream */
647       ret = FALSE;
648       gst_event_unref (event);
649       break;
650     }
651     case GST_EVENT_NEWSEGMENT:
652       /* We don't support NEWSEGMENT events */
653       ret = FALSE;
654       gst_event_unref (event);
655       break;
656     default:
657       break;
658   }
659
660   /* now GstCollectPads can take care of the rest, e.g. EOS */
661   if (ret)
662     ret = mux->collect_event (pad, event);
663
664   gst_object_unref (mux);
665
666   return ret;
667 }
668
669
670 /**
671  * gst_matroska_mux_video_pad_setcaps:
672  * @pad: Pad which got the caps.
673  * @caps: New caps.
674  *
675  * Setcaps function for video sink pad.
676  *
677  * Returns: #TRUE on success.
678  */
679 static gboolean
680 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
681 {
682   GstMatroskaTrackContext *context = NULL;
683   GstMatroskaTrackVideoContext *videocontext;
684   GstMatroskaMux *mux;
685   GstMatroskaPad *collect_pad;
686   GstStructure *structure;
687   const gchar *mimetype;
688   const GValue *value = NULL;
689   const GstBuffer *codec_buf = NULL;
690   gint width, height, pixel_width, pixel_height;
691   gint fps_d, fps_n;
692   gboolean interlaced = FALSE;
693
694   mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
695
696   /* find context */
697   collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
698   g_assert (collect_pad);
699   context = collect_pad->track;
700   g_assert (context);
701   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
702   videocontext = (GstMatroskaTrackVideoContext *) context;
703
704   /* gst -> matroska ID'ing */
705   structure = gst_caps_get_structure (caps, 0);
706
707   mimetype = gst_structure_get_name (structure);
708
709   if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
710       && interlaced)
711     context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
712
713   if (!strcmp (mimetype, "video/x-theora")) {
714     /* we'll extract the details later from the theora identification header */
715     goto skip_details;
716   }
717
718   /* get general properties */
719   /* spec says it is mandatory */
720   if (!gst_structure_get_int (structure, "width", &width) ||
721       !gst_structure_get_int (structure, "height", &height))
722     goto refuse_caps;
723
724   videocontext->pixel_width = width;
725   videocontext->pixel_height = height;
726   if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
727       && fps_n > 0) {
728     context->default_duration =
729         gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
730     GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
731         GST_TIME_ARGS (context->default_duration));
732   } else {
733     context->default_duration = 0;
734   }
735   if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
736           &pixel_width, &pixel_height)) {
737     if (pixel_width > pixel_height) {
738       videocontext->display_width = width * pixel_width / pixel_height;
739       videocontext->display_height = height;
740     } else if (pixel_width < pixel_height) {
741       videocontext->display_width = width;
742       videocontext->display_height = height * pixel_height / pixel_width;
743     } else {
744       videocontext->display_width = 0;
745       videocontext->display_height = 0;
746     }
747   } else {
748     videocontext->display_width = 0;
749     videocontext->display_height = 0;
750   }
751
752 skip_details:
753
754   videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
755   videocontext->fourcc = 0;
756
757   /* TODO: - check if we handle all codecs by the spec, i.e. codec private
758    *         data and other settings
759    *       - add new formats
760    */
761
762   /* extract codec_data, may turn out needed */
763   value = gst_structure_get_value (structure, "codec_data");
764   if (value)
765     codec_buf = gst_value_get_buffer (value);
766
767   /* find type */
768   if (!strcmp (mimetype, "video/x-raw-yuv")) {
769     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
770     gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
771   } else if (!strcmp (mimetype, "image/jpeg")) {
772     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
773   } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
774       ||!strcmp (mimetype, "video/x-huffyuv")
775       || !strcmp (mimetype, "video/x-divx")
776       || !strcmp (mimetype, "video/x-dv")
777       || !strcmp (mimetype, "video/x-h263")
778       || !strcmp (mimetype, "video/x-msmpeg")
779       || !strcmp (mimetype, "video/x-wmv")) {
780     gst_riff_strf_vids *bih;
781     gint size = sizeof (gst_riff_strf_vids);
782     guint32 fourcc = 0;
783
784     if (!strcmp (mimetype, "video/x-xvid"))
785       fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
786     else if (!strcmp (mimetype, "video/x-huffyuv"))
787       fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
788     else if (!strcmp (mimetype, "video/x-dv"))
789       fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
790     else if (!strcmp (mimetype, "video/x-h263"))
791       fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
792     else if (!strcmp (mimetype, "video/x-divx")) {
793       gint divxversion;
794
795       gst_structure_get_int (structure, "divxversion", &divxversion);
796       switch (divxversion) {
797         case 3:
798           fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
799           break;
800         case 4:
801           fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
802           break;
803         case 5:
804           fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
805           break;
806       }
807     } else if (!strcmp (mimetype, "video/x-msmpeg")) {
808       gint msmpegversion;
809
810       gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
811       switch (msmpegversion) {
812         case 41:
813           fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
814           break;
815         case 42:
816           fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
817           break;
818         case 43:
819           goto msmpeg43;
820           break;
821       }
822     } else if (!strcmp (mimetype, "video/x-wmv")) {
823       gint wmvversion;
824       guint32 format;
825       if (gst_structure_get_fourcc (structure, "format", &format)) {
826         fourcc = format;
827       } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
828         if (wmvversion == 2) {
829           fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
830         } else if (wmvversion == 1) {
831           fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
832         } else if (wmvversion == 3) {
833           fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
834         }
835       }
836     }
837
838     if (!fourcc)
839       goto refuse_caps;
840
841     bih = g_new0 (gst_riff_strf_vids, 1);
842     GST_WRITE_UINT32_LE (&bih->size, size);
843     GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
844     GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
845     GST_WRITE_UINT32_LE (&bih->compression, fourcc);
846     GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
847     GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
848     GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
849         videocontext->pixel_height * 3);
850
851     /* process codec private/initialization data, if any */
852     if (codec_buf) {
853       size += GST_BUFFER_SIZE (codec_buf);
854       bih = g_realloc (bih, size);
855       GST_WRITE_UINT32_LE (&bih->size, size);
856       memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
857           GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
858     }
859
860     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
861     context->codec_priv = (gpointer) bih;
862     context->codec_priv_size = size;
863   } else if (!strcmp (mimetype, "video/x-h264")) {
864     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
865
866     if (context->codec_priv != NULL) {
867       g_free (context->codec_priv);
868       context->codec_priv = NULL;
869       context->codec_priv_size = 0;
870     }
871
872     /* Create avcC header */
873     if (codec_buf != NULL) {
874       context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
875       context->codec_priv = g_malloc0 (context->codec_priv_size);
876       memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
877           context->codec_priv_size);
878     }
879   } else if (!strcmp (mimetype, "video/x-theora")) {
880     const GValue *streamheader;
881
882     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
883
884     if (context->codec_priv != NULL) {
885       g_free (context->codec_priv);
886       context->codec_priv = NULL;
887       context->codec_priv_size = 0;
888     }
889
890     streamheader = gst_structure_get_value (structure, "streamheader");
891     if (!theora_streamheader_to_codecdata (streamheader, context)) {
892       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
893           ("theora stream headers missing or malformed"));
894       goto refuse_caps;
895     }
896   } else if (!strcmp (mimetype, "video/x-dirac")) {
897     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
898   } else if (!strcmp (mimetype, "video/x-vp8")) {
899     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
900   } else if (!strcmp (mimetype, "video/mpeg")) {
901     gint mpegversion;
902
903     gst_structure_get_int (structure, "mpegversion", &mpegversion);
904     switch (mpegversion) {
905       case 1:
906         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
907         break;
908       case 2:
909         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
910         break;
911       case 4:
912         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
913         break;
914       default:
915         goto refuse_caps;
916     }
917
918     /* global headers may be in codec data */
919     if (codec_buf != NULL) {
920       context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
921       context->codec_priv = g_malloc0 (context->codec_priv_size);
922       memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
923           context->codec_priv_size);
924     }
925   } else if (!strcmp (mimetype, "video/x-msmpeg")) {
926   msmpeg43:
927     /* can only make it here if preceding case verified it was version 3 */
928     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
929   } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
930     gint rmversion;
931     const GValue *mdpr_data;
932
933     gst_structure_get_int (structure, "rmversion", &rmversion);
934     switch (rmversion) {
935       case 1:
936         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
937         break;
938       case 2:
939         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
940         break;
941       case 3:
942         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
943         break;
944       case 4:
945         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
946         break;
947       default:
948         goto refuse_caps;
949     }
950
951     mdpr_data = gst_structure_get_value (structure, "mdpr_data");
952     if (mdpr_data != NULL) {
953       guint8 *priv_data = NULL;
954       guint priv_data_size = 0;
955
956       GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
957
958       priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
959       priv_data = g_malloc0 (priv_data_size);
960
961       memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
962
963       context->codec_priv = priv_data;
964       context->codec_priv_size = priv_data_size;
965     }
966   }
967
968   return TRUE;
969
970   /* ERRORS */
971 refuse_caps:
972   {
973     GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
974         GST_PAD_NAME (pad), caps);
975     return FALSE;
976   }
977 }
978
979 /* N > 0 to expect a particular number of headers, negative if the
980    number of headers is variable */
981 static gboolean
982 xiphN_streamheader_to_codecdata (const GValue * streamheader,
983     GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
984 {
985   GstBuffer **buf = NULL;
986   GArray *bufarr;
987   guint8 *priv_data;
988   guint bufi, i, offset, priv_data_size;
989
990   if (streamheader == NULL)
991     goto no_stream_headers;
992
993   if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
994     goto wrong_type;
995
996   bufarr = g_value_peek_pointer (streamheader);
997   if (bufarr->len <= 0 || bufarr->len > 255)    /* at least one header, and count stored in a byte */
998     goto wrong_count;
999   if (N > 0 && bufarr->len != N)
1000     goto wrong_count;
1001
1002   context->xiph_headers_to_skip = bufarr->len;
1003
1004   buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1005   for (i = 0; i < bufarr->len; i++) {
1006     GValue *bufval = &g_array_index (bufarr, GValue, i);
1007
1008     if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1009       g_free (buf);
1010       goto wrong_content_type;
1011     }
1012
1013     buf[i] = g_value_peek_pointer (bufval);
1014   }
1015
1016   priv_data_size = 1;
1017   if (bufarr->len > 0) {
1018     for (i = 0; i < bufarr->len - 1; i++) {
1019       priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1020     }
1021   }
1022
1023   for (i = 0; i < bufarr->len; ++i) {
1024     priv_data_size += GST_BUFFER_SIZE (buf[i]);
1025   }
1026
1027   priv_data = g_malloc0 (priv_data_size);
1028
1029   priv_data[0] = bufarr->len - 1;
1030   offset = 1;
1031
1032   if (bufarr->len > 0) {
1033     for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1034       for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1035         priv_data[offset++] = 0xff;
1036       }
1037       priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1038     }
1039   }
1040
1041   for (i = 0; i < bufarr->len; ++i) {
1042     memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1043         GST_BUFFER_SIZE (buf[i]));
1044     offset += GST_BUFFER_SIZE (buf[i]);
1045   }
1046
1047   context->codec_priv = priv_data;
1048   context->codec_priv_size = priv_data_size;
1049
1050   if (p_buf0)
1051     *p_buf0 = gst_buffer_ref (buf[0]);
1052
1053   g_free (buf);
1054
1055   return TRUE;
1056
1057 /* ERRORS */
1058 no_stream_headers:
1059   {
1060     GST_WARNING ("required streamheaders missing in sink caps!");
1061     return FALSE;
1062   }
1063 wrong_type:
1064   {
1065     GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1066         G_VALUE_TYPE_NAME (streamheader));
1067     return FALSE;
1068   }
1069 wrong_count:
1070   {
1071     GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1072     return FALSE;
1073   }
1074 wrong_content_type:
1075   {
1076     GST_WARNING ("streamheaders array does not contain GstBuffers");
1077     return FALSE;
1078   }
1079 }
1080
1081 static gboolean
1082 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1083     GstMatroskaTrackContext * context)
1084 {
1085   GstBuffer *buf0 = NULL;
1086
1087   if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1088     return FALSE;
1089
1090   if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1091     GST_WARNING ("First vorbis header too small, ignoring");
1092   } else {
1093     if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1094       GstMatroskaTrackAudioContext *audiocontext;
1095       guint8 *hdr;
1096
1097       hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1098       audiocontext = (GstMatroskaTrackAudioContext *) context;
1099       audiocontext->channels = GST_READ_UINT8 (hdr);
1100       audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1101     }
1102   }
1103
1104   if (buf0)
1105     gst_buffer_unref (buf0);
1106
1107   return TRUE;
1108 }
1109
1110 static gboolean
1111 theora_streamheader_to_codecdata (const GValue * streamheader,
1112     GstMatroskaTrackContext * context)
1113 {
1114   GstBuffer *buf0 = NULL;
1115
1116   if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1117     return FALSE;
1118
1119   if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1120     GST_WARNING ("First theora header too small, ignoring");
1121   } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1122     GST_WARNING ("First header not a theora identification header, ignoring");
1123   } else {
1124     GstMatroskaTrackVideoContext *videocontext;
1125     guint fps_num, fps_denom, par_num, par_denom;
1126     guint8 *hdr;
1127
1128     hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1129
1130     videocontext = (GstMatroskaTrackVideoContext *) context;
1131     videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1132     videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1133     hdr += 3 + 3 + 1 + 1;
1134     fps_num = GST_READ_UINT32_BE (hdr);
1135     fps_denom = GST_READ_UINT32_BE (hdr + 4);
1136     context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1137         fps_denom, fps_num);
1138     hdr += 4 + 4;
1139     par_num = GST_READ_UINT32_BE (hdr) >> 8;
1140     par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1141     if (par_num > 0 && par_num > 0) {
1142       if (par_num > par_denom) {
1143         videocontext->display_width =
1144             videocontext->pixel_width * par_num / par_denom;
1145         videocontext->display_height = videocontext->pixel_height;
1146       } else if (par_num < par_denom) {
1147         videocontext->display_width = videocontext->pixel_width;
1148         videocontext->display_height =
1149             videocontext->pixel_height * par_denom / par_num;
1150       } else {
1151         videocontext->display_width = 0;
1152         videocontext->display_height = 0;
1153       }
1154     } else {
1155       videocontext->display_width = 0;
1156       videocontext->display_height = 0;
1157     }
1158     hdr += 3 + 3;
1159   }
1160
1161   if (buf0)
1162     gst_buffer_unref (buf0);
1163
1164   return TRUE;
1165 }
1166
1167 static gboolean
1168 kate_streamheader_to_codecdata (const GValue * streamheader,
1169     GstMatroskaTrackContext * context)
1170 {
1171   GstBuffer *buf0 = NULL;
1172
1173   if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1174     return FALSE;
1175
1176   if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) {    /* Kate ID header is 64 bytes */
1177     GST_WARNING ("First kate header too small, ignoring");
1178   } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1179     GST_WARNING ("First header not a kate identification header, ignoring");
1180   }
1181
1182   if (buf0)
1183     gst_buffer_unref (buf0);
1184
1185   return TRUE;
1186 }
1187
1188 static gboolean
1189 flac_streamheader_to_codecdata (const GValue * streamheader,
1190     GstMatroskaTrackContext * context)
1191 {
1192   GArray *bufarr;
1193   gint i;
1194   GValue *bufval;
1195   GstBuffer *buffer;
1196
1197   if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1198     GST_WARNING ("No or invalid streamheader field in the caps");
1199     return FALSE;
1200   }
1201
1202   bufarr = g_value_peek_pointer (streamheader);
1203   if (bufarr->len < 2) {
1204     GST_WARNING ("Too few headers in streamheader field");
1205     return FALSE;
1206   }
1207
1208   context->xiph_headers_to_skip = bufarr->len + 1;
1209
1210   bufval = &g_array_index (bufarr, GValue, 0);
1211   if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1212     GST_WARNING ("streamheaders array does not contain GstBuffers");
1213     return FALSE;
1214   }
1215
1216   buffer = g_value_peek_pointer (bufval);
1217
1218   /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1219   if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1220       || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1221       || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1222     GST_WARNING ("Invalid streamheader for FLAC");
1223     return FALSE;
1224   }
1225
1226   context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1227   context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1228   memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1229       GST_BUFFER_SIZE (buffer) - 9);
1230
1231   for (i = 1; i < bufarr->len; i++) {
1232     bufval = &g_array_index (bufarr, GValue, i);
1233
1234     if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1235       g_free (context->codec_priv);
1236       context->codec_priv = NULL;
1237       context->codec_priv_size = 0;
1238       GST_WARNING ("streamheaders array does not contain GstBuffers");
1239       return FALSE;
1240     }
1241
1242     buffer = g_value_peek_pointer (bufval);
1243
1244     context->codec_priv =
1245         g_realloc (context->codec_priv,
1246         context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1247     memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1248         GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1249     context->codec_priv_size =
1250         context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1251   }
1252
1253   return TRUE;
1254 }
1255
1256 static gboolean
1257 speex_streamheader_to_codecdata (const GValue * streamheader,
1258     GstMatroskaTrackContext * context)
1259 {
1260   GArray *bufarr;
1261   GValue *bufval;
1262   GstBuffer *buffer;
1263
1264   if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1265     GST_WARNING ("No or invalid streamheader field in the caps");
1266     return FALSE;
1267   }
1268
1269   bufarr = g_value_peek_pointer (streamheader);
1270   if (bufarr->len != 2) {
1271     GST_WARNING ("Too few headers in streamheader field");
1272     return FALSE;
1273   }
1274
1275   context->xiph_headers_to_skip = bufarr->len + 1;
1276
1277   bufval = &g_array_index (bufarr, GValue, 0);
1278   if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1279     GST_WARNING ("streamheaders array does not contain GstBuffers");
1280     return FALSE;
1281   }
1282
1283   buffer = g_value_peek_pointer (bufval);
1284
1285   if (GST_BUFFER_SIZE (buffer) < 80
1286       || memcmp (GST_BUFFER_DATA (buffer), "Speex   ", 8) != 0) {
1287     GST_WARNING ("Invalid streamheader for Speex");
1288     return FALSE;
1289   }
1290
1291   context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1292   context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1293   memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1294       GST_BUFFER_SIZE (buffer));
1295
1296   bufval = &g_array_index (bufarr, GValue, 1);
1297
1298   if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1299     g_free (context->codec_priv);
1300     context->codec_priv = NULL;
1301     context->codec_priv_size = 0;
1302     GST_WARNING ("streamheaders array does not contain GstBuffers");
1303     return FALSE;
1304   }
1305
1306   buffer = g_value_peek_pointer (bufval);
1307
1308   context->codec_priv =
1309       g_realloc (context->codec_priv,
1310       context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1311   memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1312       GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1313   context->codec_priv_size =
1314       context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1315
1316   return TRUE;
1317 }
1318
1319 static const gchar *
1320 aac_codec_data_to_codec_id (const GstBuffer * buf)
1321 {
1322   const gchar *result;
1323   gint profile;
1324
1325   /* default to MAIN */
1326   profile = 1;
1327
1328   if (GST_BUFFER_SIZE (buf) >= 2) {
1329     profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1330     profile >>= 3;
1331   }
1332
1333   switch (profile) {
1334     case 1:
1335       result = "MAIN";
1336       break;
1337     case 2:
1338       result = "LC";
1339       break;
1340     case 3:
1341       result = "SSR";
1342       break;
1343     case 4:
1344       result = "LTP";
1345       break;
1346     default:
1347       GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1348       result = "MAIN";
1349       break;
1350   }
1351
1352   return result;
1353 }
1354
1355 /**
1356  * gst_matroska_mux_audio_pad_setcaps:
1357  * @pad: Pad which got the caps.
1358  * @caps: New caps.
1359  *
1360  * Setcaps function for audio sink pad.
1361  *
1362  * Returns: #TRUE on success.
1363  */
1364 static gboolean
1365 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1366 {
1367   GstMatroskaTrackContext *context = NULL;
1368   GstMatroskaTrackAudioContext *audiocontext;
1369   GstMatroskaMux *mux;
1370   GstMatroskaPad *collect_pad;
1371   const gchar *mimetype;
1372   gint samplerate = 0, channels = 0;
1373   GstStructure *structure;
1374   const GValue *codec_data = NULL;
1375   const GstBuffer *buf = NULL;
1376   const gchar *stream_format = NULL;
1377
1378   mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1379
1380   /* find context */
1381   collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1382   g_assert (collect_pad);
1383   context = collect_pad->track;
1384   g_assert (context);
1385   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1386   audiocontext = (GstMatroskaTrackAudioContext *) context;
1387
1388   structure = gst_caps_get_structure (caps, 0);
1389   mimetype = gst_structure_get_name (structure);
1390
1391   /* general setup */
1392   gst_structure_get_int (structure, "rate", &samplerate);
1393   gst_structure_get_int (structure, "channels", &channels);
1394
1395   audiocontext->samplerate = samplerate;
1396   audiocontext->channels = channels;
1397   audiocontext->bitdepth = 0;
1398   context->default_duration = 0;
1399
1400   codec_data = gst_structure_get_value (structure, "codec_data");
1401   if (codec_data)
1402     buf = gst_value_get_buffer (codec_data);
1403
1404   /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1405    *         data and other settings
1406    *       - add new formats
1407    */
1408
1409   if (!strcmp (mimetype, "audio/mpeg")) {
1410     gint mpegversion = 0;
1411
1412     gst_structure_get_int (structure, "mpegversion", &mpegversion);
1413     switch (mpegversion) {
1414       case 1:{
1415         gint layer;
1416         gint version = 1;
1417         gint spf;
1418
1419         gst_structure_get_int (structure, "layer", &layer);
1420
1421         if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1422           GST_WARNING_OBJECT (mux,
1423               "Unable to determine MPEG audio version, assuming 1");
1424           version = 1;
1425         }
1426
1427         if (layer == 1)
1428           spf = 384;
1429         else if (layer == 2)
1430           spf = 1152;
1431         else if (version == 2)
1432           spf = 576;
1433         else
1434           spf = 1152;
1435
1436         context->default_duration =
1437             gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1438
1439         switch (layer) {
1440           case 1:
1441             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1442             break;
1443           case 2:
1444             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1445             break;
1446           case 3:
1447             context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1448             break;
1449           default:
1450             goto refuse_caps;
1451         }
1452         break;
1453       }
1454       case 2:
1455       case 4:
1456         stream_format = gst_structure_get_string (structure, "stream-format");
1457         /* check this is raw aac */
1458         if (stream_format) {
1459           if (strcmp (stream_format, "raw") != 0) {
1460             GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1461                 stream_format);
1462           }
1463         } else {
1464           GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1465               "assuming 'raw'");
1466         }
1467
1468         if (buf) {
1469           if (mpegversion == 2)
1470             context->codec_id =
1471                 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1472                 aac_codec_data_to_codec_id (buf));
1473           else if (mpegversion == 4)
1474             context->codec_id =
1475                 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1476                 aac_codec_data_to_codec_id (buf));
1477           else
1478             g_assert_not_reached ();
1479         } else {
1480           GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1481           goto refuse_caps;
1482         }
1483         break;
1484       default:
1485         goto refuse_caps;
1486     }
1487   } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1488     gint width, depth;
1489     gint endianness = G_LITTLE_ENDIAN;
1490     gboolean signedness = TRUE;
1491
1492     if (!gst_structure_get_int (structure, "width", &width) ||
1493         !gst_structure_get_int (structure, "depth", &depth) ||
1494         !gst_structure_get_boolean (structure, "signed", &signedness)) {
1495       GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1496       goto refuse_caps;
1497     }
1498
1499     if (depth > 8 &&
1500         !gst_structure_get_int (structure, "endianness", &endianness)) {
1501       GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1502       goto refuse_caps;
1503     }
1504
1505     if (width != depth) {
1506       GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1507       goto refuse_caps;
1508     }
1509
1510     /* FIXME: where is this spec'ed out? (tpm) */
1511     if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1512       GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1513       goto refuse_caps;
1514     }
1515
1516     audiocontext->bitdepth = depth;
1517     if (endianness == G_BIG_ENDIAN)
1518       context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1519     else
1520       context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1521
1522   } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1523     gint width;
1524
1525     if (!gst_structure_get_int (structure, "width", &width)) {
1526       GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1527       goto refuse_caps;
1528     }
1529
1530     audiocontext->bitdepth = width;
1531     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1532
1533   } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1534     const GValue *streamheader;
1535
1536     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1537
1538     if (context->codec_priv != NULL) {
1539       g_free (context->codec_priv);
1540       context->codec_priv = NULL;
1541       context->codec_priv_size = 0;
1542     }
1543
1544     streamheader = gst_structure_get_value (structure, "streamheader");
1545     if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1546       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1547           ("vorbis stream headers missing or malformed"));
1548       goto refuse_caps;
1549     }
1550   } else if (!strcmp (mimetype, "audio/x-flac")) {
1551     const GValue *streamheader;
1552
1553     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1554     if (context->codec_priv != NULL) {
1555       g_free (context->codec_priv);
1556       context->codec_priv = NULL;
1557       context->codec_priv_size = 0;
1558     }
1559
1560     streamheader = gst_structure_get_value (structure, "streamheader");
1561     if (!flac_streamheader_to_codecdata (streamheader, context)) {
1562       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1563           ("flac stream headers missing or malformed"));
1564       goto refuse_caps;
1565     }
1566   } else if (!strcmp (mimetype, "audio/x-speex")) {
1567     const GValue *streamheader;
1568
1569     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1570     if (context->codec_priv != NULL) {
1571       g_free (context->codec_priv);
1572       context->codec_priv = NULL;
1573       context->codec_priv_size = 0;
1574     }
1575
1576     streamheader = gst_structure_get_value (structure, "streamheader");
1577     if (!speex_streamheader_to_codecdata (streamheader, context)) {
1578       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1579           ("speex stream headers missing or malformed"));
1580       goto refuse_caps;
1581     }
1582   } else if (!strcmp (mimetype, "audio/x-ac3")) {
1583     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1584   } else if (!strcmp (mimetype, "audio/x-eac3")) {
1585     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1586   } else if (!strcmp (mimetype, "audio/x-dts")) {
1587     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1588   } else if (!strcmp (mimetype, "audio/x-tta")) {
1589     gint width;
1590
1591     /* TTA frame duration */
1592     context->default_duration = 1.04489795918367346939 * GST_SECOND;
1593
1594     gst_structure_get_int (structure, "width", &width);
1595     audiocontext->bitdepth = width;
1596     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1597
1598   } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1599     gint raversion;
1600     const GValue *mdpr_data;
1601
1602     gst_structure_get_int (structure, "raversion", &raversion);
1603     switch (raversion) {
1604       case 1:
1605         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1606         break;
1607       case 2:
1608         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1609         break;
1610       case 8:
1611         context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1612         break;
1613       default:
1614         goto refuse_caps;
1615     }
1616
1617     mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1618     if (mdpr_data != NULL) {
1619       guint8 *priv_data = NULL;
1620       guint priv_data_size = 0;
1621
1622       GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1623
1624       priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1625       priv_data = g_malloc0 (priv_data_size);
1626
1627       memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1628
1629       context->codec_priv = priv_data;
1630       context->codec_priv_size = priv_data_size;
1631     }
1632
1633   } else if (!strcmp (mimetype, "audio/x-wma")) {
1634     guint8 *codec_priv;
1635     guint codec_priv_size;
1636     guint16 format;
1637     gint block_align;
1638     gint bitrate;
1639     gint wmaversion;
1640     gint depth;
1641
1642     if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1643         || !gst_structure_get_int (structure, "block_align", &block_align)
1644         || !gst_structure_get_int (structure, "bitrate", &bitrate)
1645         || samplerate == 0 || channels == 0) {
1646       GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1647           "channels/rate on WMA caps");
1648       goto refuse_caps;
1649     }
1650
1651     switch (wmaversion) {
1652       case 1:
1653         format = GST_RIFF_WAVE_FORMAT_WMAV1;
1654         break;
1655       case 2:
1656         format = GST_RIFF_WAVE_FORMAT_WMAV2;
1657         break;
1658       case 3:
1659         format = GST_RIFF_WAVE_FORMAT_WMAV3;
1660         break;
1661       default:
1662         GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1663         goto refuse_caps;
1664     }
1665
1666     if (gst_structure_get_int (structure, "depth", &depth))
1667       audiocontext->bitdepth = depth;
1668
1669     codec_priv_size = WAVEFORMATEX_SIZE;
1670     if (buf)
1671       codec_priv_size += GST_BUFFER_SIZE (buf);
1672
1673     /* serialize waveformatex structure */
1674     codec_priv = g_malloc0 (codec_priv_size);
1675     GST_WRITE_UINT16_LE (codec_priv, format);
1676     GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1677     GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1678     GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1679     GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1680     GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1681     if (buf)
1682       GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1683     else
1684       GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1685
1686     /* process codec private/initialization data, if any */
1687     if (buf) {
1688       memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1689           GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1690     }
1691
1692     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1693     context->codec_priv = (gpointer) codec_priv;
1694     context->codec_priv_size = codec_priv_size;
1695   }
1696
1697   return TRUE;
1698
1699   /* ERRORS */
1700 refuse_caps:
1701   {
1702     GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1703         GST_PAD_NAME (pad), caps);
1704     return FALSE;
1705   }
1706 }
1707
1708
1709 /**
1710  * gst_matroska_mux_subtitle_pad_setcaps:
1711  * @pad: Pad which got the caps.
1712  * @caps: New caps.
1713  *
1714  * Setcaps function for subtitle sink pad.
1715  *
1716  * Returns: #TRUE on success.
1717  */
1718 static gboolean
1719 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1720 {
1721   /* FIXME:
1722    * Consider this as boilerplate code for now. There is
1723    * no single subtitle creation element in GStreamer,
1724    * neither do I know how subtitling works at all. */
1725
1726   /* There is now (at least) one such alement (kateenc), and I'm going
1727      to handle it here and claim it works when it can be piped back
1728      through GStreamer and VLC */
1729
1730   GstMatroskaTrackContext *context = NULL;
1731   GstMatroskaTrackSubtitleContext *scontext;
1732   GstMatroskaMux *mux;
1733   GstMatroskaPad *collect_pad;
1734   const gchar *mimetype;
1735   GstStructure *structure;
1736
1737   mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1738
1739   /* find context */
1740   collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1741   g_assert (collect_pad);
1742   context = collect_pad->track;
1743   g_assert (context);
1744   g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1745   scontext = (GstMatroskaTrackSubtitleContext *) context;
1746
1747   structure = gst_caps_get_structure (caps, 0);
1748   mimetype = gst_structure_get_name (structure);
1749
1750   /* general setup */
1751   scontext->check_utf8 = 1;
1752   scontext->invalid_utf8 = 0;
1753   context->default_duration = 0;
1754
1755   /* TODO: - other format than Kate */
1756
1757   if (!strcmp (mimetype, "subtitle/x-kate")) {
1758     const GValue *streamheader;
1759
1760     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1761
1762     if (context->codec_priv != NULL) {
1763       g_free (context->codec_priv);
1764       context->codec_priv = NULL;
1765       context->codec_priv_size = 0;
1766     }
1767
1768     streamheader = gst_structure_get_value (structure, "streamheader");
1769     if (!kate_streamheader_to_codecdata (streamheader, context)) {
1770       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1771           ("kate stream headers missing or malformed"));
1772       return FALSE;
1773     }
1774     return TRUE;
1775   }
1776
1777   return FALSE;
1778 }
1779
1780
1781 /**
1782  * gst_matroska_mux_request_new_pad:
1783  * @element: #GstMatroskaMux.
1784  * @templ: #GstPadTemplate.
1785  * @pad_name: New pad name.
1786  *
1787  * Request pad function for sink templates.
1788  *
1789  * Returns: New #GstPad.
1790  */
1791 static GstPad *
1792 gst_matroska_mux_request_new_pad (GstElement * element,
1793     GstPadTemplate * templ, const gchar * pad_name)
1794 {
1795   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1796   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1797   GstMatroskaPad *collect_pad;
1798   GstPad *newpad = NULL;
1799   gchar *name = NULL;
1800   GstPadSetCapsFunction setcapsfunc = NULL;
1801   GstMatroskaTrackContext *context = NULL;
1802
1803   if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1804     name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1805     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1806     context = (GstMatroskaTrackContext *)
1807         g_new0 (GstMatroskaTrackAudioContext, 1);
1808     context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1809     context->name = g_strdup ("Audio");
1810   } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1811     name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1812     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1813     context = (GstMatroskaTrackContext *)
1814         g_new0 (GstMatroskaTrackVideoContext, 1);
1815     context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1816     context->name = g_strdup ("Video");
1817   } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1818     name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1819     setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1820     context = (GstMatroskaTrackContext *)
1821         g_new0 (GstMatroskaTrackSubtitleContext, 1);
1822     context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1823     context->name = g_strdup ("Subtitle");
1824   } else {
1825     GST_WARNING_OBJECT (mux, "This is not our template!");
1826     return NULL;
1827   }
1828
1829   newpad = gst_pad_new_from_template (templ, name);
1830   g_free (name);
1831   collect_pad = (GstMatroskaPad *)
1832       gst_collect_pads_add_pad_full (mux->collect, newpad,
1833       sizeof (GstMatroskaPad),
1834       (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1835
1836   collect_pad->track = context;
1837   gst_matroska_pad_reset (collect_pad, FALSE);
1838
1839   /* FIXME: hacked way to override/extend the event function of
1840    * GstCollectPads; because it sets its own event function giving the
1841    * element no access to events.
1842    * TODO GstCollectPads should really give its 'users' a clean chance to
1843    * properly handle events that are not meant for collectpads itself.
1844    * Perhaps a callback or so, though rejected (?) in #340060.
1845    * This would allow (clean) transcoding of info from demuxer/streams
1846    * to another muxer */
1847   mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1848   gst_pad_set_event_function (newpad,
1849       GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1850
1851   gst_pad_set_setcaps_function (newpad, setcapsfunc);
1852   gst_pad_set_active (newpad, TRUE);
1853   gst_element_add_pad (element, newpad);
1854   mux->num_streams++;
1855
1856   return newpad;
1857 }
1858
1859 /**
1860  * gst_matroska_mux_release_pad:
1861  * @element: #GstMatroskaMux.
1862  * @pad: Pad to release.
1863  *
1864  * Release a previously requested pad.
1865 */
1866 static void
1867 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1868 {
1869   GstMatroskaMux *mux;
1870   GSList *walk;
1871
1872   mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1873
1874   for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1875     GstCollectData *cdata = (GstCollectData *) walk->data;
1876     GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1877
1878     if (cdata->pad == pad) {
1879       GstClockTime min_dur;     /* observed minimum duration */
1880
1881       if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1882           GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1883         min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1884         if (collect_pad->duration < min_dur)
1885           collect_pad->duration = min_dur;
1886       }
1887
1888       if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1889           mux->duration < collect_pad->duration)
1890         mux->duration = collect_pad->duration;
1891
1892       break;
1893     }
1894   }
1895
1896   gst_collect_pads_remove_pad (mux->collect, pad);
1897   if (gst_element_remove_pad (element, pad))
1898     mux->num_streams--;
1899 }
1900
1901
1902 /**
1903  * gst_matroska_mux_track_header:
1904  * @mux: #GstMatroskaMux
1905  * @context: Tack context.
1906  *
1907  * Write a track header.
1908  */
1909 static void
1910 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1911     GstMatroskaTrackContext * context)
1912 {
1913   GstEbmlWrite *ebml = mux->ebml_write;
1914   guint64 master;
1915
1916   /* TODO: check if everything necessary is written and check default values */
1917
1918   /* track type goes before the type-specific stuff */
1919   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1920   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1921
1922   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1923       gst_matroska_mux_create_uid ());
1924   if (context->default_duration) {
1925     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1926         context->default_duration);
1927   }
1928   if (context->language) {
1929     gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1930         context->language);
1931   }
1932
1933   /* type-specific stuff */
1934   switch (context->type) {
1935     case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1936       GstMatroskaTrackVideoContext *videocontext =
1937           (GstMatroskaTrackVideoContext *) context;
1938
1939       master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1940       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1941           videocontext->pixel_width);
1942       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1943           videocontext->pixel_height);
1944       if (videocontext->display_width && videocontext->display_height) {
1945         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1946             videocontext->display_width);
1947         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1948             videocontext->display_height);
1949       }
1950       if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1951         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1952       if (videocontext->fourcc) {
1953         guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1954
1955         gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1956             (gpointer) & fcc_le, 4);
1957       }
1958       gst_ebml_write_master_finish (ebml, master);
1959
1960       break;
1961     }
1962
1963     case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1964       GstMatroskaTrackAudioContext *audiocontext =
1965           (GstMatroskaTrackAudioContext *) context;
1966
1967       master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1968       if (audiocontext->samplerate != 8000)
1969         gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1970             audiocontext->samplerate);
1971       if (audiocontext->channels != 1)
1972         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1973             audiocontext->channels);
1974       if (audiocontext->bitdepth) {
1975         gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1976             audiocontext->bitdepth);
1977       }
1978       gst_ebml_write_master_finish (ebml, master);
1979
1980       break;
1981     }
1982
1983     default:
1984       /* doesn't need type-specific data */
1985       break;
1986   }
1987
1988   gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1989   if (context->codec_priv)
1990     gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1991         context->codec_priv, context->codec_priv_size);
1992   /* FIXME: until we have a nice way of getting the codecname
1993    * out of the caps, I'm not going to enable this. Too much
1994    * (useless, double, boring) work... */
1995   /* TODO: Use value from tags if any */
1996   /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1997      context->codec_name); */
1998   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1999 }
2000
2001
2002 /**
2003  * gst_matroska_mux_start:
2004  * @mux: #GstMatroskaMux
2005  *
2006  * Start a new matroska file (write headers etc...)
2007  */
2008 static void
2009 gst_matroska_mux_start (GstMatroskaMux * mux)
2010 {
2011   GstEbmlWrite *ebml = mux->ebml_write;
2012   const gchar *doctype;
2013   guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2014     GST_MATROSKA_ID_TRACKS,
2015     GST_MATROSKA_ID_CUES,
2016     GST_MATROSKA_ID_TAGS,
2017     0
2018   };
2019   guint64 master, child;
2020   GSList *collected;
2021   int i;
2022   guint tracknum = 1;
2023   GstClockTime duration = 0;
2024   guint32 segment_uid[4];
2025   GTimeVal time = { 0, 0 };
2026
2027   if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2028     ebml->caps = gst_caps_from_string ("video/webm");
2029   } else {
2030     ebml->caps = gst_caps_from_string ("video/x-matroska");
2031   }
2032   /* we start with a EBML header */
2033   doctype = mux->doctype;
2034   GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2035       doctype, mux->doctype_version);
2036   gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2037
2038   /* the rest of the header is cached */
2039   gst_ebml_write_set_cache (ebml, 0x1000);
2040
2041   /* start a segment */
2042   mux->segment_pos =
2043       gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2044   mux->segment_master = ebml->pos;
2045
2046   if (!mux->streamable) {
2047     /* seekhead (table of contents) - we set the positions later */
2048     mux->seekhead_pos = ebml->pos;
2049     master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2050     for (i = 0; seekhead_id[i] != 0; i++) {
2051       child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2052       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2053       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2054       gst_ebml_write_master_finish (ebml, child);
2055     }
2056     gst_ebml_write_master_finish (ebml, master);
2057   }
2058
2059   /* segment info */
2060   mux->info_pos = ebml->pos;
2061   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2062   for (i = 0; i < 4; i++) {
2063     segment_uid[i] = g_random_int ();
2064   }
2065   gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2066       (guint8 *) segment_uid, 16);
2067   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2068   mux->duration_pos = ebml->pos;
2069   /* get duration */
2070   if (!mux->streamable) {
2071     for (collected = mux->collect->data; collected;
2072         collected = g_slist_next (collected)) {
2073       GstMatroskaPad *collect_pad;
2074       GstFormat format = GST_FORMAT_TIME;
2075       GstPad *thepad;
2076       gint64 trackduration;
2077
2078       collect_pad = (GstMatroskaPad *) collected->data;
2079       thepad = collect_pad->collect.pad;
2080
2081       /* Query the total length of the track. */
2082       GST_DEBUG_OBJECT (thepad, "querying peer duration");
2083       if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2084         GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2085             GST_TIME_ARGS (trackduration));
2086         if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2087           duration = (GstClockTime) trackduration;
2088         }
2089       }
2090     }
2091     gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2092         gst_guint64_to_gdouble (duration) /
2093         gst_guint64_to_gdouble (mux->time_scale));
2094   }
2095   gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2096       "GStreamer plugin version " PACKAGE_VERSION);
2097   if (mux->writing_app && mux->writing_app[0]) {
2098     gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2099   }
2100   g_get_current_time (&time);
2101   gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2102   gst_ebml_write_master_finish (ebml, master);
2103
2104   /* tracks */
2105   mux->tracks_pos = ebml->pos;
2106   master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2107
2108   for (collected = mux->collect->data; collected;
2109       collected = g_slist_next (collected)) {
2110     GstMatroskaPad *collect_pad;
2111     GstPad *thepad;
2112
2113     collect_pad = (GstMatroskaPad *) collected->data;
2114     thepad = collect_pad->collect.pad;
2115
2116     if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2117         collect_pad->track->codec_id != 0) {
2118       collect_pad->track->num = tracknum++;
2119       child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2120       gst_matroska_mux_track_header (mux, collect_pad->track);
2121       gst_ebml_write_master_finish (ebml, child);
2122     }
2123   }
2124   gst_ebml_write_master_finish (ebml, master);
2125
2126   /* lastly, flush the cache */
2127   gst_ebml_write_flush_cache (ebml, FALSE);
2128 }
2129
2130 static void
2131 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2132     gpointer data)
2133 {
2134   /* TODO: more sensible tag mappings */
2135   struct
2136   {
2137     const gchar *matroska_tagname;
2138     const gchar *gstreamer_tagname;
2139   }
2140   tag_conv[] = {
2141     {
2142     GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2143     GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2144     GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2145     GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2146     GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2147     GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2148     GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2149     GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2150     GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2151     GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2152     GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2153     GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2154     GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2155     GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2156     GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2157   };
2158   GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2159   guint i;
2160   guint64 simpletag_master;
2161
2162   for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2163     const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2164     const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2165
2166     if (strcmp (tagname_gst, tag) == 0) {
2167       GValue src = { 0, };
2168       gchar *dest;
2169
2170       if (!gst_tag_list_copy_value (&src, list, tag))
2171         break;
2172       if ((dest = gst_value_serialize (&src))) {
2173
2174         simpletag_master = gst_ebml_write_master_start (ebml,
2175             GST_MATROSKA_ID_SIMPLETAG);
2176         gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2177         gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2178         gst_ebml_write_master_finish (ebml, simpletag_master);
2179         g_free (dest);
2180       } else {
2181         GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2182       }
2183       g_value_unset (&src);
2184       break;
2185     }
2186   }
2187 }
2188
2189
2190 /**
2191  * gst_matroska_mux_finish:
2192  * @mux: #GstMatroskaMux
2193  *
2194  * Finish a new matroska file (write index etc...)
2195  */
2196 static void
2197 gst_matroska_mux_finish (GstMatroskaMux * mux)
2198 {
2199   GstEbmlWrite *ebml = mux->ebml_write;
2200   guint64 pos;
2201   guint64 duration = 0;
2202   GSList *collected;
2203   const GstTagList *tags;
2204
2205   /* finish last cluster */
2206   if (mux->cluster) {
2207     gst_ebml_write_master_finish (ebml, mux->cluster);
2208   }
2209
2210   /* cues */
2211   if (mux->index != NULL) {
2212     guint n;
2213     guint64 master, pointentry_master, trackpos_master;
2214
2215     mux->cues_pos = ebml->pos;
2216     gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2217     master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2218
2219     for (n = 0; n < mux->num_indexes; n++) {
2220       GstMatroskaIndex *idx = &mux->index[n];
2221
2222       pointentry_master = gst_ebml_write_master_start (ebml,
2223           GST_MATROSKA_ID_POINTENTRY);
2224       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2225           idx->time / mux->time_scale);
2226       trackpos_master = gst_ebml_write_master_start (ebml,
2227           GST_MATROSKA_ID_CUETRACKPOSITIONS);
2228       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2229       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2230           idx->pos - mux->segment_master);
2231       gst_ebml_write_master_finish (ebml, trackpos_master);
2232       gst_ebml_write_master_finish (ebml, pointentry_master);
2233     }
2234
2235     gst_ebml_write_master_finish (ebml, master);
2236     gst_ebml_write_flush_cache (ebml, FALSE);
2237   }
2238
2239   /* tags */
2240   tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2241
2242   if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2243     guint64 master_tags, master_tag;
2244
2245     GST_DEBUG ("Writing tags");
2246
2247     /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2248     mux->tags_pos = ebml->pos;
2249     master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2250     master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2251     gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2252     gst_ebml_write_master_finish (ebml, master_tag);
2253     gst_ebml_write_master_finish (ebml, master_tags);
2254   }
2255
2256   /* update seekhead. We know that:
2257    * - a seekhead contains 4 entries.
2258    * - order of entries is as above.
2259    * - a seekhead has a 4-byte header + 8-byte length
2260    * - each entry is 2-byte master, 2-byte ID pointer,
2261    *     2-byte length pointer, all 8/1-byte length, 4-
2262    *     byte ID and 8-byte length pointer, where the
2263    *     length pointer starts at 20.
2264    * - all entries are local to the segment (so pos - segment_master).
2265    * - so each entry is at 12 + 20 + num * 28. */
2266   gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2267       mux->info_pos - mux->segment_master);
2268   gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2269       mux->tracks_pos - mux->segment_master);
2270   if (mux->index != NULL) {
2271     gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2272         mux->cues_pos - mux->segment_master);
2273   } else {
2274     /* void'ify */
2275     guint64 my_pos = ebml->pos;
2276
2277     gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2278     gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2279     gst_ebml_write_seek (ebml, my_pos);
2280   }
2281   if (tags != NULL) {
2282     gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2283         mux->tags_pos - mux->segment_master);
2284   } else {
2285     /* void'ify */
2286     guint64 my_pos = ebml->pos;
2287
2288     gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2289     gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2290     gst_ebml_write_seek (ebml, my_pos);
2291   }
2292
2293   /* update duration */
2294   /* first get the overall duration */
2295   /* a released track may have left a duration in here */
2296   duration = mux->duration;
2297   for (collected = mux->collect->data; collected;
2298       collected = g_slist_next (collected)) {
2299     GstMatroskaPad *collect_pad;
2300     GstClockTime min_duration;  /* observed minimum duration */
2301
2302     collect_pad = (GstMatroskaPad *) collected->data;
2303
2304     GST_DEBUG_OBJECT (mux,
2305         "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2306         " end ts %" GST_TIME_FORMAT, collect_pad,
2307         GST_TIME_ARGS (collect_pad->start_ts),
2308         GST_TIME_ARGS (collect_pad->end_ts));
2309
2310     if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2311         GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2312       min_duration =
2313           GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2314       if (collect_pad->duration < min_duration)
2315         collect_pad->duration = min_duration;
2316       GST_DEBUG_OBJECT (collect_pad,
2317           "final track duration: %" GST_TIME_FORMAT,
2318           GST_TIME_ARGS (collect_pad->duration));
2319     }
2320
2321     if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2322         duration < collect_pad->duration)
2323       duration = collect_pad->duration;
2324   }
2325   if (duration != 0) {
2326     GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2327         GST_TIME_ARGS (duration));
2328     pos = mux->ebml_write->pos;
2329     gst_ebml_write_seek (ebml, mux->duration_pos);
2330     gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2331         gst_guint64_to_gdouble (duration) /
2332         gst_guint64_to_gdouble (mux->time_scale));
2333     gst_ebml_write_seek (ebml, pos);
2334   } else {
2335     /* void'ify */
2336     guint64 my_pos = ebml->pos;
2337
2338     gst_ebml_write_seek (ebml, mux->duration_pos);
2339     gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2340     gst_ebml_write_seek (ebml, my_pos);
2341   }
2342   GST_DEBUG_OBJECT (mux, "finishing segment");
2343   /* finish segment - this also writes element length */
2344   gst_ebml_write_master_finish (ebml, mux->segment_pos);
2345 }
2346
2347
2348 /**
2349  * gst_matroska_mux_best_pad:
2350  * @mux: #GstMatroskaMux
2351  * @popped: True if at least one buffer was popped from #GstCollectPads
2352  *
2353  * Find a pad with the oldest data 
2354  * (data from this pad should be written first).
2355  *
2356  * Returns: Selected pad.
2357  */
2358 static GstMatroskaPad *
2359 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2360 {
2361   GSList *collected;
2362   GstMatroskaPad *best = NULL;
2363
2364   *popped = FALSE;
2365   for (collected = mux->collect->data; collected;
2366       collected = g_slist_next (collected)) {
2367     GstMatroskaPad *collect_pad;
2368
2369     collect_pad = (GstMatroskaPad *) collected->data;
2370     /* fetch a new buffer if needed */
2371     if (collect_pad->buffer == NULL) {
2372       collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2373           (GstCollectData *) collect_pad);
2374
2375       if (collect_pad->buffer != NULL)
2376         *popped = TRUE;
2377     }
2378
2379     /* if we have a buffer check if it is better then the current best one */
2380     if (collect_pad->buffer != NULL) {
2381       if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2382           || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2383               && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2384               GST_BUFFER_TIMESTAMP (best->buffer))) {
2385         best = collect_pad;
2386       }
2387     }
2388   }
2389
2390   return best;
2391 }
2392
2393 /**
2394  * gst_matroska_mux_buffer_header:
2395  * @track: Track context.
2396  * @relative_timestamp: relative timestamp of the buffer
2397  * @flags: Buffer flags.
2398  *
2399  * Create a buffer containing buffer header.
2400  * 
2401  * Returns: New buffer.
2402  */
2403 static GstBuffer *
2404 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2405     gint16 relative_timestamp, int flags)
2406 {
2407   GstBuffer *hdr;
2408
2409   hdr = gst_buffer_new_and_alloc (4);
2410   /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2411   GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2412   /* time relative to clustertime */
2413   GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2414
2415   /* flags */
2416   GST_BUFFER_DATA (hdr)[3] = flags;
2417
2418   return hdr;
2419 }
2420
2421 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2422 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2423 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2424
2425 static GstBuffer *
2426 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2427     GstMatroskaPad * collect_pad, GstBuffer * buf)
2428 {
2429   GstMatroskaTrackVideoContext *ctx =
2430       (GstMatroskaTrackVideoContext *) collect_pad->track;
2431   const guint8 *data = GST_BUFFER_DATA (buf);
2432   guint size = GST_BUFFER_SIZE (buf);
2433   guint8 parse_code;
2434   guint32 next_parse_offset;
2435   GstBuffer *ret = NULL;
2436   gboolean is_muxing_unit = FALSE;
2437
2438   if (GST_BUFFER_SIZE (buf) < 13) {
2439     gst_buffer_unref (buf);
2440     return ret;
2441   }
2442
2443   /* Check if this buffer contains a picture or end-of-sequence packet */
2444   while (size >= 13) {
2445     if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2446       gst_buffer_unref (buf);
2447       return ret;
2448     }
2449
2450     parse_code = GST_READ_UINT8 (data + 4);
2451     if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2452       if (ctx->dirac_unit) {
2453         gst_buffer_unref (ctx->dirac_unit);
2454         ctx->dirac_unit = NULL;
2455       }
2456     } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2457         parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2458       is_muxing_unit = TRUE;
2459       break;
2460     }
2461
2462     next_parse_offset = GST_READ_UINT32_BE (data + 5);
2463
2464     if (G_UNLIKELY (next_parse_offset == 0))
2465       break;
2466
2467     data += next_parse_offset;
2468     size -= next_parse_offset;
2469   }
2470
2471   if (ctx->dirac_unit)
2472     ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2473   else
2474     ctx->dirac_unit = gst_buffer_ref (buf);
2475
2476   if (is_muxing_unit) {
2477     ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2478     ctx->dirac_unit = NULL;
2479     gst_buffer_copy_metadata (ret, buf,
2480         GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2481         GST_BUFFER_COPY_CAPS);
2482     gst_buffer_unref (buf);
2483   } else {
2484     gst_buffer_unref (buf);
2485     ret = NULL;
2486   }
2487
2488   return ret;
2489 }
2490
2491 static void
2492 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2493 {
2494   GstCaps *caps;
2495   GstStructure *s;
2496   GValue streamheader = { 0 };
2497   GValue bufval = { 0 };
2498   GstBuffer *streamheader_buffer;
2499   GstEbmlWrite *ebml = mux->ebml_write;
2500
2501   streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2502   if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2503     caps = gst_caps_from_string ("video/webm");
2504   } else {
2505     caps = gst_caps_from_string ("video/x-matroska");
2506   }
2507   s = gst_caps_get_structure (caps, 0);
2508   g_value_init (&streamheader, GST_TYPE_ARRAY);
2509   g_value_init (&bufval, GST_TYPE_BUFFER);
2510   gst_value_set_buffer (&bufval, streamheader_buffer);
2511   gst_value_array_append_value (&streamheader, &bufval);
2512   g_value_unset (&bufval);
2513   gst_structure_set_value (s, "streamheader", &streamheader);
2514   g_value_unset (&streamheader);
2515   gst_caps_unref (ebml->caps);
2516   gst_buffer_unref (streamheader_buffer);
2517   ebml->caps = caps;
2518 }
2519
2520 /**
2521  * gst_matroska_mux_write_data:
2522  * @mux: #GstMatroskaMux
2523  * @collect_pad: #GstMatroskaPad with the data
2524  *
2525  * Write collected data (called from gst_matroska_mux_collected).
2526  *
2527  * Returns: Result of the gst_pad_push issued to write the data.
2528  */
2529 static GstFlowReturn
2530 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2531 {
2532   GstEbmlWrite *ebml = mux->ebml_write;
2533   GstBuffer *buf, *hdr;
2534   guint64 blockgroup;
2535   gboolean write_duration;
2536   gint16 relative_timestamp;
2537   gint64 relative_timestamp64;
2538   guint64 block_duration;
2539   gboolean is_video_keyframe = FALSE;
2540
2541   /* write data */
2542   buf = collect_pad->buffer;
2543   collect_pad->buffer = NULL;
2544
2545   /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2546   if (collect_pad->track->xiph_headers_to_skip > 0) {
2547     GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2548     gst_buffer_unref (buf);
2549     --collect_pad->track->xiph_headers_to_skip;
2550     return GST_FLOW_OK;
2551   }
2552
2553   /* for dirac we have to queue up everything up to a picture unit */
2554   if (collect_pad->track->codec_id != NULL &&
2555       strcmp (collect_pad->track->codec_id,
2556           GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2557     buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2558     if (!buf)
2559       return GST_FLOW_OK;
2560   }
2561
2562   /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2563    * this would wreak havoc with time stored in matroska file */
2564   /* TODO: maybe calculate a timestamp by using the previous timestamp
2565    * and default duration */
2566   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2567     GST_WARNING_OBJECT (collect_pad->collect.pad,
2568         "Invalid buffer timestamp; dropping buffer");
2569     gst_buffer_unref (buf);
2570     return GST_FLOW_OK;
2571   }
2572
2573   /* set the timestamp for outgoing buffers */
2574   ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2575
2576   if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2577       !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2578     GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2579         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2580     is_video_keyframe = TRUE;
2581   }
2582
2583   if (mux->cluster) {
2584     /* start a new cluster at every keyframe or when we may be reaching the
2585      * limit of the relative timestamp */
2586     if (mux->cluster_time +
2587         mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2588         || is_video_keyframe) {
2589       if (!mux->streamable)
2590         gst_ebml_write_master_finish (ebml, mux->cluster);
2591       mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2592       mux->cluster_pos = ebml->pos;
2593       gst_ebml_write_set_cache (ebml, 0x20);
2594       mux->cluster =
2595           gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2596       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2597           gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2598               mux->time_scale));
2599       GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2600           gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2601               mux->time_scale));
2602       gst_ebml_write_flush_cache (ebml, TRUE);
2603       mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2604       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2605           mux->prev_cluster_size);
2606     }
2607   } else {
2608     /* first cluster */
2609
2610     mux->cluster_pos = ebml->pos;
2611     gst_ebml_write_set_cache (ebml, 0x20);
2612     mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2613     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2614         gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2615     gst_ebml_write_flush_cache (ebml, TRUE);
2616     mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2617   }
2618
2619   /* update duration of this track */
2620   if (GST_BUFFER_DURATION_IS_VALID (buf))
2621     collect_pad->duration += GST_BUFFER_DURATION (buf);
2622
2623   /* We currently write index entries for all video tracks or for the audio
2624    * track in a single-track audio file.  This could be improved by keeping the
2625    * index only for the *first* video track. */
2626
2627   /* TODO: index is useful for every track, should contain the number of
2628    * the block in the cluster which contains the timestamp, should also work
2629    * for files with multiple audio tracks.
2630    */
2631   if (is_video_keyframe ||
2632       ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2633           (mux->num_streams == 1))) {
2634     gint last_idx = -1;
2635
2636     if (mux->min_index_interval != 0) {
2637       for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2638         if (mux->index[last_idx].track == collect_pad->track->num)
2639           break;
2640       }
2641     }
2642
2643     if (last_idx < 0 || mux->min_index_interval == 0 ||
2644         (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2645             >= mux->min_index_interval)) {
2646       GstMatroskaIndex *idx;
2647
2648       if (mux->num_indexes % 32 == 0) {
2649         mux->index = g_renew (GstMatroskaIndex, mux->index,
2650             mux->num_indexes + 32);
2651       }
2652       idx = &mux->index[mux->num_indexes++];
2653
2654       idx->pos = mux->cluster_pos;
2655       idx->time = GST_BUFFER_TIMESTAMP (buf);
2656       idx->track = collect_pad->track->num;
2657     }
2658   }
2659
2660   /* Check if the duration differs from the default duration. */
2661   write_duration = FALSE;
2662   block_duration = GST_BUFFER_DURATION (buf);
2663   if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2664     if (block_duration != collect_pad->track->default_duration) {
2665       write_duration = TRUE;
2666     }
2667   }
2668
2669   /* write the block, for doctype v2 use SimpleBlock if possible
2670    * one slice (*breath*).
2671    * FIXME: Need to do correct lacing! */
2672   relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2673   if (relative_timestamp64 >= 0) {
2674     /* round the timestamp */
2675     relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2676   } else {
2677     /* round the timestamp */
2678     relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2679   }
2680   relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2681       mux->time_scale);
2682   if (mux->doctype_version > 1 && !write_duration) {
2683     int flags =
2684         GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2685
2686     hdr =
2687         gst_matroska_mux_create_buffer_header (collect_pad->track,
2688         relative_timestamp, flags);
2689     gst_ebml_write_set_cache (ebml, 0x40);
2690     gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2691         GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2692     gst_ebml_write_buffer (ebml, hdr);
2693     gst_ebml_write_flush_cache (ebml, FALSE);
2694     gst_ebml_write_buffer (ebml, buf);
2695
2696     return gst_ebml_last_write_result (ebml);
2697   } else {
2698     gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2699     /* write and call order slightly unnatural,
2700      * but avoids seek and minizes pushing */
2701     blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2702     hdr =
2703         gst_matroska_mux_create_buffer_header (collect_pad->track,
2704         relative_timestamp, 0);
2705     if (write_duration) {
2706       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2707           gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2708     }
2709     gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2710         GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2711     gst_ebml_write_buffer (ebml, hdr);
2712     gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2713     gst_ebml_write_flush_cache (ebml, FALSE);
2714     gst_ebml_write_buffer (ebml, buf);
2715     return gst_ebml_last_write_result (ebml);
2716   }
2717 }
2718
2719
2720 /**
2721  * gst_matroska_mux_collected:
2722  * @pads: #GstCollectPads
2723  * @uuser_data: #GstMatroskaMux
2724  *
2725  * Collectpads callback.
2726  *
2727  * Returns: #GstFlowReturn
2728  */
2729 static GstFlowReturn
2730 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2731 {
2732   GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2733   GstEbmlWrite *ebml = mux->ebml_write;
2734   GstMatroskaPad *best;
2735   gboolean popped;
2736   GstFlowReturn ret;
2737
2738   GST_DEBUG_OBJECT (mux, "Collected pads");
2739
2740   /* start with a header */
2741   if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2742     if (mux->collect->data == NULL) {
2743       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2744           ("No input streams configured"));
2745       return GST_FLOW_ERROR;
2746     }
2747     mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2748     gst_ebml_start_streamheader (ebml);
2749     gst_matroska_mux_start (mux);
2750     gst_matroska_mux_stop_streamheader (mux);
2751     mux->state = GST_MATROSKA_MUX_STATE_DATA;
2752   }
2753
2754   do {
2755     /* which stream to write from? */
2756     best = gst_matroska_mux_best_pad (mux, &popped);
2757
2758     /* if there is no best pad, we have reached EOS */
2759     if (best == NULL) {
2760       GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2761       if (!mux->streamable) {
2762         gst_matroska_mux_finish (mux);
2763       } else {
2764         GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2765       }
2766       gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2767       ret = GST_FLOW_UNEXPECTED;
2768       break;
2769     }
2770     GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2771         GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2772         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2773         GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2774
2775     /* make note of first and last encountered timestamps, so we can calculate
2776      * the actual duration later when we send an updated header on eos */
2777     if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2778       GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2779       GstClockTime end_ts = start_ts;
2780
2781       if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2782         end_ts += GST_BUFFER_DURATION (best->buffer);
2783       else if (best->track->default_duration)
2784         end_ts += best->track->default_duration;
2785
2786       if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2787         best->end_ts = end_ts;
2788
2789       if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2790               start_ts < best->start_ts))
2791         best->start_ts = start_ts;
2792     }
2793
2794     /* write one buffer */
2795     ret = gst_matroska_mux_write_data (mux, best);
2796   } while (ret == GST_FLOW_OK && !popped);
2797
2798   return ret;
2799 }
2800
2801
2802 /**
2803  * gst_matroska_mux_change_state:
2804  * @element: #GstMatroskaMux
2805  * @transition: State change transition.
2806  *
2807  * Change the muxer state.
2808  *
2809  * Returns: #GstStateChangeReturn
2810  */
2811 static GstStateChangeReturn
2812 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2813 {
2814   GstStateChangeReturn ret;
2815   GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2816
2817   switch (transition) {
2818     case GST_STATE_CHANGE_NULL_TO_READY:
2819       break;
2820     case GST_STATE_CHANGE_READY_TO_PAUSED:
2821       gst_collect_pads_start (mux->collect);
2822       break;
2823     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2824       break;
2825     case GST_STATE_CHANGE_PAUSED_TO_READY:
2826       gst_collect_pads_stop (mux->collect);
2827       break;
2828     default:
2829       break;
2830   }
2831
2832   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2833
2834   switch (transition) {
2835     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2836       break;
2837     case GST_STATE_CHANGE_PAUSED_TO_READY:
2838       gst_matroska_mux_reset (GST_ELEMENT (mux));
2839       break;
2840     case GST_STATE_CHANGE_READY_TO_NULL:
2841       break;
2842     default:
2843       break;
2844   }
2845
2846   return ret;
2847 }
2848
2849 static void
2850 gst_matroska_mux_set_property (GObject * object,
2851     guint prop_id, const GValue * value, GParamSpec * pspec)
2852 {
2853   GstMatroskaMux *mux;
2854
2855   g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2856   mux = GST_MATROSKA_MUX (object);
2857
2858   switch (prop_id) {
2859     case ARG_WRITING_APP:
2860       if (!g_value_get_string (value)) {
2861         GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2862         break;
2863       }
2864       g_free (mux->writing_app);
2865       mux->writing_app = g_value_dup_string (value);
2866       break;
2867     case ARG_DOCTYPE_VERSION:
2868       mux->doctype_version = g_value_get_int (value);
2869       break;
2870     case ARG_MIN_INDEX_INTERVAL:
2871       mux->min_index_interval = g_value_get_int64 (value);
2872       break;
2873     case ARG_STREAMABLE:
2874       mux->streamable = g_value_get_boolean (value);
2875       break;
2876     default:
2877       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2878       break;
2879   }
2880 }
2881
2882 static void
2883 gst_matroska_mux_get_property (GObject * object,
2884     guint prop_id, GValue * value, GParamSpec * pspec)
2885 {
2886   GstMatroskaMux *mux;
2887
2888   g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2889   mux = GST_MATROSKA_MUX (object);
2890
2891   switch (prop_id) {
2892     case ARG_WRITING_APP:
2893       g_value_set_string (value, mux->writing_app);
2894       break;
2895     case ARG_DOCTYPE_VERSION:
2896       g_value_set_int (value, mux->doctype_version);
2897       break;
2898     case ARG_MIN_INDEX_INTERVAL:
2899       g_value_set_int64 (value, mux->min_index_interval);
2900       break;
2901     case ARG_STREAMABLE:
2902       g_value_set_boolean (value, mux->streamable);
2903       break;
2904     default:
2905       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2906       break;
2907   }
2908 }