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>
6 * matroska-mux.c: matroska file/stream muxer
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.
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.
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.
24 /* TODO: - check everywhere that we don't write invalid values
25 * - make sure timestamps are correctly scaled everywhere
29 * SECTION:element-matroskamux
31 * matroskamux muxes different input streams into a Matroska file.
34 * <title>Example launch line</title>
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.
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.
51 #include <gst/riff/riff-media.h>
52 #include <gst/tag/tag.h>
54 #include "matroska-mux.h"
55 #include "matroska-ids.h"
57 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
58 #define GST_CAT_DEFAULT matroskamux_debug
67 #define DEFAULT_MATROSKA_VERSION 1
68 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
70 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
73 GST_STATIC_CAPS ("video/x-matroska")
76 #define COMMON_VIDEO_CAPS \
77 "width = (int) [ 16, 4096 ], " \
78 "height = (int) [ 16, 4096 ], " \
79 "framerate = (fraction) [ 0, MAX ]"
81 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
82 "width = (int) [ 16, 4096 ], " \
83 "height = (int) [ 16, 4096 ] "
86 * * require codec data, etc as needed
89 static GstStaticPadTemplate videosink_templ =
90 GST_STATIC_PAD_TEMPLATE ("video_%d",
93 GST_STATIC_CAPS ("video/mpeg, "
94 "mpegversion = (int) { 1, 2, 4 }, "
95 "systemstream = (boolean) false, "
96 COMMON_VIDEO_CAPS "; "
98 COMMON_VIDEO_CAPS "; "
100 COMMON_VIDEO_CAPS "; "
102 COMMON_VIDEO_CAPS "; "
104 COMMON_VIDEO_CAPS "; "
106 COMMON_VIDEO_CAPS "; "
108 COMMON_VIDEO_CAPS "; "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
115 COMMON_VIDEO_CAPS "; "
116 "video/x-pn-realvideo, "
117 "rmversion = (int) [1, 4], "
118 COMMON_VIDEO_CAPS "; "
120 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
121 COMMON_VIDEO_CAPS "; "
122 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
125 #define COMMON_AUDIO_CAPS \
126 "channels = (int) [ 1, MAX ], " \
127 "rate = (int) [ 1, MAX ]"
130 * * require codec data, etc as needed
132 static GstStaticPadTemplate audiosink_templ =
133 GST_STATIC_PAD_TEMPLATE ("audio_%d",
136 GST_STATIC_CAPS ("audio/mpeg, "
137 "mpegversion = (int) 1, "
138 "layer = (int) [ 1, 3 ], "
139 "stream-format = (string) { raw }, "
140 COMMON_AUDIO_CAPS "; "
142 "mpegversion = (int) { 2, 4 }, "
143 COMMON_AUDIO_CAPS "; "
145 COMMON_AUDIO_CAPS "; "
147 COMMON_AUDIO_CAPS "; "
149 COMMON_AUDIO_CAPS "; "
151 COMMON_AUDIO_CAPS "; "
155 "signed = (boolean) false, "
156 COMMON_AUDIO_CAPS ";"
160 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
161 "signed = (boolean) true, "
162 COMMON_AUDIO_CAPS ";"
166 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
167 "signed = (boolean) true, "
168 COMMON_AUDIO_CAPS ";"
172 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
173 "signed = (boolean) true, "
174 COMMON_AUDIO_CAPS ";"
175 "audio/x-raw-float, "
176 "width = (int) [ 32, 64 ], "
177 "endianness = (int) LITTLE_ENDIAN, "
178 COMMON_AUDIO_CAPS ";"
180 "width = (int) { 8, 16, 24 }, "
181 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
182 "audio/x-pn-realaudio, "
183 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
184 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
185 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
189 static GstStaticPadTemplate subtitlesink_templ =
190 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
193 GST_STATIC_CAPS_ANY);
195 static GArray *used_uids;
196 G_LOCK_DEFINE_STATIC (used_uids);
198 static void gst_matroska_mux_add_interfaces (GType type);
200 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
201 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
203 /* Matroska muxer destructor */
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
208 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
211 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
213 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
214 GstPadTemplate * templ, const gchar * name);
215 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
217 /* gst internal change state handler */
218 static GstStateChangeReturn
219 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
221 /* gobject bla bla */
222 static void gst_matroska_mux_set_property (GObject * object,
223 guint prop_id, const GValue * value, GParamSpec * pspec);
224 static void gst_matroska_mux_get_property (GObject * object,
225 guint prop_id, GValue * value, GParamSpec * pspec);
228 static void gst_matroska_mux_reset (GstElement * element);
231 static guint64 gst_matroska_mux_create_uid ();
233 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
234 GstMatroskaTrackContext * context);
235 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
245 gst_matroska_mux_add_interfaces (GType type)
247 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
249 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
253 gst_matroska_mux_base_init (gpointer g_class)
255 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
257 gst_element_class_add_pad_template (element_class,
258 gst_static_pad_template_get (&videosink_templ));
259 gst_element_class_add_pad_template (element_class,
260 gst_static_pad_template_get (&audiosink_templ));
261 gst_element_class_add_pad_template (element_class,
262 gst_static_pad_template_get (&subtitlesink_templ));
263 gst_element_class_add_pad_template (element_class,
264 gst_static_pad_template_get (&src_templ));
265 gst_element_class_set_details_simple (element_class, "Matroska muxer",
267 "Muxes video/audio/subtitle streams into a matroska stream",
268 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
270 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
275 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
277 GObjectClass *gobject_class;
278 GstElementClass *gstelement_class;
280 gobject_class = (GObjectClass *) klass;
281 gstelement_class = (GstElementClass *) klass;
283 gobject_class->finalize = gst_matroska_mux_finalize;
285 gobject_class->get_property = gst_matroska_mux_get_property;
286 gobject_class->set_property = gst_matroska_mux_set_property;
288 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
289 g_param_spec_string ("writing-app", "Writing application.",
290 "The name the application that creates the matroska file.",
291 NULL, G_PARAM_READWRITE));
292 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
293 g_param_spec_int ("version", "Matroska version",
294 "This parameter determines what matroska features can be used.",
295 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
297 gstelement_class->change_state =
298 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
299 gstelement_class->request_new_pad =
300 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
301 gstelement_class->release_pad =
302 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
307 * gst_matroska_mux_init:
308 * @mux: #GstMatroskaMux that should be initialized.
309 * @g_class: Class of the muxer.
311 * Matroska muxer constructor.
314 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
316 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
317 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
318 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
320 mux->collect = gst_collect_pads_new ();
321 gst_collect_pads_set_function (mux->collect,
322 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
325 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
327 /* property defaults */
328 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
329 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
331 /* initialize internal variables */
333 mux->num_streams = 0;
334 mux->num_a_streams = 0;
335 mux->num_t_streams = 0;
336 mux->num_v_streams = 0;
338 /* initialize remaining variables */
339 gst_matroska_mux_reset (GST_ELEMENT (mux));
344 * gst_matroska_mux_finalize:
345 * @object: #GstMatroskaMux that should be finalized.
347 * Finalize matroska muxer.
350 gst_matroska_mux_finalize (GObject * object)
352 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
354 gst_object_unref (mux->collect);
355 gst_object_unref (mux->ebml_write);
356 if (mux->writing_app)
357 g_free (mux->writing_app);
359 G_OBJECT_CLASS (parent_class)->finalize (object);
364 * gst_matroska_mux_create_uid:
366 * Generate new unused track UID.
368 * Returns: New track UID.
371 gst_matroska_mux_create_uid (void)
378 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
383 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
384 for (i = 0; i < used_uids->len; i++) {
385 if (g_array_index (used_uids, guint64, i) == uid) {
390 g_array_append_val (used_uids, uid);
393 G_UNLOCK (used_uids);
399 * gst_matroska_pad_reset:
400 * @collect_pad: the #GstMatroskaPad
402 * Reset and/or release resources of a matroska collect pad.
405 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
408 GstMatroskaTrackType type = 0;
410 /* free track information */
411 if (collect_pad->track != NULL) {
412 /* retrieve for optional later use */
413 name = collect_pad->track->name;
414 type = collect_pad->track->type;
415 /* extra for video */
416 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
417 GstMatroskaTrackVideoContext *ctx =
418 (GstMatroskaTrackVideoContext *) collect_pad->track;
420 if (ctx->dirac_unit) {
421 gst_buffer_unref (ctx->dirac_unit);
422 ctx->dirac_unit = NULL;
425 g_free (collect_pad->track->codec_id);
426 g_free (collect_pad->track->codec_name);
428 g_free (collect_pad->track->name);
429 g_free (collect_pad->track->language);
430 g_free (collect_pad->track->codec_priv);
431 g_free (collect_pad->track);
432 collect_pad->track = NULL;
435 /* free cached buffer */
436 if (collect_pad->buffer != NULL) {
437 gst_buffer_unref (collect_pad->buffer);
438 collect_pad->buffer = NULL;
441 if (!full && type != 0) {
442 GstMatroskaTrackContext *context;
444 /* create a fresh context */
446 case GST_MATROSKA_TRACK_TYPE_VIDEO:
447 context = (GstMatroskaTrackContext *)
448 g_new0 (GstMatroskaTrackVideoContext, 1);
450 case GST_MATROSKA_TRACK_TYPE_AUDIO:
451 context = (GstMatroskaTrackContext *)
452 g_new0 (GstMatroskaTrackAudioContext, 1);
454 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
455 context = (GstMatroskaTrackContext *)
456 g_new0 (GstMatroskaTrackSubtitleContext, 1);
459 g_assert_not_reached ();
463 context->type = type;
464 context->name = name;
465 /* TODO: check default values for the context */
466 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
467 collect_pad->track = context;
468 collect_pad->buffer = NULL;
469 collect_pad->duration = 0;
470 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
471 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
476 * gst_matroska_pad_free:
477 * @collect_pad: the #GstMatroskaPad
479 * Release resources of a matroska collect pad.
482 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
484 gst_matroska_pad_reset (collect_pad, TRUE);
489 * gst_matroska_mux_reset:
490 * @element: #GstMatroskaMux that should be reseted.
492 * Reset matroska muxer back to initial state.
495 gst_matroska_mux_reset (GstElement * element)
497 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
500 /* reset EBML write */
501 gst_ebml_write_reset (mux->ebml_write);
504 mux->state = GST_MATROSKA_MUX_STATE_START;
506 /* clean up existing streams */
508 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
509 GstMatroskaPad *collect_pad;
511 collect_pad = (GstMatroskaPad *) walk->data;
513 /* reset collect pad to pristine state */
514 gst_matroska_pad_reset (collect_pad, FALSE);
518 mux->num_indexes = 0;
523 mux->time_scale = GST_MSECOND;
528 mux->cluster_time = 0;
529 mux->cluster_pos = 0;
532 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
536 * gst_matroska_mux_handle_src_event:
537 * @pad: Pad which received the event.
538 * @event: Received event.
540 * handle events - copied from oggmux without understanding
542 * Returns: #TRUE on success.
545 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
549 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
553 /* disable seeking for now */
559 return gst_pad_event_default (pad, event);
563 * gst_matroska_mux_handle_sink_event:
564 * @pad: Pad which received the event.
565 * @event: Received event.
567 * handle events - informational ones like tags
569 * Returns: #TRUE on success.
572 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
574 GstMatroskaTrackContext *context;
575 GstMatroskaPad *collect_pad;
580 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
582 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
583 switch (GST_EVENT_TYPE (event)) {
587 GST_DEBUG_OBJECT (mux, "received tag event");
588 gst_event_parse_tag (event, &list);
590 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
591 g_assert (collect_pad);
592 context = collect_pad->track;
595 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
596 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
597 const gchar *lang_code;
599 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
601 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
602 context->language = g_strdup (lang_code);
604 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
609 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
610 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
613 case GST_EVENT_NEWSEGMENT:
614 /* We don't support NEWSEGMENT events */
616 gst_event_unref (event);
622 /* now GstCollectPads can take care of the rest, e.g. EOS */
624 ret = mux->collect_event (pad, event);
625 gst_object_unref (mux);
632 * gst_matroska_mux_video_pad_setcaps:
633 * @pad: Pad which got the caps.
636 * Setcaps function for video sink pad.
638 * Returns: #TRUE on success.
641 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
643 GstMatroskaTrackContext *context = NULL;
644 GstMatroskaTrackVideoContext *videocontext;
646 GstMatroskaPad *collect_pad;
647 GstStructure *structure;
648 const gchar *mimetype;
649 const GValue *value = NULL;
650 const GstBuffer *codec_buf = NULL;
651 gint width, height, pixel_width, pixel_height;
654 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
657 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
658 g_assert (collect_pad);
659 context = collect_pad->track;
661 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
662 videocontext = (GstMatroskaTrackVideoContext *) context;
664 /* gst -> matroska ID'ing */
665 structure = gst_caps_get_structure (caps, 0);
667 mimetype = gst_structure_get_name (structure);
669 if (!strcmp (mimetype, "video/x-theora")) {
670 /* we'll extract the details later from the theora identification header */
674 /* get general properties */
675 /* spec says it is mandatory */
676 if (!gst_structure_get_int (structure, "width", &width) ||
677 !gst_structure_get_int (structure, "height", &height))
680 videocontext->pixel_width = width;
681 videocontext->pixel_height = height;
682 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
684 context->default_duration =
685 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
686 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
687 GST_TIME_ARGS (context->default_duration));
689 context->default_duration = 0;
691 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
692 &pixel_width, &pixel_height)) {
693 if (pixel_width > pixel_height) {
694 videocontext->display_width = width * pixel_width / pixel_height;
695 videocontext->display_height = height;
696 } else if (pixel_width < pixel_height) {
697 videocontext->display_width = width;
698 videocontext->display_height = height * pixel_height / pixel_width;
700 videocontext->display_width = 0;
701 videocontext->display_height = 0;
704 videocontext->display_width = 0;
705 videocontext->display_height = 0;
710 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
711 videocontext->fourcc = 0;
713 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
714 * data and other settings
718 /* extract codec_data, may turn out needed */
719 value = gst_structure_get_value (structure, "codec_data");
721 codec_buf = gst_value_get_buffer (value);
724 if (!strcmp (mimetype, "video/x-raw-yuv")) {
725 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
726 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
727 } else if (!strcmp (mimetype, "image/jpeg")) {
728 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
729 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
730 ||!strcmp (mimetype, "video/x-huffyuv")
731 || !strcmp (mimetype, "video/x-divx")
732 || !strcmp (mimetype, "video/x-dv")
733 || !strcmp (mimetype, "video/x-h263")
734 || !strcmp (mimetype, "video/x-msmpeg")
735 || !strcmp (mimetype, "video/x-wmv")) {
736 BITMAPINFOHEADER *bih;
737 gint size = sizeof (BITMAPINFOHEADER);
740 if (!strcmp (mimetype, "video/x-xvid"))
741 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
742 else if (!strcmp (mimetype, "video/x-huffyuv"))
743 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
744 else if (!strcmp (mimetype, "video/x-dv"))
745 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
746 else if (!strcmp (mimetype, "video/x-h263"))
747 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
748 else if (!strcmp (mimetype, "video/x-divx")) {
751 gst_structure_get_int (structure, "divxversion", &divxversion);
752 switch (divxversion) {
754 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
757 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
760 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
763 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
766 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
767 switch (msmpegversion) {
769 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
772 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
778 } else if (!strcmp (mimetype, "video/x-wmv")) {
781 if (gst_structure_get_fourcc (structure, "format", &format)) {
783 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
784 if (wmvversion == 2) {
785 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
786 } else if (wmvversion == 1) {
787 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
788 } else if (wmvversion == 3) {
789 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
797 bih = g_new0 (BITMAPINFOHEADER, 1);
798 GST_WRITE_UINT32_LE (&bih->bi_size, size);
799 GST_WRITE_UINT32_LE (&bih->bi_width, videocontext->pixel_width);
800 GST_WRITE_UINT32_LE (&bih->bi_height, videocontext->pixel_height);
801 GST_WRITE_UINT32_LE (&bih->bi_compression, fourcc);
802 GST_WRITE_UINT16_LE (&bih->bi_planes, (guint16) 1);
803 GST_WRITE_UINT16_LE (&bih->bi_bit_count, (guint16) 24);
804 GST_WRITE_UINT32_LE (&bih->bi_size_image, videocontext->pixel_width *
805 videocontext->pixel_height * 3);
807 /* process codec private/initialization data, if any */
809 size += GST_BUFFER_SIZE (codec_buf);
810 bih = g_realloc (bih, size);
811 GST_WRITE_UINT32_LE (&bih->bi_size, size);
812 memcpy ((guint8 *) bih + sizeof (BITMAPINFOHEADER),
813 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
816 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
817 context->codec_priv = (gpointer) bih;
818 context->codec_priv_size = size;
819 } else if (!strcmp (mimetype, "video/x-h264")) {
820 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
822 if (context->codec_priv != NULL) {
823 g_free (context->codec_priv);
824 context->codec_priv = NULL;
825 context->codec_priv_size = 0;
828 /* Create avcC header */
829 if (codec_buf != NULL) {
830 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
831 context->codec_priv = g_malloc0 (context->codec_priv_size);
832 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
833 context->codec_priv_size);
835 } else if (!strcmp (mimetype, "video/x-theora")) {
836 const GValue *streamheader;
838 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
840 if (context->codec_priv != NULL) {
841 g_free (context->codec_priv);
842 context->codec_priv = NULL;
843 context->codec_priv_size = 0;
846 streamheader = gst_structure_get_value (structure, "streamheader");
847 if (!theora_streamheader_to_codecdata (streamheader, context)) {
848 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
849 ("theora stream headers missing or malformed"));
852 } else if (!strcmp (mimetype, "video/x-dirac")) {
853 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
854 } else if (!strcmp (mimetype, "video/mpeg")) {
857 gst_structure_get_int (structure, "mpegversion", &mpegversion);
858 switch (mpegversion) {
860 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
863 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
866 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
872 /* global headers may be in codec data */
873 if (codec_buf != NULL) {
874 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
875 context->codec_priv = g_malloc0 (context->codec_priv_size);
876 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
877 context->codec_priv_size);
879 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
881 /* can only make it here if preceding case verified it was version 3 */
882 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
883 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
885 const GValue *mdpr_data;
887 gst_structure_get_int (structure, "rmversion", &rmversion);
890 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
896 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
905 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
906 if (mdpr_data != NULL) {
907 guint8 *priv_data = NULL;
908 guint priv_data_size = 0;
910 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
912 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
913 priv_data = g_malloc0 (priv_data_size);
915 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
917 context->codec_priv = priv_data;
918 context->codec_priv_size = priv_data_size;
927 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
928 GST_PAD_NAME (pad), caps);
933 /* N > 0 to expect a particular number of headers, negative if the
934 number of headers is variable */
936 xiphN_streamheader_to_codecdata (const GValue * streamheader,
937 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
939 GstBuffer **buf = NULL;
942 guint bufi, i, offset, priv_data_size;
944 if (streamheader == NULL)
945 goto no_stream_headers;
947 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
950 bufarr = g_value_peek_pointer (streamheader);
951 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
953 if (N > 0 && bufarr->len != N)
956 context->xiph_headers_to_skip = bufarr->len;
958 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
959 for (i = 0; i < bufarr->len; i++) {
960 GValue *bufval = &g_array_index (bufarr, GValue, i);
962 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
964 goto wrong_content_type;
967 buf[i] = g_value_peek_pointer (bufval);
971 if (bufarr->len > 0) {
972 for (i = 0; i < bufarr->len - 1; i++) {
973 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
977 for (i = 0; i < bufarr->len; ++i) {
978 priv_data_size += GST_BUFFER_SIZE (buf[i]);
981 priv_data = g_malloc0 (priv_data_size);
983 priv_data[0] = bufarr->len - 1;
986 if (bufarr->len > 0) {
987 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
988 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
989 priv_data[offset++] = 0xff;
991 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
995 for (i = 0; i < bufarr->len; ++i) {
996 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
997 GST_BUFFER_SIZE (buf[i]));
998 offset += GST_BUFFER_SIZE (buf[i]);
1001 context->codec_priv = priv_data;
1002 context->codec_priv_size = priv_data_size;
1005 *p_buf0 = gst_buffer_ref (buf[0]);
1014 GST_WARNING ("required streamheaders missing in sink caps!");
1019 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1020 G_VALUE_TYPE_NAME (streamheader));
1025 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1030 GST_WARNING ("streamheaders array does not contain GstBuffers");
1036 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1037 GstMatroskaTrackContext * context)
1039 GstBuffer *buf0 = NULL;
1041 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1044 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1045 GST_WARNING ("First vorbis header too small, ignoring");
1047 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1048 GstMatroskaTrackAudioContext *audiocontext;
1051 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1052 audiocontext = (GstMatroskaTrackAudioContext *) context;
1053 audiocontext->channels = GST_READ_UINT8 (hdr);
1054 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1059 gst_buffer_unref (buf0);
1065 theora_streamheader_to_codecdata (const GValue * streamheader,
1066 GstMatroskaTrackContext * context)
1068 GstBuffer *buf0 = NULL;
1070 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1073 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1074 GST_WARNING ("First theora header too small, ignoring");
1075 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1076 GST_WARNING ("First header not a theora identification header, ignoring");
1078 GstMatroskaTrackVideoContext *videocontext;
1079 guint fps_num, fps_denom, par_num, par_denom;
1082 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1084 videocontext = (GstMatroskaTrackVideoContext *) context;
1085 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1086 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1087 hdr += 3 + 3 + 1 + 1;
1088 fps_num = GST_READ_UINT32_BE (hdr);
1089 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1090 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1091 fps_denom, fps_num);
1093 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1094 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1095 if (par_num > 0 && par_num > 0) {
1096 if (par_num > par_denom) {
1097 videocontext->display_width =
1098 videocontext->pixel_width * par_num / par_denom;
1099 videocontext->display_height = videocontext->pixel_height;
1100 } else if (par_num < par_denom) {
1101 videocontext->display_width = videocontext->pixel_width;
1102 videocontext->display_height =
1103 videocontext->pixel_height * par_denom / par_num;
1105 videocontext->display_width = 0;
1106 videocontext->display_height = 0;
1109 videocontext->display_width = 0;
1110 videocontext->display_height = 0;
1116 gst_buffer_unref (buf0);
1122 kate_streamheader_to_codecdata (const GValue * streamheader,
1123 GstMatroskaTrackContext * context)
1125 GstBuffer *buf0 = NULL;
1127 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1130 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1131 GST_WARNING ("First kate header too small, ignoring");
1132 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1133 GST_WARNING ("First header not a kate identification header, ignoring");
1137 gst_buffer_unref (buf0);
1143 flac_streamheader_to_codecdata (const GValue * streamheader,
1144 GstMatroskaTrackContext * context)
1151 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1152 GST_WARNING ("No or invalid streamheader field in the caps");
1156 bufarr = g_value_peek_pointer (streamheader);
1157 if (bufarr->len < 2) {
1158 GST_WARNING ("Too few headers in streamheader field");
1162 context->xiph_headers_to_skip = bufarr->len + 1;
1164 bufval = &g_array_index (bufarr, GValue, 0);
1165 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1166 GST_WARNING ("streamheaders array does not contain GstBuffers");
1170 buffer = g_value_peek_pointer (bufval);
1172 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1173 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1174 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1175 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1176 GST_WARNING ("Invalid streamheader for FLAC");
1180 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1181 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1182 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1183 GST_BUFFER_SIZE (buffer) - 9);
1185 for (i = 1; i < bufarr->len; i++) {
1186 bufval = &g_array_index (bufarr, GValue, i);
1188 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1189 g_free (context->codec_priv);
1190 context->codec_priv = NULL;
1191 context->codec_priv_size = 0;
1192 GST_WARNING ("streamheaders array does not contain GstBuffers");
1196 buffer = g_value_peek_pointer (bufval);
1198 context->codec_priv =
1199 g_realloc (context->codec_priv,
1200 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1201 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1202 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1203 context->codec_priv_size =
1204 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1211 speex_streamheader_to_codecdata (const GValue * streamheader,
1212 GstMatroskaTrackContext * context)
1218 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1219 GST_WARNING ("No or invalid streamheader field in the caps");
1223 bufarr = g_value_peek_pointer (streamheader);
1224 if (bufarr->len != 2) {
1225 GST_WARNING ("Too few headers in streamheader field");
1229 context->xiph_headers_to_skip = bufarr->len + 1;
1231 bufval = &g_array_index (bufarr, GValue, 0);
1232 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1233 GST_WARNING ("streamheaders array does not contain GstBuffers");
1237 buffer = g_value_peek_pointer (bufval);
1239 if (GST_BUFFER_SIZE (buffer) < 80
1240 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1241 GST_WARNING ("Invalid streamheader for Speex");
1245 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1246 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1247 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1248 GST_BUFFER_SIZE (buffer));
1250 bufval = &g_array_index (bufarr, GValue, 1);
1252 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1253 g_free (context->codec_priv);
1254 context->codec_priv = NULL;
1255 context->codec_priv_size = 0;
1256 GST_WARNING ("streamheaders array does not contain GstBuffers");
1260 buffer = g_value_peek_pointer (bufval);
1262 context->codec_priv =
1263 g_realloc (context->codec_priv,
1264 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1265 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1266 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1267 context->codec_priv_size =
1268 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1274 aac_codec_data_to_codec_id (const GstBuffer * buf)
1279 /* default to MAIN */
1282 if (GST_BUFFER_SIZE (buf) >= 2) {
1283 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1301 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1310 * gst_matroska_mux_audio_pad_setcaps:
1311 * @pad: Pad which got the caps.
1314 * Setcaps function for audio sink pad.
1316 * Returns: #TRUE on success.
1319 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1321 GstMatroskaTrackContext *context = NULL;
1322 GstMatroskaTrackAudioContext *audiocontext;
1323 GstMatroskaMux *mux;
1324 GstMatroskaPad *collect_pad;
1325 const gchar *mimetype;
1326 gint samplerate = 0, channels = 0;
1327 GstStructure *structure;
1328 const GValue *codec_data = NULL;
1329 const GstBuffer *buf = NULL;
1330 const gchar *stream_format = NULL;
1332 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1335 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1336 g_assert (collect_pad);
1337 context = collect_pad->track;
1339 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1340 audiocontext = (GstMatroskaTrackAudioContext *) context;
1342 structure = gst_caps_get_structure (caps, 0);
1343 mimetype = gst_structure_get_name (structure);
1346 gst_structure_get_int (structure, "rate", &samplerate);
1347 gst_structure_get_int (structure, "channels", &channels);
1349 audiocontext->samplerate = samplerate;
1350 audiocontext->channels = channels;
1351 audiocontext->bitdepth = 0;
1352 context->default_duration = 0;
1354 codec_data = gst_structure_get_value (structure, "codec_data");
1356 buf = gst_value_get_buffer (codec_data);
1358 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1359 * data and other settings
1363 if (!strcmp (mimetype, "audio/mpeg")) {
1364 gint mpegversion = 0;
1366 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1367 switch (mpegversion) {
1373 gst_structure_get_int (structure, "layer", &layer);
1375 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1376 GST_WARNING_OBJECT (mux,
1377 "Unable to determine MPEG audio version, assuming 1");
1383 else if (layer == 2)
1385 else if (version == 2)
1390 context->default_duration =
1391 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1395 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1398 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1401 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1410 stream_format = gst_structure_get_string (structure, "stream-format");
1411 /* check this is raw aac */
1412 if (stream_format) {
1413 if (strcmp (stream_format, "raw") != 0) {
1414 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1418 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1423 if (mpegversion == 2)
1425 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1426 aac_codec_data_to_codec_id (buf));
1427 else if (mpegversion == 4)
1429 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1430 aac_codec_data_to_codec_id (buf));
1432 g_assert_not_reached ();
1434 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1441 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1443 gint endianness = G_LITTLE_ENDIAN;
1444 gboolean signedness = TRUE;
1446 if (!gst_structure_get_int (structure, "width", &width) ||
1447 !gst_structure_get_int (structure, "depth", &depth) ||
1448 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1449 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1454 !gst_structure_get_int (structure, "endianness", &endianness)) {
1455 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1459 if (width != depth) {
1460 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1464 /* FIXME: where is this spec'ed out? (tpm) */
1465 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1466 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1470 audiocontext->bitdepth = depth;
1471 if (endianness == G_BIG_ENDIAN)
1472 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1474 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1476 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1479 if (!gst_structure_get_int (structure, "width", &width)) {
1480 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1484 audiocontext->bitdepth = width;
1485 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1487 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1488 const GValue *streamheader;
1490 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1492 if (context->codec_priv != NULL) {
1493 g_free (context->codec_priv);
1494 context->codec_priv = NULL;
1495 context->codec_priv_size = 0;
1498 streamheader = gst_structure_get_value (structure, "streamheader");
1499 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1500 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1501 ("vorbis stream headers missing or malformed"));
1504 } else if (!strcmp (mimetype, "audio/x-flac")) {
1505 const GValue *streamheader;
1507 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1508 if (context->codec_priv != NULL) {
1509 g_free (context->codec_priv);
1510 context->codec_priv = NULL;
1511 context->codec_priv_size = 0;
1514 streamheader = gst_structure_get_value (structure, "streamheader");
1515 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1516 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1517 ("flac stream headers missing or malformed"));
1520 } else if (!strcmp (mimetype, "audio/x-speex")) {
1521 const GValue *streamheader;
1523 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1524 if (context->codec_priv != NULL) {
1525 g_free (context->codec_priv);
1526 context->codec_priv = NULL;
1527 context->codec_priv_size = 0;
1530 streamheader = gst_structure_get_value (structure, "streamheader");
1531 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1532 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1533 ("speex stream headers missing or malformed"));
1536 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1537 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1538 } else if (!strcmp (mimetype, "audio/x-tta")) {
1541 /* TTA frame duration */
1542 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1544 gst_structure_get_int (structure, "width", &width);
1545 audiocontext->bitdepth = width;
1546 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1548 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1550 const GValue *mdpr_data;
1552 gst_structure_get_int (structure, "raversion", &raversion);
1553 switch (raversion) {
1555 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1558 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1561 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1567 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1568 if (mdpr_data != NULL) {
1569 guint8 *priv_data = NULL;
1570 guint priv_data_size = 0;
1572 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1574 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1575 priv_data = g_malloc0 (priv_data_size);
1577 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1579 context->codec_priv = priv_data;
1580 context->codec_priv_size = priv_data_size;
1583 } else if (!strcmp (mimetype, "audio/x-wma")) {
1585 guint codec_priv_size;
1592 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1593 || !gst_structure_get_int (structure, "block_align", &block_align)
1594 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1595 || samplerate == 0 || channels == 0) {
1596 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1597 "channels/rate on WMA caps");
1601 switch (wmaversion) {
1603 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1606 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1609 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1612 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1616 if (gst_structure_get_int (structure, "depth", &depth))
1617 audiocontext->bitdepth = depth;
1619 codec_priv_size = WAVEFORMATEX_SIZE;
1621 codec_priv_size += GST_BUFFER_SIZE (buf);
1623 /* serialize waveformatex structure */
1624 codec_priv = g_malloc0 (codec_priv_size);
1625 GST_WRITE_UINT16_LE (codec_priv, format);
1626 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1627 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1628 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1629 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1630 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1632 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1634 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1636 /* process codec private/initialization data, if any */
1638 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1639 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1642 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1643 context->codec_priv = (gpointer) codec_priv;
1644 context->codec_priv_size = codec_priv_size;
1652 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1653 GST_PAD_NAME (pad), caps);
1660 * gst_matroska_mux_subtitle_pad_setcaps:
1661 * @pad: Pad which got the caps.
1664 * Setcaps function for subtitle sink pad.
1666 * Returns: #TRUE on success.
1669 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1672 * Consider this as boilerplate code for now. There is
1673 * no single subtitle creation element in GStreamer,
1674 * neither do I know how subtitling works at all. */
1676 /* There is now (at least) one such alement (kateenc), and I'm going
1677 to handle it here and claim it works when it can be piped back
1678 through GStreamer and VLC */
1680 GstMatroskaTrackContext *context = NULL;
1681 GstMatroskaTrackSubtitleContext *scontext;
1682 GstMatroskaMux *mux;
1683 GstMatroskaPad *collect_pad;
1684 const gchar *mimetype;
1685 GstStructure *structure;
1687 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1690 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1691 g_assert (collect_pad);
1692 context = collect_pad->track;
1694 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1695 scontext = (GstMatroskaTrackSubtitleContext *) context;
1697 structure = gst_caps_get_structure (caps, 0);
1698 mimetype = gst_structure_get_name (structure);
1701 scontext->check_utf8 = 1;
1702 scontext->invalid_utf8 = 0;
1703 context->default_duration = 0;
1705 /* TODO: - other format than Kate */
1707 if (!strcmp (mimetype, "subtitle/x-kate")) {
1708 const GValue *streamheader;
1710 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1712 if (context->codec_priv != NULL) {
1713 g_free (context->codec_priv);
1714 context->codec_priv = NULL;
1715 context->codec_priv_size = 0;
1718 streamheader = gst_structure_get_value (structure, "streamheader");
1719 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1720 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1721 ("kate stream headers missing or malformed"));
1732 * gst_matroska_mux_request_new_pad:
1733 * @element: #GstMatroskaMux.
1734 * @templ: #GstPadTemplate.
1735 * @pad_name: New pad name.
1737 * Request pad function for sink templates.
1739 * Returns: New #GstPad.
1742 gst_matroska_mux_request_new_pad (GstElement * element,
1743 GstPadTemplate * templ, const gchar * pad_name)
1745 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1746 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1747 GstMatroskaPad *collect_pad;
1748 GstPad *newpad = NULL;
1750 GstPadSetCapsFunction setcapsfunc = NULL;
1751 GstMatroskaTrackContext *context = NULL;
1753 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1754 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1755 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1756 context = (GstMatroskaTrackContext *)
1757 g_new0 (GstMatroskaTrackAudioContext, 1);
1758 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1759 context->name = g_strdup ("Audio");
1760 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1761 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1762 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1763 context = (GstMatroskaTrackContext *)
1764 g_new0 (GstMatroskaTrackVideoContext, 1);
1765 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1766 context->name = g_strdup ("Video");
1767 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1768 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1769 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1770 context = (GstMatroskaTrackContext *)
1771 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1772 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1773 context->name = g_strdup ("Subtitle");
1775 GST_WARNING_OBJECT (mux, "This is not our template!");
1779 newpad = gst_pad_new_from_template (templ, name);
1781 collect_pad = (GstMatroskaPad *)
1782 gst_collect_pads_add_pad_full (mux->collect, newpad,
1783 sizeof (GstMatroskaPad),
1784 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1786 collect_pad->track = context;
1787 gst_matroska_pad_reset (collect_pad, FALSE);
1789 /* FIXME: hacked way to override/extend the event function of
1790 * GstCollectPads; because it sets its own event function giving the
1791 * element no access to events.
1792 * TODO GstCollectPads should really give its 'users' a clean chance to
1793 * properly handle events that are not meant for collectpads itself.
1794 * Perhaps a callback or so, though rejected (?) in #340060.
1795 * This would allow (clean) transcoding of info from demuxer/streams
1796 * to another muxer */
1797 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1798 gst_pad_set_event_function (newpad,
1799 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1801 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1802 gst_pad_set_active (newpad, TRUE);
1803 gst_element_add_pad (element, newpad);
1810 * gst_matroska_mux_release_pad:
1811 * @element: #GstMatroskaMux.
1812 * @pad: Pad to release.
1814 * Release a previously requested pad.
1817 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1819 GstMatroskaMux *mux;
1822 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1824 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1825 GstCollectData *cdata = (GstCollectData *) walk->data;
1826 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1828 if (cdata->pad == pad) {
1829 GstClockTime min_dur; /* observed minimum duration */
1831 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1832 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1833 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1834 if (collect_pad->duration < min_dur)
1835 collect_pad->duration = min_dur;
1838 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1839 mux->duration < collect_pad->duration)
1840 mux->duration = collect_pad->duration;
1846 gst_collect_pads_remove_pad (mux->collect, pad);
1847 if (gst_element_remove_pad (element, pad))
1853 * gst_matroska_mux_track_header:
1854 * @mux: #GstMatroskaMux
1855 * @context: Tack context.
1857 * Write a track header.
1860 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1861 GstMatroskaTrackContext * context)
1863 GstEbmlWrite *ebml = mux->ebml_write;
1866 /* TODO: check if everything necessary is written and check default values */
1868 /* track type goes before the type-specific stuff */
1869 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1870 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1872 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1873 gst_matroska_mux_create_uid ());
1874 if (context->default_duration) {
1875 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1876 context->default_duration);
1878 if (context->language) {
1879 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1883 /* type-specific stuff */
1884 switch (context->type) {
1885 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1886 GstMatroskaTrackVideoContext *videocontext =
1887 (GstMatroskaTrackVideoContext *) context;
1889 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1890 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1891 videocontext->pixel_width);
1892 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1893 videocontext->pixel_height);
1894 if (videocontext->display_width && videocontext->display_height) {
1895 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1896 videocontext->display_width);
1897 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1898 videocontext->display_height);
1900 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1902 if (videocontext->fourcc) {
1903 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1905 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1906 (gpointer) & fcc_le, 4);
1908 gst_ebml_write_master_finish (ebml, master);
1913 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1914 GstMatroskaTrackAudioContext *audiocontext =
1915 (GstMatroskaTrackAudioContext *) context;
1917 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1918 if (audiocontext->samplerate != 8000)
1919 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1920 audiocontext->samplerate);
1921 if (audiocontext->channels != 1)
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1923 audiocontext->channels);
1924 if (audiocontext->bitdepth) {
1925 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1926 audiocontext->bitdepth);
1928 gst_ebml_write_master_finish (ebml, master);
1934 /* doesn't need type-specific data */
1938 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1939 if (context->codec_priv)
1940 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1941 context->codec_priv, context->codec_priv_size);
1942 /* FIXME: until we have a nice way of getting the codecname
1943 * out of the caps, I'm not going to enable this. Too much
1944 * (useless, double, boring) work... */
1945 /* TODO: Use value from tags if any */
1946 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1947 context->codec_name); */
1948 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1953 * gst_matroska_mux_start:
1954 * @mux: #GstMatroskaMux
1956 * Start a new matroska file (write headers etc...)
1959 gst_matroska_mux_start (GstMatroskaMux * mux)
1961 GstEbmlWrite *ebml = mux->ebml_write;
1962 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1963 GST_MATROSKA_ID_TRACKS,
1964 GST_MATROSKA_ID_CUES,
1965 GST_MATROSKA_ID_TAGS,
1968 guint64 master, child;
1972 GstClockTime duration = 0;
1973 guint32 segment_uid[4];
1974 GTimeVal time = { 0, 0 };
1976 /* we start with a EBML header */
1977 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
1979 /* start a segment */
1981 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
1982 mux->segment_master = ebml->pos;
1984 /* the rest of the header is cached */
1985 gst_ebml_write_set_cache (ebml, 0x1000);
1987 /* seekhead (table of contents) - we set the positions later */
1988 mux->seekhead_pos = ebml->pos;
1989 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
1990 for (i = 0; seekhead_id[i] != 0; i++) {
1991 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
1992 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
1993 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
1994 gst_ebml_write_master_finish (ebml, child);
1996 gst_ebml_write_master_finish (ebml, master);
1999 mux->info_pos = ebml->pos;
2000 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2001 for (i = 0; i < 4; i++) {
2002 segment_uid[i] = g_random_int ();
2004 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2005 (guint8 *) segment_uid, 16);
2006 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2007 mux->duration_pos = ebml->pos;
2009 for (collected = mux->collect->data; collected;
2010 collected = g_slist_next (collected)) {
2011 GstMatroskaPad *collect_pad;
2012 GstFormat format = GST_FORMAT_TIME;
2014 gint64 trackduration;
2016 collect_pad = (GstMatroskaPad *) collected->data;
2017 thepad = collect_pad->collect.pad;
2019 /* Query the total length of the track. */
2020 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2021 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2022 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2023 GST_TIME_ARGS (trackduration));
2024 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2025 duration = (GstClockTime) trackduration;
2029 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2030 gst_guint64_to_gdouble (duration) /
2031 gst_guint64_to_gdouble (mux->time_scale));
2033 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2034 "GStreamer plugin version " PACKAGE_VERSION);
2035 if (mux->writing_app && mux->writing_app[0]) {
2036 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2038 g_get_current_time (&time);
2039 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2040 gst_ebml_write_master_finish (ebml, master);
2043 mux->tracks_pos = ebml->pos;
2044 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2046 for (collected = mux->collect->data; collected;
2047 collected = g_slist_next (collected)) {
2048 GstMatroskaPad *collect_pad;
2051 collect_pad = (GstMatroskaPad *) collected->data;
2052 thepad = collect_pad->collect.pad;
2054 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2055 collect_pad->track->codec_id != 0) {
2056 collect_pad->track->num = tracknum++;
2057 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2058 gst_matroska_mux_track_header (mux, collect_pad->track);
2059 gst_ebml_write_master_finish (ebml, child);
2062 gst_ebml_write_master_finish (ebml, master);
2064 /* lastly, flush the cache */
2065 gst_ebml_write_flush_cache (ebml);
2069 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2072 /* TODO: more sensible tag mappings */
2075 gchar *matroska_tagname;
2076 gchar *gstreamer_tagname;
2080 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2081 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2082 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2083 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2084 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2085 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2086 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2087 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2088 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2089 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2090 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2091 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2092 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2093 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2094 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2096 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2098 guint64 simpletag_master;
2100 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2101 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2102 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2104 if (strcmp (tagname_gst, tag) == 0) {
2105 GValue src = { 0, };
2108 if (!gst_tag_list_copy_value (&src, list, tag))
2110 if ((dest = gst_value_serialize (&src))) {
2112 simpletag_master = gst_ebml_write_master_start (ebml,
2113 GST_MATROSKA_ID_SIMPLETAG);
2114 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2115 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2116 gst_ebml_write_master_finish (ebml, simpletag_master);
2119 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2121 g_value_unset (&src);
2129 * gst_matroska_mux_finish:
2130 * @mux: #GstMatroskaMux
2132 * Finish a new matroska file (write index etc...)
2135 gst_matroska_mux_finish (GstMatroskaMux * mux)
2137 GstEbmlWrite *ebml = mux->ebml_write;
2139 guint64 duration = 0;
2141 const GstTagList *tags;
2143 /* finish last cluster */
2145 gst_ebml_write_master_finish (ebml, mux->cluster);
2149 if (mux->index != NULL) {
2151 guint64 master, pointentry_master, trackpos_master;
2153 mux->cues_pos = ebml->pos;
2154 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2155 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2157 for (n = 0; n < mux->num_indexes; n++) {
2158 GstMatroskaIndex *idx = &mux->index[n];
2160 pointentry_master = gst_ebml_write_master_start (ebml,
2161 GST_MATROSKA_ID_POINTENTRY);
2162 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2163 idx->time / mux->time_scale);
2164 trackpos_master = gst_ebml_write_master_start (ebml,
2165 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2166 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2167 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2168 idx->pos - mux->segment_master);
2169 gst_ebml_write_master_finish (ebml, trackpos_master);
2170 gst_ebml_write_master_finish (ebml, pointentry_master);
2173 gst_ebml_write_master_finish (ebml, master);
2174 gst_ebml_write_flush_cache (ebml);
2178 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2180 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2181 guint64 master_tags, master_tag;
2183 GST_DEBUG ("Writing tags");
2185 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2186 mux->tags_pos = ebml->pos;
2187 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2188 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2189 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2190 gst_ebml_write_master_finish (ebml, master_tag);
2191 gst_ebml_write_master_finish (ebml, master_tags);
2194 /* update seekhead. We know that:
2195 * - a seekhead contains 4 entries.
2196 * - order of entries is as above.
2197 * - a seekhead has a 4-byte header + 8-byte length
2198 * - each entry is 2-byte master, 2-byte ID pointer,
2199 * 2-byte length pointer, all 8/1-byte length, 4-
2200 * byte ID and 8-byte length pointer, where the
2201 * length pointer starts at 20.
2202 * - all entries are local to the segment (so pos - segment_master).
2203 * - so each entry is at 12 + 20 + num * 28. */
2204 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2205 mux->info_pos - mux->segment_master);
2206 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2207 mux->tracks_pos - mux->segment_master);
2208 if (mux->index != NULL) {
2209 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2210 mux->cues_pos - mux->segment_master);
2213 guint64 my_pos = ebml->pos;
2215 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2216 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2217 gst_ebml_write_seek (ebml, my_pos);
2220 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2221 mux->tags_pos - mux->segment_master);
2224 guint64 my_pos = ebml->pos;
2226 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2227 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2228 gst_ebml_write_seek (ebml, my_pos);
2231 /* update duration */
2232 /* first get the overall duration */
2233 /* a released track may have left a duration in here */
2234 duration = mux->duration;
2235 for (collected = mux->collect->data; collected;
2236 collected = g_slist_next (collected)) {
2237 GstMatroskaPad *collect_pad;
2238 GstClockTime min_duration; /* observed minimum duration */
2240 collect_pad = (GstMatroskaPad *) collected->data;
2242 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2243 " end ts %" GST_TIME_FORMAT, collect_pad,
2244 GST_TIME_ARGS (collect_pad->start_ts),
2245 GST_TIME_ARGS (collect_pad->end_ts));
2247 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2248 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2250 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2251 if (collect_pad->duration < min_duration)
2252 collect_pad->duration = min_duration;
2253 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2254 GST_TIME_ARGS (collect_pad->duration));
2257 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2258 duration < collect_pad->duration)
2259 duration = collect_pad->duration;
2261 if (duration != 0) {
2262 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2263 GST_TIME_ARGS (duration));
2264 pos = mux->ebml_write->pos;
2265 gst_ebml_write_seek (ebml, mux->duration_pos);
2266 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2267 gst_guint64_to_gdouble (duration) /
2268 gst_guint64_to_gdouble (mux->time_scale));
2269 gst_ebml_write_seek (ebml, pos);
2272 guint64 my_pos = ebml->pos;
2274 gst_ebml_write_seek (ebml, mux->duration_pos);
2275 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2276 gst_ebml_write_seek (ebml, my_pos);
2279 /* finish segment - this also writes element length */
2280 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2285 * gst_matroska_mux_best_pad:
2286 * @mux: #GstMatroskaMux
2287 * @popped: True if at least one buffer was popped from #GstCollectPads
2289 * Find a pad with the oldest data
2290 * (data from this pad should be written first).
2292 * Returns: Selected pad.
2294 static GstMatroskaPad *
2295 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2298 GstMatroskaPad *best = NULL;
2301 for (collected = mux->collect->data; collected;
2302 collected = g_slist_next (collected)) {
2303 GstMatroskaPad *collect_pad;
2305 collect_pad = (GstMatroskaPad *) collected->data;
2306 /* fetch a new buffer if needed */
2307 if (collect_pad->buffer == NULL) {
2308 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2309 (GstCollectData *) collect_pad);
2311 if (collect_pad->buffer != NULL)
2315 /* if we have a buffer check if it is better then the current best one */
2316 if (collect_pad->buffer != NULL) {
2317 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2318 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2319 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2320 GST_BUFFER_TIMESTAMP (best->buffer))) {
2330 * gst_matroska_mux_buffer_header:
2331 * @track: Track context.
2332 * @relative_timestamp: relative timestamp of the buffer
2333 * @flags: Buffer flags.
2335 * Create a buffer containing buffer header.
2337 * Returns: New buffer.
2340 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2341 gint16 relative_timestamp, int flags)
2345 hdr = gst_buffer_new_and_alloc (4);
2346 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2347 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2348 /* time relative to clustertime */
2349 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2352 GST_BUFFER_DATA (hdr)[3] = flags;
2357 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2358 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2359 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2362 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2363 GstMatroskaPad * collect_pad, GstBuffer * buf)
2365 GstMatroskaTrackVideoContext *ctx =
2366 (GstMatroskaTrackVideoContext *) collect_pad->track;
2367 const guint8 *data = GST_BUFFER_DATA (buf);
2368 guint size = GST_BUFFER_SIZE (buf);
2370 guint32 next_parse_offset;
2371 GstBuffer *ret = NULL;
2372 gboolean is_muxing_unit = FALSE;
2374 if (GST_BUFFER_SIZE (buf) < 13) {
2375 gst_buffer_unref (buf);
2379 /* Check if this buffer contains a picture or end-of-sequence packet */
2380 while (size >= 13) {
2381 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2382 gst_buffer_unref (buf);
2386 parse_code = GST_READ_UINT8 (data + 4);
2387 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2388 if (ctx->dirac_unit) {
2389 gst_buffer_unref (ctx->dirac_unit);
2390 ctx->dirac_unit = NULL;
2392 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2393 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2394 is_muxing_unit = TRUE;
2398 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2400 if (G_UNLIKELY (next_parse_offset == 0))
2403 data += next_parse_offset;
2404 size -= next_parse_offset;
2407 if (ctx->dirac_unit)
2408 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2410 ctx->dirac_unit = gst_buffer_ref (buf);
2412 if (is_muxing_unit) {
2413 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2414 ctx->dirac_unit = NULL;
2415 gst_buffer_copy_metadata (ret, buf,
2416 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2417 GST_BUFFER_COPY_CAPS);
2418 gst_buffer_unref (buf);
2420 gst_buffer_unref (buf);
2428 * gst_matroska_mux_write_data:
2429 * @mux: #GstMatroskaMux
2430 * @collect_pad: #GstMatroskaPad with the data
2432 * Write collected data (called from gst_matroska_mux_collected).
2434 * Returns: Result of the gst_pad_push issued to write the data.
2436 static GstFlowReturn
2437 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2439 GstEbmlWrite *ebml = mux->ebml_write;
2440 GstBuffer *buf, *hdr;
2442 gboolean write_duration;
2443 gint16 relative_timestamp;
2444 gint64 relative_timestamp64;
2445 guint64 block_duration;
2446 gboolean is_video_keyframe = FALSE;
2449 buf = collect_pad->buffer;
2450 collect_pad->buffer = NULL;
2452 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2453 if (collect_pad->track->xiph_headers_to_skip > 0) {
2454 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2455 gst_buffer_unref (buf);
2456 --collect_pad->track->xiph_headers_to_skip;
2460 /* for dirac we have to queue up everything up to a picture unit */
2461 if (collect_pad->track->codec_id != NULL &&
2462 strcmp (collect_pad->track->codec_id,
2463 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2464 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2469 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2470 * this would wreak havoc with time stored in matroska file */
2471 /* TODO: maybe calculate a timestamp by using the previous timestamp
2472 * and default duration */
2473 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2474 GST_WARNING_OBJECT (collect_pad->collect.pad,
2475 "Invalid buffer timestamp; dropping buffer");
2476 gst_buffer_unref (buf);
2480 /* set the timestamp for outgoing buffers */
2481 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2483 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2484 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2485 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2486 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2487 is_video_keyframe = TRUE;
2491 /* start a new cluster every two seconds or at keyframe */
2492 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2493 || is_video_keyframe) {
2495 gst_ebml_write_master_finish (ebml, mux->cluster);
2496 mux->cluster_pos = ebml->pos;
2498 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2499 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2500 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2501 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2506 mux->cluster_pos = ebml->pos;
2507 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2508 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2509 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2510 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2513 /* update duration of this track */
2514 if (GST_BUFFER_DURATION_IS_VALID (buf))
2515 collect_pad->duration += GST_BUFFER_DURATION (buf);
2517 /* We currently write an index entry for each keyframe in a
2518 * video track or one entry for each cluster in an audio track
2519 * for audio only files. This can be largely improved, such as doing
2520 * one for each keyframe or each second (for all-keyframe
2521 * streams), only the *first* video track. But that'll come later... */
2523 /* TODO: index is useful for every track, should contain the number of
2524 * the block in the cluster which contains the timestamp
2526 if (is_video_keyframe) {
2527 GstMatroskaIndex *idx;
2529 if (mux->num_indexes % 32 == 0) {
2530 mux->index = g_renew (GstMatroskaIndex, mux->index,
2531 mux->num_indexes + 32);
2533 idx = &mux->index[mux->num_indexes++];
2535 idx->pos = mux->cluster_pos;
2536 idx->time = GST_BUFFER_TIMESTAMP (buf);
2537 idx->track = collect_pad->track->num;
2538 } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2539 (mux->num_streams == 1)) {
2540 GstMatroskaIndex *idx;
2542 if (mux->num_indexes % 32 == 0) {
2543 mux->index = g_renew (GstMatroskaIndex, mux->index,
2544 mux->num_indexes + 32);
2546 idx = &mux->index[mux->num_indexes++];
2548 idx->pos = mux->cluster_pos;
2549 idx->time = GST_BUFFER_TIMESTAMP (buf);
2550 idx->track = collect_pad->track->num;
2553 /* Check if the duration differs from the default duration. */
2554 write_duration = FALSE;
2555 block_duration = GST_BUFFER_DURATION (buf);
2556 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2557 if (block_duration != collect_pad->track->default_duration) {
2558 write_duration = TRUE;
2562 /* write the block, for matroska v2 use SimpleBlock if possible
2563 * one slice (*breath*).
2564 * FIXME: Need to do correct lacing! */
2565 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2566 if (relative_timestamp64 >= 0) {
2567 /* round the timestamp */
2568 relative_timestamp64 += mux->time_scale / 2;
2570 /* round the timestamp */
2571 relative_timestamp64 -= mux->time_scale / 2;
2573 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2574 if (mux->matroska_version > 1 && !write_duration) {
2576 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2579 gst_matroska_mux_create_buffer_header (collect_pad->track,
2580 relative_timestamp, flags);
2581 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2582 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2583 gst_ebml_write_buffer (ebml, hdr);
2584 gst_ebml_write_buffer (ebml, buf);
2586 return gst_ebml_last_write_result (ebml);
2588 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2590 gst_matroska_mux_create_buffer_header (collect_pad->track,
2591 relative_timestamp, 0);
2592 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2593 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2594 gst_ebml_write_buffer (ebml, hdr);
2595 gst_ebml_write_buffer (ebml, buf);
2596 if (write_duration) {
2597 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2598 block_duration / mux->time_scale);
2600 gst_ebml_write_master_finish (ebml, blockgroup);
2601 return gst_ebml_last_write_result (ebml);
2607 * gst_matroska_mux_collected:
2608 * @pads: #GstCollectPads
2609 * @uuser_data: #GstMatroskaMux
2611 * Collectpads callback.
2613 * Returns: #GstFlowReturn
2615 static GstFlowReturn
2616 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2618 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2619 GstMatroskaPad *best;
2623 GST_DEBUG_OBJECT (mux, "Collected pads");
2625 /* start with a header */
2626 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2627 if (mux->collect->data == NULL) {
2628 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2629 ("No input streams configured"));
2630 return GST_FLOW_ERROR;
2632 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2633 gst_matroska_mux_start (mux);
2634 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2638 /* which stream to write from? */
2639 best = gst_matroska_mux_best_pad (mux, &popped);
2641 /* if there is no best pad, we have reached EOS */
2643 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2644 gst_matroska_mux_finish (mux);
2645 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2646 ret = GST_FLOW_UNEXPECTED;
2649 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2650 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2651 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2652 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2654 /* make note of first and last encountered timestamps, so we can calculate
2655 * the actual duration later when we send an updated header on eos */
2656 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2657 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2658 GstClockTime end_ts = start_ts;
2660 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2661 end_ts += GST_BUFFER_DURATION (best->buffer);
2662 else if (best->track->default_duration)
2663 end_ts += best->track->default_duration;
2665 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2666 best->end_ts = end_ts;
2668 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2669 start_ts < best->start_ts))
2670 best->start_ts = start_ts;
2673 /* write one buffer */
2674 ret = gst_matroska_mux_write_data (mux, best);
2675 } while (ret == GST_FLOW_OK && !popped);
2682 * gst_matroska_mux_change_state:
2683 * @element: #GstMatroskaMux
2684 * @transition: State change transition.
2686 * Change the muxer state.
2688 * Returns: #GstStateChangeReturn
2690 static GstStateChangeReturn
2691 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2693 GstStateChangeReturn ret;
2694 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2696 switch (transition) {
2697 case GST_STATE_CHANGE_NULL_TO_READY:
2699 case GST_STATE_CHANGE_READY_TO_PAUSED:
2700 gst_collect_pads_start (mux->collect);
2702 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2704 case GST_STATE_CHANGE_PAUSED_TO_READY:
2705 gst_collect_pads_stop (mux->collect);
2711 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2713 switch (transition) {
2714 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2716 case GST_STATE_CHANGE_PAUSED_TO_READY:
2717 gst_matroska_mux_reset (GST_ELEMENT (mux));
2719 case GST_STATE_CHANGE_READY_TO_NULL:
2729 gst_matroska_mux_set_property (GObject * object,
2730 guint prop_id, const GValue * value, GParamSpec * pspec)
2732 GstMatroskaMux *mux;
2734 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2735 mux = GST_MATROSKA_MUX (object);
2738 case ARG_WRITING_APP:
2739 if (!g_value_get_string (value)) {
2740 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2743 g_free (mux->writing_app);
2744 mux->writing_app = g_value_dup_string (value);
2746 case ARG_MATROSKA_VERSION:
2747 mux->matroska_version = g_value_get_int (value);
2750 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2756 gst_matroska_mux_get_property (GObject * object,
2757 guint prop_id, GValue * value, GParamSpec * pspec)
2759 GstMatroskaMux *mux;
2761 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2762 mux = GST_MATROSKA_MUX (object);
2765 case ARG_WRITING_APP:
2766 g_value_set_string (value, mux->writing_app);
2768 case ARG_MATROSKA_VERSION:
2769 g_value_set_int (value, mux->matroska_version);
2772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2778 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2780 return gst_element_register (plugin, "matroskamux",
2781 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);