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