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 "matroska-mux.h"
52 #include "matroska-ids.h"
54 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
55 #define GST_CAT_DEFAULT matroskamux_debug
64 #define DEFAULT_MATROSKA_VERSION 1
65 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
67 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
70 GST_STATIC_CAPS ("video/x-matroska")
73 #define COMMON_VIDEO_CAPS \
74 "width = (int) [ 16, 4096 ], " \
75 "height = (int) [ 16, 4096 ], " \
76 "framerate = (fraction) [ 0, MAX ]"
78 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
79 "width = (int) [ 16, 4096 ], " \
80 "height = (int) [ 16, 4096 ] "
83 * * require codec data, etc as needed
86 static GstStaticPadTemplate videosink_templ =
87 GST_STATIC_PAD_TEMPLATE ("video_%d",
90 GST_STATIC_CAPS ("video/mpeg, "
91 "mpegversion = (int) { 1, 2, 4 }, "
92 "systemstream = (boolean) false, "
93 COMMON_VIDEO_CAPS "; "
95 COMMON_VIDEO_CAPS "; "
97 COMMON_VIDEO_CAPS "; "
99 COMMON_VIDEO_CAPS "; "
101 COMMON_VIDEO_CAPS "; "
103 COMMON_VIDEO_CAPS "; "
105 COMMON_VIDEO_CAPS "; "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
112 COMMON_VIDEO_CAPS "; "
113 "video/x-pn-realvideo, "
114 "rmversion = (int) [1, 4], "
115 COMMON_VIDEO_CAPS "; "
117 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
118 COMMON_VIDEO_CAPS "; "
119 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
122 #define COMMON_AUDIO_CAPS \
123 "channels = (int) [ 1, MAX ], " \
124 "rate = (int) [ 1, MAX ]"
127 * * require codec data, etc as needed
129 static GstStaticPadTemplate audiosink_templ =
130 GST_STATIC_PAD_TEMPLATE ("audio_%d",
133 GST_STATIC_CAPS ("audio/mpeg, "
134 "mpegversion = (int) 1, "
135 "layer = (int) [ 1, 3 ], "
136 COMMON_AUDIO_CAPS "; "
138 "mpegversion = (int) { 2, 4 }, "
139 COMMON_AUDIO_CAPS "; "
141 COMMON_AUDIO_CAPS "; "
143 COMMON_AUDIO_CAPS "; "
145 COMMON_AUDIO_CAPS "; "
147 COMMON_AUDIO_CAPS "; "
151 "signed = (boolean) false, "
152 COMMON_AUDIO_CAPS ";"
156 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
157 "signed = (boolean) true, "
158 COMMON_AUDIO_CAPS ";"
162 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
163 "signed = (boolean) true, "
164 COMMON_AUDIO_CAPS ";"
168 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
169 "signed = (boolean) true, "
170 COMMON_AUDIO_CAPS ";"
171 "audio/x-raw-float, "
172 "width = (int) [ 32, 64 ], "
173 "endianness = (int) LITTLE_ENDIAN, "
174 COMMON_AUDIO_CAPS ";"
176 "width = (int) { 8, 16, 24 }, "
177 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
178 "audio/x-pn-realaudio, "
179 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS ";")
182 static GstStaticPadTemplate subtitlesink_templ =
183 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
186 GST_STATIC_CAPS_ANY);
188 static GArray *used_uids;
189 G_LOCK_DEFINE_STATIC (used_uids);
191 static void gst_matroska_mux_add_interfaces (GType type);
193 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
194 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
196 /* Matroska muxer destructor */
197 static void gst_matroska_mux_finalize (GObject * object);
199 /* Pads collected callback */
201 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
204 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
206 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
207 GstPadTemplate * templ, const gchar * name);
208 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
210 /* gst internal change state handler */
211 static GstStateChangeReturn
212 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
214 /* gobject bla bla */
215 static void gst_matroska_mux_set_property (GObject * object,
216 guint prop_id, const GValue * value, GParamSpec * pspec);
217 static void gst_matroska_mux_get_property (GObject * object,
218 guint prop_id, GValue * value, GParamSpec * pspec);
221 static void gst_matroska_mux_reset (GstElement * element);
224 static guint64 gst_matroska_mux_create_uid ();
226 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
227 GstMatroskaTrackContext * context);
228 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
229 GstMatroskaTrackContext * context);
230 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
231 GstMatroskaTrackContext * context);
232 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
233 GstMatroskaTrackContext * context);
234 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
235 GstMatroskaTrackContext * context);
238 gst_matroska_mux_add_interfaces (GType type)
240 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
242 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
246 gst_matroska_mux_base_init (gpointer g_class)
248 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
250 gst_element_class_add_pad_template (element_class,
251 gst_static_pad_template_get (&videosink_templ));
252 gst_element_class_add_pad_template (element_class,
253 gst_static_pad_template_get (&audiosink_templ));
254 gst_element_class_add_pad_template (element_class,
255 gst_static_pad_template_get (&subtitlesink_templ));
256 gst_element_class_add_pad_template (element_class,
257 gst_static_pad_template_get (&src_templ));
258 gst_element_class_set_details_simple (element_class, "Matroska muxer",
260 "Muxes video/audio/subtitle streams into a matroska stream",
261 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
263 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
268 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
270 GObjectClass *gobject_class;
271 GstElementClass *gstelement_class;
273 gobject_class = (GObjectClass *) klass;
274 gstelement_class = (GstElementClass *) klass;
276 gobject_class->finalize = gst_matroska_mux_finalize;
278 gobject_class->get_property = gst_matroska_mux_get_property;
279 gobject_class->set_property = gst_matroska_mux_set_property;
281 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
282 g_param_spec_string ("writing-app", "Writing application.",
283 "The name the application that creates the matroska file.",
284 NULL, G_PARAM_READWRITE));
285 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
286 g_param_spec_int ("version", "Matroska version",
287 "This parameter determines what matroska features can be used.",
288 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
290 gstelement_class->change_state =
291 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
292 gstelement_class->request_new_pad =
293 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
294 gstelement_class->release_pad =
295 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
300 * gst_matroska_mux_init:
301 * @mux: #GstMatroskaMux that should be initialized.
302 * @g_class: Class of the muxer.
304 * Matroska muxer constructor.
307 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
309 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
310 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
311 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
313 mux->collect = gst_collect_pads_new ();
314 gst_collect_pads_set_function (mux->collect,
315 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
318 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
320 /* property defaults */
321 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
322 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
324 /* initialize internal variables */
326 mux->num_streams = 0;
327 mux->num_a_streams = 0;
328 mux->num_t_streams = 0;
329 mux->num_v_streams = 0;
331 /* initialize remaining variables */
332 gst_matroska_mux_reset (GST_ELEMENT (mux));
337 * gst_matroska_mux_finalize:
338 * @object: #GstMatroskaMux that should be finalized.
340 * Finalize matroska muxer.
343 gst_matroska_mux_finalize (GObject * object)
345 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
347 gst_object_unref (mux->collect);
348 gst_object_unref (mux->ebml_write);
349 if (mux->writing_app)
350 g_free (mux->writing_app);
352 G_OBJECT_CLASS (parent_class)->finalize (object);
357 * gst_matroska_mux_create_uid:
359 * Generate new unused track UID.
361 * Returns: New track UID.
364 gst_matroska_mux_create_uid (void)
371 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
376 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
377 for (i = 0; i < used_uids->len; i++) {
378 if (g_array_index (used_uids, guint64, i) == uid) {
383 g_array_append_val (used_uids, uid);
386 G_UNLOCK (used_uids);
392 * gst_matroska_pad_reset:
393 * @collect_pad: the #GstMatroskaPad
395 * Reset and/or release resources of a matroska collect pad.
398 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
401 GstMatroskaTrackType type = 0;
403 /* free track information */
404 if (collect_pad->track != NULL) {
405 /* retrieve for optional later use */
406 name = collect_pad->track->name;
407 type = collect_pad->track->type;
408 /* extra for video */
409 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
410 GstMatroskaTrackVideoContext *ctx =
411 (GstMatroskaTrackVideoContext *) collect_pad->track;
413 if (ctx->dirac_unit) {
414 gst_buffer_unref (ctx->dirac_unit);
415 ctx->dirac_unit = NULL;
418 g_free (collect_pad->track->codec_id);
419 g_free (collect_pad->track->codec_name);
421 g_free (collect_pad->track->name);
422 g_free (collect_pad->track->language);
423 g_free (collect_pad->track->codec_priv);
424 g_free (collect_pad->track);
425 collect_pad->track = NULL;
428 /* free cached buffer */
429 if (collect_pad->buffer != NULL) {
430 gst_buffer_unref (collect_pad->buffer);
431 collect_pad->buffer = NULL;
434 if (!full && type != 0) {
435 GstMatroskaTrackContext *context;
437 /* create a fresh context */
439 case GST_MATROSKA_TRACK_TYPE_VIDEO:
440 context = (GstMatroskaTrackContext *)
441 g_new0 (GstMatroskaTrackVideoContext, 1);
443 case GST_MATROSKA_TRACK_TYPE_AUDIO:
444 context = (GstMatroskaTrackContext *)
445 g_new0 (GstMatroskaTrackAudioContext, 1);
447 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
448 context = (GstMatroskaTrackContext *)
449 g_new0 (GstMatroskaTrackSubtitleContext, 1);
452 g_assert_not_reached ();
456 context->type = type;
457 context->name = name;
458 /* TODO: check default values for the context */
459 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
460 collect_pad->track = context;
461 collect_pad->buffer = NULL;
462 collect_pad->duration = 0;
463 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
464 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
469 * gst_matroska_pad_free:
470 * @collect_pad: the #GstMatroskaPad
472 * Release resources of a matroska collect pad.
475 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
477 gst_matroska_pad_reset (collect_pad, TRUE);
482 * gst_matroska_mux_reset:
483 * @element: #GstMatroskaMux that should be reseted.
485 * Reset matroska muxer back to initial state.
488 gst_matroska_mux_reset (GstElement * element)
490 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
493 /* reset EBML write */
494 gst_ebml_write_reset (mux->ebml_write);
497 mux->state = GST_MATROSKA_MUX_STATE_START;
499 /* clean up existing streams */
501 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
502 GstMatroskaPad *collect_pad;
504 collect_pad = (GstMatroskaPad *) walk->data;
506 /* reset collect pad to pristine state */
507 gst_matroska_pad_reset (collect_pad, FALSE);
511 mux->num_indexes = 0;
516 mux->time_scale = GST_MSECOND;
521 mux->cluster_time = 0;
522 mux->cluster_pos = 0;
525 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
529 * gst_matroska_mux_handle_src_event:
530 * @pad: Pad which received the event.
531 * @event: Received event.
533 * handle events - copied from oggmux without understanding
535 * Returns: #TRUE on success.
538 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
542 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
546 /* disable seeking for now */
552 return gst_pad_event_default (pad, event);
556 * gst_matroska_mux_handle_sink_event:
557 * @pad: Pad which received the event.
558 * @event: Received event.
560 * handle events - informational ones like tags
562 * Returns: #TRUE on success.
565 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
567 GstMatroskaTrackContext *context;
568 GstMatroskaPad *collect_pad;
573 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
575 switch (GST_EVENT_TYPE (event)) {
577 GST_DEBUG_OBJECT (mux, "received tag event");
578 gst_event_parse_tag (event, &list);
580 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
581 g_assert (collect_pad);
582 context = collect_pad->track;
585 * strictly speaking, the incoming language code may only be 639-1, so not
586 * 639-2 according to matroska specs, but it will have to do for now */
587 gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &context->language);
589 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
590 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
592 case GST_EVENT_NEWSEGMENT:
593 /* We don't support NEWSEGMENT events */
595 gst_event_unref (event);
601 /* now GstCollectPads can take care of the rest, e.g. EOS */
603 ret = mux->collect_event (pad, event);
604 gst_object_unref (mux);
611 * gst_matroska_mux_video_pad_setcaps:
612 * @pad: Pad which got the caps.
615 * Setcaps function for video sink pad.
617 * Returns: #TRUE on success.
620 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
622 GstMatroskaTrackContext *context = NULL;
623 GstMatroskaTrackVideoContext *videocontext;
625 GstMatroskaPad *collect_pad;
626 GstStructure *structure;
627 const gchar *mimetype;
628 const GValue *value = NULL;
629 const GstBuffer *codec_buf = NULL;
630 gint width, height, pixel_width, pixel_height;
633 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
636 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
637 g_assert (collect_pad);
638 context = collect_pad->track;
640 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
641 videocontext = (GstMatroskaTrackVideoContext *) context;
643 /* gst -> matroska ID'ing */
644 structure = gst_caps_get_structure (caps, 0);
646 mimetype = gst_structure_get_name (structure);
648 if (!strcmp (mimetype, "video/x-theora")) {
649 /* we'll extract the details later from the theora identification header */
653 /* get general properties */
654 gst_structure_get_int (structure, "width", &width);
655 gst_structure_get_int (structure, "height", &height);
656 videocontext->pixel_width = width;
657 videocontext->pixel_height = height;
658 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
660 context->default_duration =
661 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
662 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
663 GST_TIME_ARGS (context->default_duration));
665 context->default_duration = 0;
667 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
668 &pixel_width, &pixel_height)) {
669 if (pixel_width > pixel_height) {
670 videocontext->display_width = width * pixel_width / pixel_height;
671 videocontext->display_height = height;
672 } else if (pixel_width < pixel_height) {
673 videocontext->display_width = width;
674 videocontext->display_height = height * pixel_height / pixel_width;
676 videocontext->display_width = 0;
677 videocontext->display_height = 0;
680 videocontext->display_width = 0;
681 videocontext->display_height = 0;
686 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
687 videocontext->fourcc = 0;
689 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
690 * data and other settings
694 /* extract codec_data, may turn out needed */
695 value = gst_structure_get_value (structure, "codec_data");
697 codec_buf = gst_value_get_buffer (value);
700 if (!strcmp (mimetype, "video/x-raw-yuv")) {
701 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
702 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
705 } else if (!strcmp (mimetype, "image/jpeg")) {
706 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
709 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
710 ||!strcmp (mimetype, "video/x-huffyuv")
711 || !strcmp (mimetype, "video/x-divx")
712 || !strcmp (mimetype, "video/x-dv")
713 || !strcmp (mimetype, "video/x-h263")
714 || !strcmp (mimetype, "video/x-msmpeg")
715 || !strcmp (mimetype, "video/x-wmv")) {
716 BITMAPINFOHEADER *bih;
717 gint size = sizeof (BITMAPINFOHEADER);
720 if (!strcmp (mimetype, "video/x-xvid"))
721 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
722 else if (!strcmp (mimetype, "video/x-huffyuv"))
723 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
724 else if (!strcmp (mimetype, "video/x-dv"))
725 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
726 else if (!strcmp (mimetype, "video/x-h263"))
727 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
728 else if (!strcmp (mimetype, "video/x-divx")) {
731 gst_structure_get_int (structure, "divxversion", &divxversion);
732 switch (divxversion) {
734 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
737 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
740 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
743 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
746 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
747 switch (msmpegversion) {
749 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
752 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
758 } else if (!strcmp (mimetype, "video/x-wmv")) {
761 GST_WARNING_OBJECT (mux, "WMV");
762 if (gst_structure_get_fourcc (structure, "format", &format)) {
764 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
765 if (wmvversion == 2) {
766 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
767 } else if (wmvversion == 1) {
768 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
769 } else if (wmvversion == 3) {
770 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
773 GST_WARNING_OBJECT (mux, "fourcc=%u", fourcc);
779 bih = g_new0 (BITMAPINFOHEADER, 1);
780 GST_WRITE_UINT32_LE (&bih->bi_size, size);
781 GST_WRITE_UINT32_LE (&bih->bi_width, videocontext->pixel_width);
782 GST_WRITE_UINT32_LE (&bih->bi_height, videocontext->pixel_height);
783 GST_WRITE_UINT32_LE (&bih->bi_compression, fourcc);
784 GST_WRITE_UINT16_LE (&bih->bi_planes, (guint16) 1);
785 GST_WRITE_UINT16_LE (&bih->bi_bit_count, (guint16) 24);
786 GST_WRITE_UINT32_LE (&bih->bi_size_image, videocontext->pixel_width *
787 videocontext->pixel_height * 3);
789 /* process codec private/initialization data, if any */
791 size += GST_BUFFER_SIZE (codec_buf);
792 bih = g_realloc (bih, size);
793 GST_WRITE_UINT32_LE (&bih->bi_size, size);
794 memcpy ((guint8 *) bih + sizeof (BITMAPINFOHEADER),
795 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
798 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
799 context->codec_priv = (gpointer) bih;
800 context->codec_priv_size = size;
803 } else if (!strcmp (mimetype, "video/x-h264")) {
804 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
806 if (context->codec_priv != NULL) {
807 g_free (context->codec_priv);
808 context->codec_priv = NULL;
809 context->codec_priv_size = 0;
812 /* Create avcC header */
813 if (codec_buf != NULL) {
814 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
815 context->codec_priv = g_malloc0 (context->codec_priv_size);
816 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
817 context->codec_priv_size);
821 } else if (!strcmp (mimetype, "video/x-theora")) {
822 const GValue *streamheader;
824 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
826 if (context->codec_priv != NULL) {
827 g_free (context->codec_priv);
828 context->codec_priv = NULL;
829 context->codec_priv_size = 0;
832 streamheader = gst_structure_get_value (structure, "streamheader");
833 if (!theora_streamheader_to_codecdata (streamheader, context)) {
834 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
835 ("theora stream headers missing or malformed"));
839 } else if (!strcmp (mimetype, "video/x-dirac")) {
840 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
843 } else if (!strcmp (mimetype, "video/mpeg")) {
846 gst_structure_get_int (structure, "mpegversion", &mpegversion);
847 switch (mpegversion) {
849 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
852 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
855 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
861 /* global headers may be in codec data */
862 if (codec_buf != NULL) {
863 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
864 context->codec_priv = g_malloc0 (context->codec_priv_size);
865 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
866 context->codec_priv_size);
870 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
872 /* can only make it here if preceding case verified it was version 3 */
873 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
876 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
878 const GValue *mdpr_data;
880 gst_structure_get_int (structure, "rmversion", &rmversion);
883 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
886 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
889 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
898 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
899 if (mdpr_data != NULL) {
900 guint8 *priv_data = NULL;
901 guint priv_data_size = 0;
903 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
905 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
906 priv_data = g_malloc0 (priv_data_size);
908 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
910 context->codec_priv = priv_data;
911 context->codec_priv_size = priv_data_size;
920 /* N > 0 to expect a particular number of headers, negative if the
921 number of headers is variable */
923 xiphN_streamheader_to_codecdata (const GValue * streamheader,
924 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
926 GstBuffer **buf = NULL;
929 guint bufi, i, offset, priv_data_size;
931 if (streamheader == NULL)
932 goto no_stream_headers;
934 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
937 bufarr = g_value_peek_pointer (streamheader);
938 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
940 if (N > 0 && bufarr->len != N)
943 context->xiph_headers_to_skip = bufarr->len;
945 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
946 for (i = 0; i < bufarr->len; i++) {
947 GValue *bufval = &g_array_index (bufarr, GValue, i);
949 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
951 goto wrong_content_type;
954 buf[i] = g_value_peek_pointer (bufval);
958 if (bufarr->len > 0) {
959 for (i = 0; i < bufarr->len - 1; i++) {
960 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
964 for (i = 0; i < bufarr->len; ++i) {
965 priv_data_size += GST_BUFFER_SIZE (buf[i]);
968 priv_data = g_malloc0 (priv_data_size);
970 priv_data[0] = bufarr->len - 1;
973 if (bufarr->len > 0) {
974 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
975 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
976 priv_data[offset++] = 0xff;
978 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
982 for (i = 0; i < bufarr->len; ++i) {
983 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
984 GST_BUFFER_SIZE (buf[i]));
985 offset += GST_BUFFER_SIZE (buf[i]);
988 context->codec_priv = priv_data;
989 context->codec_priv_size = priv_data_size;
992 *p_buf0 = gst_buffer_ref (buf[0]);
1001 GST_WARNING ("required streamheaders missing in sink caps!");
1006 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1007 G_VALUE_TYPE_NAME (streamheader));
1012 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1017 GST_WARNING ("streamheaders array does not contain GstBuffers");
1022 /* FIXME: after release make all code use xiph3_streamheader_to_codecdata() */
1024 xiph3_streamheader_to_codecdata (const GValue * streamheader,
1025 GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
1030 guint i, offset, priv_data_size;
1032 if (streamheader == NULL)
1033 goto no_stream_headers;
1035 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1038 bufarr = g_value_peek_pointer (streamheader);
1039 if (bufarr->len != 3)
1042 context->xiph_headers_to_skip = bufarr->len;
1044 for (i = 0; i < 3; i++) {
1045 GValue *bufval = &g_array_index (bufarr, GValue, i);
1047 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER)
1048 goto wrong_content_type;
1050 buf[i] = g_value_peek_pointer (bufval);
1054 priv_data_size += GST_BUFFER_SIZE (buf[0]) / 0xff + 1;
1055 priv_data_size += GST_BUFFER_SIZE (buf[1]) / 0xff + 1;
1057 for (i = 0; i < 3; ++i) {
1058 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1061 priv_data = g_malloc0 (priv_data_size);
1066 for (i = 0; i < GST_BUFFER_SIZE (buf[0]) / 0xff; ++i) {
1067 priv_data[offset++] = 0xff;
1069 priv_data[offset++] = GST_BUFFER_SIZE (buf[0]) % 0xff;
1071 for (i = 0; i < GST_BUFFER_SIZE (buf[1]) / 0xff; ++i) {
1072 priv_data[offset++] = 0xff;
1074 priv_data[offset++] = GST_BUFFER_SIZE (buf[1]) % 0xff;
1076 for (i = 0; i < 3; ++i) {
1077 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1078 GST_BUFFER_SIZE (buf[i]));
1079 offset += GST_BUFFER_SIZE (buf[i]);
1082 context->codec_priv = priv_data;
1083 context->codec_priv_size = priv_data_size;
1086 *p_buf0 = gst_buffer_ref (buf[0]);
1093 GST_WARNING ("required streamheaders missing in sink caps!");
1098 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1099 G_VALUE_TYPE_NAME (streamheader));
1104 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1109 GST_WARNING ("streamheaders array does not contain GstBuffers");
1115 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1116 GstMatroskaTrackContext * context)
1118 GstBuffer *buf0 = NULL;
1120 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1121 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1124 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1125 GST_WARNING ("First vorbis header too small, ignoring");
1127 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1128 GstMatroskaTrackAudioContext *audiocontext;
1131 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1132 audiocontext = (GstMatroskaTrackAudioContext *) context;
1133 audiocontext->channels = GST_READ_UINT8 (hdr);
1134 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1139 gst_buffer_unref (buf0);
1145 theora_streamheader_to_codecdata (const GValue * streamheader,
1146 GstMatroskaTrackContext * context)
1148 GstBuffer *buf0 = NULL;
1150 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1151 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1154 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1155 GST_WARNING ("First theora header too small, ignoring");
1156 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1157 GST_WARNING ("First header not a theora identification header, ignoring");
1159 GstMatroskaTrackVideoContext *videocontext;
1160 guint fps_num, fps_denom, par_num, par_denom;
1163 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1165 videocontext = (GstMatroskaTrackVideoContext *) context;
1166 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1167 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1168 hdr += 3 + 3 + 1 + 1;
1169 fps_num = GST_READ_UINT32_BE (hdr);
1170 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1171 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1172 fps_denom, fps_num);
1174 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1175 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1176 if (par_num > 0 && par_num > 0) {
1177 if (par_num > par_denom) {
1178 videocontext->display_width =
1179 videocontext->pixel_width * par_num / par_denom;
1180 videocontext->display_height = videocontext->pixel_height;
1181 } else if (par_num < par_denom) {
1182 videocontext->display_width = videocontext->pixel_width;
1183 videocontext->display_height =
1184 videocontext->pixel_height * par_denom / par_num;
1186 videocontext->display_width = 0;
1187 videocontext->display_height = 0;
1190 videocontext->display_width = 0;
1191 videocontext->display_height = 0;
1197 gst_buffer_unref (buf0);
1203 kate_streamheader_to_codecdata (const GValue * streamheader,
1204 GstMatroskaTrackContext * context)
1206 GstBuffer *buf0 = NULL;
1208 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1211 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1212 GST_WARNING ("First kate header too small, ignoring");
1213 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1214 GST_WARNING ("First header not a kate identification header, ignoring");
1218 gst_buffer_unref (buf0);
1224 flac_streamheader_to_codecdata (const GValue * streamheader,
1225 GstMatroskaTrackContext * context)
1232 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1233 GST_WARNING ("No or invalid streamheader field in the caps");
1237 bufarr = g_value_peek_pointer (streamheader);
1238 if (bufarr->len < 2) {
1239 GST_WARNING ("Too few headers in streamheader field");
1243 context->xiph_headers_to_skip = bufarr->len + 1;
1245 bufval = &g_array_index (bufarr, GValue, 0);
1246 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1247 GST_WARNING ("streamheaders array does not contain GstBuffers");
1251 buffer = g_value_peek_pointer (bufval);
1253 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1254 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1255 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1256 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1257 GST_WARNING ("Invalid streamheader for FLAC");
1261 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1262 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1263 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1264 GST_BUFFER_SIZE (buffer) - 9);
1266 for (i = 1; i < bufarr->len; i++) {
1267 bufval = &g_array_index (bufarr, GValue, i);
1269 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1270 g_free (context->codec_priv);
1271 context->codec_priv = NULL;
1272 context->codec_priv_size = 0;
1273 GST_WARNING ("streamheaders array does not contain GstBuffers");
1277 buffer = g_value_peek_pointer (bufval);
1279 context->codec_priv =
1280 g_realloc (context->codec_priv,
1281 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1282 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1283 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1284 context->codec_priv_size =
1285 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1292 speex_streamheader_to_codecdata (const GValue * streamheader,
1293 GstMatroskaTrackContext * context)
1299 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1300 GST_WARNING ("No or invalid streamheader field in the caps");
1304 bufarr = g_value_peek_pointer (streamheader);
1305 if (bufarr->len != 2) {
1306 GST_WARNING ("Too few headers in streamheader field");
1310 context->xiph_headers_to_skip = bufarr->len + 1;
1312 bufval = &g_array_index (bufarr, GValue, 0);
1313 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1314 GST_WARNING ("streamheaders array does not contain GstBuffers");
1318 buffer = g_value_peek_pointer (bufval);
1320 if (GST_BUFFER_SIZE (buffer) < 80
1321 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1322 GST_WARNING ("Invalid streamheader for Speex");
1326 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1327 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1328 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1329 GST_BUFFER_SIZE (buffer));
1331 bufval = &g_array_index (bufarr, GValue, 1);
1333 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1334 g_free (context->codec_priv);
1335 context->codec_priv = NULL;
1336 context->codec_priv_size = 0;
1337 GST_WARNING ("streamheaders array does not contain GstBuffers");
1341 buffer = g_value_peek_pointer (bufval);
1343 context->codec_priv =
1344 g_realloc (context->codec_priv,
1345 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1346 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1347 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1348 context->codec_priv_size =
1349 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1355 aac_codec_data_to_codec_id (const GstBuffer * buf)
1360 /* default to MAIN */
1363 if (GST_BUFFER_SIZE (buf) >= 2) {
1364 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1382 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1391 * gst_matroska_mux_audio_pad_setcaps:
1392 * @pad: Pad which got the caps.
1395 * Setcaps function for audio sink pad.
1397 * Returns: #TRUE on success.
1400 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1402 GstMatroskaTrackContext *context = NULL;
1403 GstMatroskaTrackAudioContext *audiocontext;
1404 GstMatroskaMux *mux;
1405 GstMatroskaPad *collect_pad;
1406 const gchar *mimetype;
1407 gint samplerate = 0, channels = 0;
1408 GstStructure *structure;
1410 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1413 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1414 g_assert (collect_pad);
1415 context = collect_pad->track;
1417 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1418 audiocontext = (GstMatroskaTrackAudioContext *) context;
1420 structure = gst_caps_get_structure (caps, 0);
1421 mimetype = gst_structure_get_name (structure);
1424 gst_structure_get_int (structure, "rate", &samplerate);
1425 gst_structure_get_int (structure, "channels", &channels);
1427 audiocontext->samplerate = samplerate;
1428 audiocontext->channels = channels;
1429 audiocontext->bitdepth = 0;
1430 context->default_duration = 0;
1432 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1433 * data and other settings
1437 if (!strcmp (mimetype, "audio/mpeg")) {
1438 gint mpegversion = 0;
1439 const GValue *codec_data;
1440 const GstBuffer *buf = NULL;
1442 codec_data = gst_structure_get_value (structure, "codec_data");
1444 buf = gst_value_get_buffer (codec_data);
1446 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1447 switch (mpegversion) {
1453 gst_structure_get_int (structure, "layer", &layer);
1455 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1456 GST_WARNING_OBJECT (mux,
1457 "Unable to determine MPEG audio version, assuming 1");
1463 else if (layer == 2)
1465 else if (version == 2)
1470 context->default_duration =
1471 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1475 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1478 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1481 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1491 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1492 aac_codec_data_to_codec_id (buf));
1494 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1501 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1502 aac_codec_data_to_codec_id (buf));
1504 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1513 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1515 gint endianness = G_LITTLE_ENDIAN;
1516 gboolean signedness = TRUE;
1518 if (!gst_structure_get_int (structure, "width", &width) ||
1519 !gst_structure_get_int (structure, "depth", &depth) ||
1520 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1521 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1526 !gst_structure_get_int (structure, "endianness", &endianness)) {
1527 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1531 if (width != depth) {
1532 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1536 /* FIXME: where is this spec'ed out? (tpm) */
1537 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1538 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1542 audiocontext->bitdepth = depth;
1543 if (endianness == G_BIG_ENDIAN)
1544 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1546 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1549 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1552 if (!gst_structure_get_int (structure, "width", &width)) {
1553 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1557 audiocontext->bitdepth = width;
1558 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1561 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1562 const GValue *streamheader;
1564 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1566 if (context->codec_priv != NULL) {
1567 g_free (context->codec_priv);
1568 context->codec_priv = NULL;
1569 context->codec_priv_size = 0;
1572 streamheader = gst_structure_get_value (structure, "streamheader");
1573 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1574 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1575 ("vorbis stream headers missing or malformed"));
1579 } else if (!strcmp (mimetype, "audio/x-flac")) {
1580 const GValue *streamheader;
1582 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1583 if (context->codec_priv != NULL) {
1584 g_free (context->codec_priv);
1585 context->codec_priv = NULL;
1586 context->codec_priv_size = 0;
1589 streamheader = gst_structure_get_value (structure, "streamheader");
1590 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1591 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1592 ("flac stream headers missing or malformed"));
1596 } else if (!strcmp (mimetype, "audio/x-speex")) {
1597 const GValue *streamheader;
1599 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1600 if (context->codec_priv != NULL) {
1601 g_free (context->codec_priv);
1602 context->codec_priv = NULL;
1603 context->codec_priv_size = 0;
1606 streamheader = gst_structure_get_value (structure, "streamheader");
1607 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1608 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1609 ("speex stream headers missing or malformed"));
1613 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1614 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1617 } else if (!strcmp (mimetype, "audio/x-tta")) {
1620 /* TTA frame duration */
1621 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1623 gst_structure_get_int (structure, "width", &width);
1624 audiocontext->bitdepth = width;
1625 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1628 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1630 const GValue *mdpr_data;
1632 gst_structure_get_int (structure, "raversion", &raversion);
1633 switch (raversion) {
1635 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1638 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1641 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1647 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1648 if (mdpr_data != NULL) {
1649 guint8 *priv_data = NULL;
1650 guint priv_data_size = 0;
1652 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1654 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1655 priv_data = g_malloc0 (priv_data_size);
1657 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1659 context->codec_priv = priv_data;
1660 context->codec_priv_size = priv_data_size;
1671 * gst_matroska_mux_subtitle_pad_setcaps:
1672 * @pad: Pad which got the caps.
1675 * Setcaps function for subtitle sink pad.
1677 * Returns: #TRUE on success.
1680 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1683 * Consider this as boilerplate code for now. There is
1684 * no single subtitle creation element in GStreamer,
1685 * neither do I know how subtitling works at all. */
1687 /* There is now (at least) one such alement (kateenc), and I'm going
1688 to handle it here and claim it works when it can be piped back
1689 through GStreamer and VLC */
1691 GstMatroskaTrackContext *context = NULL;
1692 GstMatroskaTrackSubtitleContext *scontext;
1693 GstMatroskaMux *mux;
1694 GstMatroskaPad *collect_pad;
1695 const gchar *mimetype;
1696 GstStructure *structure;
1698 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1701 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1702 g_assert (collect_pad);
1703 context = collect_pad->track;
1705 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1706 scontext = (GstMatroskaTrackSubtitleContext *) context;
1708 structure = gst_caps_get_structure (caps, 0);
1709 mimetype = gst_structure_get_name (structure);
1712 scontext->check_utf8 = 1;
1713 scontext->invalid_utf8 = 0;
1714 context->default_duration = 0;
1716 /* TODO: - other format than Kate */
1718 if (!strcmp (mimetype, "subtitle/x-kate")) {
1719 const GValue *streamheader;
1721 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1723 if (context->codec_priv != NULL) {
1724 g_free (context->codec_priv);
1725 context->codec_priv = NULL;
1726 context->codec_priv_size = 0;
1729 streamheader = gst_structure_get_value (structure, "streamheader");
1730 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1731 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1732 ("kate stream headers missing or malformed"));
1743 * gst_matroska_mux_request_new_pad:
1744 * @element: #GstMatroskaMux.
1745 * @templ: #GstPadTemplate.
1746 * @pad_name: New pad name.
1748 * Request pad function for sink templates.
1750 * Returns: New #GstPad.
1753 gst_matroska_mux_request_new_pad (GstElement * element,
1754 GstPadTemplate * templ, const gchar * pad_name)
1756 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1757 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1758 GstMatroskaPad *collect_pad;
1759 GstPad *newpad = NULL;
1761 GstPadSetCapsFunction setcapsfunc = NULL;
1762 GstMatroskaTrackContext *context = NULL;
1764 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1765 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1766 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1767 context = (GstMatroskaTrackContext *)
1768 g_new0 (GstMatroskaTrackAudioContext, 1);
1769 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1770 context->name = g_strdup ("Audio");
1771 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1772 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1773 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1774 context = (GstMatroskaTrackContext *)
1775 g_new0 (GstMatroskaTrackVideoContext, 1);
1776 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1777 context->name = g_strdup ("Video");
1778 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1779 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1780 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1781 context = (GstMatroskaTrackContext *)
1782 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1783 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1784 context->name = g_strdup ("Subtitle");
1786 GST_WARNING_OBJECT (mux, "This is not our template!");
1790 newpad = gst_pad_new_from_template (templ, name);
1792 collect_pad = (GstMatroskaPad *)
1793 gst_collect_pads_add_pad_full (mux->collect, newpad,
1794 sizeof (GstMatroskaPad),
1795 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1797 collect_pad->track = context;
1798 gst_matroska_pad_reset (collect_pad, FALSE);
1800 /* FIXME: hacked way to override/extend the event function of
1801 * GstCollectPads; because it sets its own event function giving the
1802 * element no access to events.
1803 * TODO GstCollectPads should really give its 'users' a clean chance to
1804 * properly handle events that are not meant for collectpads itself.
1805 * Perhaps a callback or so, though rejected (?) in #340060.
1806 * This would allow (clean) transcoding of info from demuxer/streams
1807 * to another muxer */
1808 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1809 gst_pad_set_event_function (newpad,
1810 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1812 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1813 gst_pad_set_active (newpad, TRUE);
1814 gst_element_add_pad (element, newpad);
1821 * gst_matroska_mux_release_pad:
1822 * @element: #GstMatroskaMux.
1823 * @pad: Pad to release.
1825 * Release a previously requested pad.
1828 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1830 GstMatroskaMux *mux;
1833 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1835 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1836 GstCollectData *cdata = (GstCollectData *) walk->data;
1837 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1839 if (cdata->pad == pad) {
1840 GstClockTime min_dur; /* observed minimum duration */
1842 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1843 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1844 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1845 if (collect_pad->duration < min_dur)
1846 collect_pad->duration = min_dur;
1849 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1850 mux->duration < collect_pad->duration)
1851 mux->duration = collect_pad->duration;
1857 gst_collect_pads_remove_pad (mux->collect, pad);
1858 if (gst_element_remove_pad (element, pad))
1864 * gst_matroska_mux_track_header:
1865 * @mux: #GstMatroskaMux
1866 * @context: Tack context.
1868 * Write a track header.
1871 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1872 GstMatroskaTrackContext * context)
1874 GstEbmlWrite *ebml = mux->ebml_write;
1877 /* TODO: check if everything necessary is written and check default values */
1879 /* track type goes before the type-specific stuff */
1880 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1881 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1883 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1884 gst_matroska_mux_create_uid ());
1885 if (context->default_duration) {
1886 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1887 context->default_duration);
1889 if (context->language) {
1890 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1894 /* type-specific stuff */
1895 switch (context->type) {
1896 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1897 GstMatroskaTrackVideoContext *videocontext =
1898 (GstMatroskaTrackVideoContext *) context;
1900 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1902 videocontext->pixel_width);
1903 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1904 videocontext->pixel_height);
1905 if (videocontext->display_width && videocontext->display_height) {
1906 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1907 videocontext->display_width);
1908 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1909 videocontext->display_height);
1911 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1912 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1913 if (videocontext->fourcc) {
1914 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1916 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1917 (gpointer) & fcc_le, 4);
1919 gst_ebml_write_master_finish (ebml, master);
1924 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1925 GstMatroskaTrackAudioContext *audiocontext =
1926 (GstMatroskaTrackAudioContext *) context;
1928 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1929 if (audiocontext->samplerate != 8000)
1930 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1931 audiocontext->samplerate);
1932 if (audiocontext->channels != 1)
1933 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1934 audiocontext->channels);
1935 if (audiocontext->bitdepth) {
1936 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1937 audiocontext->bitdepth);
1939 gst_ebml_write_master_finish (ebml, master);
1945 /* doesn't need type-specific data */
1949 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1950 if (context->codec_priv)
1951 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1952 context->codec_priv, context->codec_priv_size);
1953 /* FIXME: until we have a nice way of getting the codecname
1954 * out of the caps, I'm not going to enable this. Too much
1955 * (useless, double, boring) work... */
1956 /* TODO: Use value from tags if any */
1957 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1958 context->codec_name); */
1959 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1964 * gst_matroska_mux_start:
1965 * @mux: #GstMatroskaMux
1967 * Start a new matroska file (write headers etc...)
1970 gst_matroska_mux_start (GstMatroskaMux * mux)
1972 GstEbmlWrite *ebml = mux->ebml_write;
1973 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1974 GST_MATROSKA_ID_TRACKS,
1975 GST_MATROSKA_ID_CUES,
1976 GST_MATROSKA_ID_TAGS,
1979 guint64 master, child;
1983 GstClockTime duration = 0;
1984 guint32 segment_uid[4];
1985 GTimeVal time = { 0, 0 };
1987 /* we start with a EBML header */
1988 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
1990 /* start a segment */
1992 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
1993 mux->segment_master = ebml->pos;
1995 /* the rest of the header is cached */
1996 gst_ebml_write_set_cache (ebml, 0x1000);
1998 /* seekhead (table of contents) - we set the positions later */
1999 mux->seekhead_pos = ebml->pos;
2000 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2001 for (i = 0; seekhead_id[i] != 0; i++) {
2002 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2003 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2004 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2005 gst_ebml_write_master_finish (ebml, child);
2007 gst_ebml_write_master_finish (ebml, master);
2010 mux->info_pos = ebml->pos;
2011 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2012 for (i = 0; i < 4; i++) {
2013 segment_uid[i] = g_random_int ();
2015 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2016 (guint8 *) segment_uid, 16);
2017 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2018 mux->duration_pos = ebml->pos;
2020 for (collected = mux->collect->data; collected;
2021 collected = g_slist_next (collected)) {
2022 GstMatroskaPad *collect_pad;
2023 GstFormat format = GST_FORMAT_TIME;
2025 gint64 trackduration;
2027 collect_pad = (GstMatroskaPad *) collected->data;
2028 thepad = collect_pad->collect.pad;
2030 /* Query the total length of the track. */
2031 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2032 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2033 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2034 GST_TIME_ARGS (trackduration));
2035 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2036 duration = (GstClockTime) trackduration;
2040 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2041 gst_guint64_to_gdouble (duration) /
2042 gst_guint64_to_gdouble (mux->time_scale));
2044 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2045 "GStreamer plugin version " PACKAGE_VERSION);
2046 if (mux->writing_app && mux->writing_app[0]) {
2047 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2049 g_get_current_time (&time);
2050 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2051 gst_ebml_write_master_finish (ebml, master);
2054 mux->tracks_pos = ebml->pos;
2055 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2057 for (collected = mux->collect->data; collected;
2058 collected = g_slist_next (collected)) {
2059 GstMatroskaPad *collect_pad;
2062 collect_pad = (GstMatroskaPad *) collected->data;
2063 thepad = collect_pad->collect.pad;
2065 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2066 collect_pad->track->codec_id != 0) {
2067 collect_pad->track->num = tracknum++;
2068 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2069 gst_matroska_mux_track_header (mux, collect_pad->track);
2070 gst_ebml_write_master_finish (ebml, child);
2073 gst_ebml_write_master_finish (ebml, master);
2075 /* lastly, flush the cache */
2076 gst_ebml_write_flush_cache (ebml);
2080 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2083 /* TODO: more sensible tag mappings */
2086 gchar *matroska_tagname;
2087 gchar *gstreamer_tagname;
2091 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2092 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2093 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2094 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2095 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2096 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2097 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2098 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2099 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2100 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2101 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2102 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2103 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2104 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2105 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2107 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2109 guint64 simpletag_master;
2111 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2112 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2113 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2115 if (strcmp (tagname_gst, tag) == 0) {
2116 GValue src = { 0, };
2119 if (!gst_tag_list_copy_value (&src, list, tag))
2121 if ((dest = gst_value_serialize (&src))) {
2123 simpletag_master = gst_ebml_write_master_start (ebml,
2124 GST_MATROSKA_ID_SIMPLETAG);
2125 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2126 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2127 gst_ebml_write_master_finish (ebml, simpletag_master);
2130 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2132 g_value_unset (&src);
2140 * gst_matroska_mux_finish:
2141 * @mux: #GstMatroskaMux
2143 * Finish a new matroska file (write index etc...)
2146 gst_matroska_mux_finish (GstMatroskaMux * mux)
2148 GstEbmlWrite *ebml = mux->ebml_write;
2150 guint64 duration = 0;
2152 const GstTagList *tags;
2154 /* finish last cluster */
2156 gst_ebml_write_master_finish (ebml, mux->cluster);
2160 if (mux->index != NULL) {
2162 guint64 master, pointentry_master, trackpos_master;
2164 mux->cues_pos = ebml->pos;
2165 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2166 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2168 for (n = 0; n < mux->num_indexes; n++) {
2169 GstMatroskaIndex *idx = &mux->index[n];
2171 pointentry_master = gst_ebml_write_master_start (ebml,
2172 GST_MATROSKA_ID_POINTENTRY);
2173 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2174 idx->time / mux->time_scale);
2175 trackpos_master = gst_ebml_write_master_start (ebml,
2176 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2177 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2178 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2179 idx->pos - mux->segment_master);
2180 gst_ebml_write_master_finish (ebml, trackpos_master);
2181 gst_ebml_write_master_finish (ebml, pointentry_master);
2184 gst_ebml_write_master_finish (ebml, master);
2185 gst_ebml_write_flush_cache (ebml);
2189 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2191 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2192 guint64 master_tags, master_tag;
2194 GST_DEBUG ("Writing tags");
2196 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2197 mux->tags_pos = ebml->pos;
2198 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2199 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2200 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2201 gst_ebml_write_master_finish (ebml, master_tag);
2202 gst_ebml_write_master_finish (ebml, master_tags);
2205 /* update seekhead. We know that:
2206 * - a seekhead contains 4 entries.
2207 * - order of entries is as above.
2208 * - a seekhead has a 4-byte header + 8-byte length
2209 * - each entry is 2-byte master, 2-byte ID pointer,
2210 * 2-byte length pointer, all 8/1-byte length, 4-
2211 * byte ID and 8-byte length pointer, where the
2212 * length pointer starts at 20.
2213 * - all entries are local to the segment (so pos - segment_master).
2214 * - so each entry is at 12 + 20 + num * 28. */
2215 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2216 mux->info_pos - mux->segment_master);
2217 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2218 mux->tracks_pos - mux->segment_master);
2219 if (mux->index != NULL) {
2220 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2221 mux->cues_pos - mux->segment_master);
2224 guint64 my_pos = ebml->pos;
2226 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2227 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2228 gst_ebml_write_seek (ebml, my_pos);
2231 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2232 mux->tags_pos - mux->segment_master);
2235 guint64 my_pos = ebml->pos;
2237 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2238 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2239 gst_ebml_write_seek (ebml, my_pos);
2242 /* update duration */
2243 /* first get the overall duration */
2244 /* a released track may have left a duration in here */
2245 duration = mux->duration;
2246 for (collected = mux->collect->data; collected;
2247 collected = g_slist_next (collected)) {
2248 GstMatroskaPad *collect_pad;
2249 GstClockTime min_duration; /* observed minimum duration */
2251 collect_pad = (GstMatroskaPad *) collected->data;
2253 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2254 " end ts %" GST_TIME_FORMAT, collect_pad,
2255 GST_TIME_ARGS (collect_pad->start_ts),
2256 GST_TIME_ARGS (collect_pad->end_ts));
2258 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2259 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2261 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2262 if (collect_pad->duration < min_duration)
2263 collect_pad->duration = min_duration;
2264 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2265 GST_TIME_ARGS (collect_pad->duration));
2268 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2269 duration < collect_pad->duration)
2270 duration = collect_pad->duration;
2272 if (duration != 0) {
2273 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2274 GST_TIME_ARGS (duration));
2275 pos = mux->ebml_write->pos;
2276 gst_ebml_write_seek (ebml, mux->duration_pos);
2277 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2278 gst_guint64_to_gdouble (duration) /
2279 gst_guint64_to_gdouble (mux->time_scale));
2280 gst_ebml_write_seek (ebml, pos);
2283 guint64 my_pos = ebml->pos;
2285 gst_ebml_write_seek (ebml, mux->duration_pos);
2286 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2287 gst_ebml_write_seek (ebml, my_pos);
2290 /* finish segment - this also writes element length */
2291 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2296 * gst_matroska_mux_best_pad:
2297 * @mux: #GstMatroskaMux
2298 * @popped: True if at least one buffer was popped from #GstCollectPads
2300 * Find a pad with the oldest data
2301 * (data from this pad should be written first).
2303 * Returns: Selected pad.
2305 static GstMatroskaPad *
2306 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2309 GstMatroskaPad *best = NULL;
2312 for (collected = mux->collect->data; collected;
2313 collected = g_slist_next (collected)) {
2314 GstMatroskaPad *collect_pad;
2316 collect_pad = (GstMatroskaPad *) collected->data;
2317 /* fetch a new buffer if needed */
2318 if (collect_pad->buffer == NULL) {
2319 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2320 (GstCollectData *) collect_pad);
2322 if (collect_pad->buffer != NULL)
2326 /* if we have a buffer check if it is better then the current best one */
2327 if (collect_pad->buffer != NULL) {
2328 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2329 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2330 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2331 GST_BUFFER_TIMESTAMP (best->buffer))) {
2341 * gst_matroska_mux_buffer_header:
2342 * @track: Track context.
2343 * @relative_timestamp: relative timestamp of the buffer
2344 * @flags: Buffer flags.
2346 * Create a buffer containing buffer header.
2348 * Returns: New buffer.
2351 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2352 gint16 relative_timestamp, int flags)
2356 hdr = gst_buffer_new_and_alloc (4);
2357 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2358 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2359 /* time relative to clustertime */
2360 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2363 GST_BUFFER_DATA (hdr)[3] = flags;
2369 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2370 GstMatroskaPad * collect_pad, GstBuffer * buf)
2372 GstMatroskaTrackVideoContext *ctx =
2373 (GstMatroskaTrackVideoContext *) collect_pad->track;
2374 const guint8 *data = GST_BUFFER_DATA (buf);
2375 guint size = GST_BUFFER_SIZE (buf);
2377 guint32 next_parse_offset;
2378 GstBuffer *ret = NULL;
2379 gboolean is_picture = FALSE;
2381 if (GST_BUFFER_SIZE (buf) < 13) {
2382 gst_buffer_unref (buf);
2386 /* Check if this buffer contains a picture packet */
2387 while (size >= 13) {
2388 if (GST_READ_UINT32_BE (data) != 0x42424344) {
2389 gst_buffer_unref (buf);
2393 parse_code = GST_READ_UINT8 (data + 4);
2394 if (parse_code == 0x00) {
2395 if (ctx->dirac_unit) {
2396 gst_buffer_unref (ctx->dirac_unit);
2397 ctx->dirac_unit = NULL;
2399 } else if (parse_code & 0x08) {
2404 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2406 data += next_parse_offset;
2407 size -= next_parse_offset;
2410 if (ctx->dirac_unit)
2411 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2413 ctx->dirac_unit = gst_buffer_ref (buf);
2416 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2417 ctx->dirac_unit = NULL;
2418 gst_buffer_copy_metadata (ret, buf,
2419 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2420 GST_BUFFER_COPY_CAPS);
2421 gst_buffer_unref (buf);
2423 gst_buffer_unref (buf);
2431 * gst_matroska_mux_write_data:
2432 * @mux: #GstMatroskaMux
2433 * @collect_pad: #GstMatroskaPad with the data
2435 * Write collected data (called from gst_matroska_mux_collected).
2437 * Returns: Result of the gst_pad_push issued to write the data.
2439 static GstFlowReturn
2440 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2442 GstEbmlWrite *ebml = mux->ebml_write;
2443 GstBuffer *buf, *hdr;
2445 gboolean write_duration;
2446 gint16 relative_timestamp;
2447 gint64 relative_timestamp64;
2448 guint64 block_duration;
2449 gboolean is_video_keyframe = FALSE;
2452 buf = collect_pad->buffer;
2453 collect_pad->buffer = NULL;
2455 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2456 if (collect_pad->track->xiph_headers_to_skip > 0) {
2457 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2458 gst_buffer_unref (buf);
2459 --collect_pad->track->xiph_headers_to_skip;
2463 /* for dirac we have to queue up everything up to a picture unit */
2464 if (collect_pad->track->codec_id != NULL &&
2465 strcmp (collect_pad->track->codec_id,
2466 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2467 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2472 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2473 * this would wreak havoc with time stored in matroska file */
2474 /* TODO: maybe calculate a timestamp by using the previous timestamp
2475 * and default duration */
2476 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2477 GST_WARNING_OBJECT (collect_pad->collect.pad,
2478 "Invalid buffer timestamp; dropping buffer");
2479 gst_buffer_unref (buf);
2483 /* set the timestamp for outgoing buffers */
2484 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2486 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2487 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2488 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2489 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2490 is_video_keyframe = TRUE;
2494 /* start a new cluster every two seconds or at keyframe */
2495 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2496 || is_video_keyframe) {
2498 gst_ebml_write_master_finish (ebml, mux->cluster);
2499 mux->cluster_pos = ebml->pos;
2501 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2502 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2503 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2504 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2509 mux->cluster_pos = ebml->pos;
2510 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2511 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2512 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2513 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2516 /* update duration of this track */
2517 if (GST_BUFFER_DURATION_IS_VALID (buf))
2518 collect_pad->duration += GST_BUFFER_DURATION (buf);
2520 /* We currently write an index entry for each keyframe in a
2521 * video track or one entry for each cluster in an audio track
2522 * for audio only files. This can be largely improved, such as doing
2523 * one for each keyframe or each second (for all-keyframe
2524 * streams), only the *first* video track. But that'll come later... */
2526 /* TODO: index is useful for every track, should contain the number of
2527 * the block in the cluster which contains the timestamp
2529 if (is_video_keyframe) {
2530 GstMatroskaIndex *idx;
2532 if (mux->num_indexes % 32 == 0) {
2533 mux->index = g_renew (GstMatroskaIndex, mux->index,
2534 mux->num_indexes + 32);
2536 idx = &mux->index[mux->num_indexes++];
2538 idx->pos = mux->cluster_pos;
2539 idx->time = GST_BUFFER_TIMESTAMP (buf);
2540 idx->track = collect_pad->track->num;
2541 } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2542 (mux->num_streams == 1)) {
2543 GstMatroskaIndex *idx;
2545 if (mux->num_indexes % 32 == 0) {
2546 mux->index = g_renew (GstMatroskaIndex, mux->index,
2547 mux->num_indexes + 32);
2549 idx = &mux->index[mux->num_indexes++];
2551 idx->pos = mux->cluster_pos;
2552 idx->time = GST_BUFFER_TIMESTAMP (buf);
2553 idx->track = collect_pad->track->num;
2556 /* Check if the duration differs from the default duration. */
2557 write_duration = FALSE;
2558 block_duration = GST_BUFFER_DURATION (buf);
2559 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2560 if (block_duration != collect_pad->track->default_duration) {
2561 write_duration = TRUE;
2565 /* write the block, for matroska v2 use SimpleBlock if possible
2566 * one slice (*breath*).
2567 * FIXME: Need to do correct lacing! */
2568 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2569 if (relative_timestamp64 >= 0) {
2570 /* round the timestamp */
2571 relative_timestamp64 += mux->time_scale / 2;
2573 /* round the timestamp */
2574 relative_timestamp64 -= mux->time_scale / 2;
2576 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2577 if (mux->matroska_version > 1 && !write_duration) {
2579 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2582 gst_matroska_mux_create_buffer_header (collect_pad->track,
2583 relative_timestamp, flags);
2584 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2585 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2586 gst_ebml_write_buffer (ebml, hdr);
2587 gst_ebml_write_buffer (ebml, buf);
2589 return gst_ebml_last_write_result (ebml);
2591 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2593 gst_matroska_mux_create_buffer_header (collect_pad->track,
2594 relative_timestamp, 0);
2595 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2596 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2597 gst_ebml_write_buffer (ebml, hdr);
2598 gst_ebml_write_buffer (ebml, buf);
2599 if (write_duration) {
2600 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2601 block_duration / mux->time_scale);
2603 gst_ebml_write_master_finish (ebml, blockgroup);
2604 return gst_ebml_last_write_result (ebml);
2610 * gst_matroska_mux_collected:
2611 * @pads: #GstCollectPads
2612 * @uuser_data: #GstMatroskaMux
2614 * Collectpads callback.
2616 * Returns: #GstFlowReturn
2618 static GstFlowReturn
2619 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2621 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2622 GstMatroskaPad *best;
2626 GST_DEBUG_OBJECT (mux, "Collected pads");
2628 /* start with a header */
2629 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2630 if (mux->collect->data == NULL) {
2631 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2632 ("No input streams configured"));
2633 return GST_FLOW_ERROR;
2635 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2636 gst_matroska_mux_start (mux);
2637 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2641 /* which stream to write from? */
2642 best = gst_matroska_mux_best_pad (mux, &popped);
2644 /* if there is no best pad, we have reached EOS */
2646 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2647 gst_matroska_mux_finish (mux);
2648 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2649 ret = GST_FLOW_UNEXPECTED;
2652 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2653 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2654 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2655 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2657 /* make note of first and last encountered timestamps, so we can calculate
2658 * the actual duration later when we send an updated header on eos */
2659 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2660 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2661 GstClockTime end_ts = start_ts;
2663 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2664 end_ts += GST_BUFFER_DURATION (best->buffer);
2665 else if (best->track->default_duration)
2666 end_ts += best->track->default_duration;
2668 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2669 best->end_ts = end_ts;
2671 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2672 start_ts < best->start_ts))
2673 best->start_ts = start_ts;
2676 /* write one buffer */
2677 ret = gst_matroska_mux_write_data (mux, best);
2678 } while (ret == GST_FLOW_OK && !popped);
2685 * gst_matroska_mux_change_state:
2686 * @element: #GstMatroskaMux
2687 * @transition: State change transition.
2689 * Change the muxer state.
2691 * Returns: #GstStateChangeReturn
2693 static GstStateChangeReturn
2694 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2696 GstStateChangeReturn ret;
2697 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2699 switch (transition) {
2700 case GST_STATE_CHANGE_NULL_TO_READY:
2702 case GST_STATE_CHANGE_READY_TO_PAUSED:
2703 gst_collect_pads_start (mux->collect);
2705 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2707 case GST_STATE_CHANGE_PAUSED_TO_READY:
2708 gst_collect_pads_stop (mux->collect);
2714 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2716 switch (transition) {
2717 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2719 case GST_STATE_CHANGE_PAUSED_TO_READY:
2720 gst_matroska_mux_reset (GST_ELEMENT (mux));
2722 case GST_STATE_CHANGE_READY_TO_NULL:
2732 gst_matroska_mux_set_property (GObject * object,
2733 guint prop_id, const GValue * value, GParamSpec * pspec)
2735 GstMatroskaMux *mux;
2737 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2738 mux = GST_MATROSKA_MUX (object);
2741 case ARG_WRITING_APP:
2742 if (!g_value_get_string (value)) {
2743 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2746 g_free (mux->writing_app);
2747 mux->writing_app = g_value_dup_string (value);
2749 case ARG_MATROSKA_VERSION:
2750 mux->matroska_version = g_value_get_int (value);
2753 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2759 gst_matroska_mux_get_property (GObject * object,
2760 guint prop_id, GValue * value, GParamSpec * pspec)
2762 GstMatroskaMux *mux;
2764 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2765 mux = GST_MATROSKA_MUX (object);
2768 case ARG_WRITING_APP:
2769 g_value_set_string (value, mux->writing_app);
2771 case ARG_MATROSKA_VERSION:
2772 g_value_set_int (value, mux->matroska_version);
2775 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2781 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2783 return gst_element_register (plugin, "matroskamux",
2784 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);