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.
52 #include <gst/riff/riff-media.h>
53 #include <gst/tag/tag.h>
55 #include "matroska-mux.h"
56 #include "matroska-ids.h"
58 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
59 #define GST_CAT_DEFAULT matroskamux_debug
66 ARG_MIN_INDEX_INTERVAL,
70 #define DEFAULT_DOCTYPE_VERSION 2
71 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
72 #define DEFAULT_MIN_INDEX_INTERVAL 0
73 #define DEFAULT_STREAMABLE FALSE
75 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
76 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
78 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
81 GST_STATIC_CAPS ("video/x-matroska")
84 #define COMMON_VIDEO_CAPS \
85 "width = (int) [ 16, 4096 ], " \
86 "height = (int) [ 16, 4096 ], " \
87 "framerate = (fraction) [ 0, MAX ]"
89 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
90 "width = (int) [ 16, 4096 ], " \
91 "height = (int) [ 16, 4096 ] "
94 * * require codec data, etc as needed
97 static GstStaticPadTemplate videosink_templ =
98 GST_STATIC_PAD_TEMPLATE ("video_%d",
101 GST_STATIC_CAPS ("video/mpeg, "
102 "mpegversion = (int) { 1, 2, 4 }, "
103 "systemstream = (boolean) false, "
104 COMMON_VIDEO_CAPS "; "
106 COMMON_VIDEO_CAPS "; "
108 COMMON_VIDEO_CAPS "; "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS "; "
114 COMMON_VIDEO_CAPS "; "
116 COMMON_VIDEO_CAPS "; "
118 COMMON_VIDEO_CAPS "; "
120 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
123 COMMON_VIDEO_CAPS "; "
124 "video/x-pn-realvideo, "
125 "rmversion = (int) [1, 4], "
126 COMMON_VIDEO_CAPS "; "
128 COMMON_VIDEO_CAPS "; "
130 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
131 COMMON_VIDEO_CAPS "; "
132 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
135 #define COMMON_AUDIO_CAPS \
136 "channels = (int) [ 1, MAX ], " \
137 "rate = (int) [ 1, MAX ]"
140 * * require codec data, etc as needed
142 static GstStaticPadTemplate audiosink_templ =
143 GST_STATIC_PAD_TEMPLATE ("audio_%d",
146 GST_STATIC_CAPS ("audio/mpeg, "
147 "mpegversion = (int) 1, "
148 "layer = (int) [ 1, 3 ], "
149 "stream-format = (string) { raw }, "
150 COMMON_AUDIO_CAPS "; "
152 "mpegversion = (int) { 2, 4 }, "
153 COMMON_AUDIO_CAPS "; "
155 COMMON_AUDIO_CAPS "; "
157 COMMON_AUDIO_CAPS "; "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
169 "signed = (boolean) false, "
170 COMMON_AUDIO_CAPS ";"
174 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
175 "signed = (boolean) true, "
176 COMMON_AUDIO_CAPS ";"
180 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
181 "signed = (boolean) true, "
182 COMMON_AUDIO_CAPS ";"
186 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
187 "signed = (boolean) true, "
188 COMMON_AUDIO_CAPS ";"
189 "audio/x-raw-float, "
190 "width = (int) [ 32, 64 ], "
191 "endianness = (int) LITTLE_ENDIAN, "
192 COMMON_AUDIO_CAPS ";"
194 "width = (int) { 8, 16, 24 }, "
195 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
196 "audio/x-pn-realaudio, "
197 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
198 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
199 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
203 static GstStaticPadTemplate subtitlesink_templ =
204 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
207 GST_STATIC_CAPS ("subtitle/x-kate"));
209 static GArray *used_uids;
210 G_LOCK_DEFINE_STATIC (used_uids);
212 static void gst_matroska_mux_add_interfaces (GType type);
214 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
215 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
217 /* Matroska muxer destructor */
218 static void gst_matroska_mux_finalize (GObject * object);
220 /* Pads collected callback */
222 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
225 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
227 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
228 GstPadTemplate * templ, const gchar * name);
229 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
231 /* gst internal change state handler */
232 static GstStateChangeReturn
233 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
235 /* gobject bla bla */
236 static void gst_matroska_mux_set_property (GObject * object,
237 guint prop_id, const GValue * value, GParamSpec * pspec);
238 static void gst_matroska_mux_get_property (GObject * object,
239 guint prop_id, GValue * value, GParamSpec * pspec);
242 static void gst_matroska_mux_reset (GstElement * element);
245 static guint64 gst_matroska_mux_create_uid ();
247 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
251 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
253 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
254 GstMatroskaTrackContext * context);
255 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
256 GstMatroskaTrackContext * context);
259 gst_matroska_mux_add_interfaces (GType type)
261 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
263 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
267 gst_matroska_mux_base_init (gpointer g_class)
272 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
274 GObjectClass *gobject_class;
275 GstElementClass *gstelement_class;
277 gobject_class = (GObjectClass *) klass;
278 gstelement_class = (GstElementClass *) klass;
280 gst_element_class_add_pad_template (gstelement_class,
281 gst_static_pad_template_get (&videosink_templ));
282 gst_element_class_add_pad_template (gstelement_class,
283 gst_static_pad_template_get (&audiosink_templ));
284 gst_element_class_add_pad_template (gstelement_class,
285 gst_static_pad_template_get (&subtitlesink_templ));
286 gst_element_class_add_pad_template (gstelement_class,
287 gst_static_pad_template_get (&src_templ));
288 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
290 "Muxes video/audio/subtitle streams into a matroska stream",
291 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
293 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
296 gobject_class->finalize = gst_matroska_mux_finalize;
298 gobject_class->get_property = gst_matroska_mux_get_property;
299 gobject_class->set_property = gst_matroska_mux_set_property;
301 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
302 g_param_spec_string ("writing-app", "Writing application.",
303 "The name the application that creates the matroska file.",
304 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
306 g_param_spec_int ("version", "DocType version",
307 "This parameter determines what Matroska features can be used.",
308 1, 2, DEFAULT_DOCTYPE_VERSION,
309 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
311 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
312 "entries", "An index entry is created every so many nanoseconds.",
313 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
314 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
316 g_param_spec_boolean ("streamable", "Determines whether output should "
317 "be streamable", "If set to true, the output should be as if it is "
318 "to be streamed and hence no indexes written or duration written.",
320 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
322 gstelement_class->change_state =
323 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
324 gstelement_class->request_new_pad =
325 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
326 gstelement_class->release_pad =
327 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
332 * gst_matroska_mux_init:
333 * @mux: #GstMatroskaMux that should be initialized.
334 * @g_class: Class of the muxer.
336 * Matroska muxer constructor.
339 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
341 GstPadTemplate *templ;
344 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
345 mux->srcpad = gst_pad_new_from_template (templ, "src");
347 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
348 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
350 mux->collect = gst_collect_pads_new ();
351 gst_collect_pads_set_function (mux->collect,
352 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
355 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
356 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
358 /* property defaults */
359 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
360 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
361 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
362 mux->streamable = DEFAULT_STREAMABLE;
364 /* initialize internal variables */
366 mux->num_streams = 0;
367 mux->num_a_streams = 0;
368 mux->num_t_streams = 0;
369 mux->num_v_streams = 0;
371 /* initialize remaining variables */
372 gst_matroska_mux_reset (GST_ELEMENT (mux));
377 * gst_matroska_mux_finalize:
378 * @object: #GstMatroskaMux that should be finalized.
380 * Finalize matroska muxer.
383 gst_matroska_mux_finalize (GObject * object)
385 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
387 gst_object_unref (mux->collect);
388 gst_object_unref (mux->ebml_write);
389 if (mux->writing_app)
390 g_free (mux->writing_app);
392 G_OBJECT_CLASS (parent_class)->finalize (object);
397 * gst_matroska_mux_create_uid:
399 * Generate new unused track UID.
401 * Returns: New track UID.
404 gst_matroska_mux_create_uid (void)
411 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
416 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
417 for (i = 0; i < used_uids->len; i++) {
418 if (g_array_index (used_uids, guint64, i) == uid) {
423 g_array_append_val (used_uids, uid);
426 G_UNLOCK (used_uids);
432 * gst_matroska_pad_reset:
433 * @collect_pad: the #GstMatroskaPad
435 * Reset and/or release resources of a matroska collect pad.
438 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
441 GstMatroskaTrackType type = 0;
443 /* free track information */
444 if (collect_pad->track != NULL) {
445 /* retrieve for optional later use */
446 name = collect_pad->track->name;
447 type = collect_pad->track->type;
448 /* extra for video */
449 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
450 GstMatroskaTrackVideoContext *ctx =
451 (GstMatroskaTrackVideoContext *) collect_pad->track;
453 if (ctx->dirac_unit) {
454 gst_buffer_unref (ctx->dirac_unit);
455 ctx->dirac_unit = NULL;
458 g_free (collect_pad->track->codec_id);
459 g_free (collect_pad->track->codec_name);
461 g_free (collect_pad->track->name);
462 g_free (collect_pad->track->language);
463 g_free (collect_pad->track->codec_priv);
464 g_free (collect_pad->track);
465 collect_pad->track = NULL;
468 /* free cached buffer */
469 if (collect_pad->buffer != NULL) {
470 gst_buffer_unref (collect_pad->buffer);
471 collect_pad->buffer = NULL;
474 if (!full && type != 0) {
475 GstMatroskaTrackContext *context;
477 /* create a fresh context */
479 case GST_MATROSKA_TRACK_TYPE_VIDEO:
480 context = (GstMatroskaTrackContext *)
481 g_new0 (GstMatroskaTrackVideoContext, 1);
483 case GST_MATROSKA_TRACK_TYPE_AUDIO:
484 context = (GstMatroskaTrackContext *)
485 g_new0 (GstMatroskaTrackAudioContext, 1);
487 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
488 context = (GstMatroskaTrackContext *)
489 g_new0 (GstMatroskaTrackSubtitleContext, 1);
492 g_assert_not_reached ();
496 context->type = type;
497 context->name = name;
498 /* TODO: check default values for the context */
499 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
500 collect_pad->track = context;
501 collect_pad->buffer = NULL;
502 collect_pad->duration = 0;
503 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
504 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
509 * gst_matroska_pad_free:
510 * @collect_pad: the #GstMatroskaPad
512 * Release resources of a matroska collect pad.
515 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
517 gst_matroska_pad_reset (collect_pad, TRUE);
522 * gst_matroska_mux_reset:
523 * @element: #GstMatroskaMux that should be reseted.
525 * Reset matroska muxer back to initial state.
528 gst_matroska_mux_reset (GstElement * element)
530 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
533 /* reset EBML write */
534 gst_ebml_write_reset (mux->ebml_write);
537 mux->state = GST_MATROSKA_MUX_STATE_START;
539 /* clean up existing streams */
541 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
542 GstMatroskaPad *collect_pad;
544 collect_pad = (GstMatroskaPad *) walk->data;
546 /* reset collect pad to pristine state */
547 gst_matroska_pad_reset (collect_pad, FALSE);
551 mux->num_indexes = 0;
556 mux->time_scale = GST_MSECOND;
557 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
562 mux->cluster_time = 0;
563 mux->cluster_pos = 0;
564 mux->prev_cluster_size = 0;
567 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
571 * gst_matroska_mux_handle_src_event:
572 * @pad: Pad which received the event.
573 * @event: Received event.
575 * handle events - copied from oggmux without understanding
577 * Returns: #TRUE on success.
580 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
584 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
588 /* disable seeking for now */
594 return gst_pad_event_default (pad, event);
598 * gst_matroska_mux_handle_sink_event:
599 * @pad: Pad which received the event.
600 * @event: Received event.
602 * handle events - informational ones like tags
604 * Returns: #TRUE on success.
607 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
609 GstMatroskaTrackContext *context;
610 GstMatroskaPad *collect_pad;
615 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
617 switch (GST_EVENT_TYPE (event)) {
621 GST_DEBUG_OBJECT (mux, "received tag event");
622 gst_event_parse_tag (event, &list);
624 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
625 g_assert (collect_pad);
626 context = collect_pad->track;
629 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
630 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
631 const gchar *lang_code;
633 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
635 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
636 context->language = g_strdup (lang_code);
638 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
643 /* FIXME: what about stream-specific tags? */
644 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
645 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
647 /* handled this, don't want collectpads to forward it downstream */
649 gst_event_unref (event);
652 case GST_EVENT_NEWSEGMENT:
653 /* We don't support NEWSEGMENT events */
655 gst_event_unref (event);
661 /* now GstCollectPads can take care of the rest, e.g. EOS */
663 ret = mux->collect_event (pad, event);
665 gst_object_unref (mux);
672 * gst_matroska_mux_video_pad_setcaps:
673 * @pad: Pad which got the caps.
676 * Setcaps function for video sink pad.
678 * Returns: #TRUE on success.
681 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
683 GstMatroskaTrackContext *context = NULL;
684 GstMatroskaTrackVideoContext *videocontext;
686 GstMatroskaPad *collect_pad;
687 GstStructure *structure;
688 const gchar *mimetype;
689 const GValue *value = NULL;
690 const GstBuffer *codec_buf = NULL;
691 gint width, height, pixel_width, pixel_height;
693 gboolean interlaced = FALSE;
695 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
698 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
699 g_assert (collect_pad);
700 context = collect_pad->track;
702 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
703 videocontext = (GstMatroskaTrackVideoContext *) context;
705 /* gst -> matroska ID'ing */
706 structure = gst_caps_get_structure (caps, 0);
708 mimetype = gst_structure_get_name (structure);
710 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
712 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
714 if (!strcmp (mimetype, "video/x-theora")) {
715 /* we'll extract the details later from the theora identification header */
719 /* get general properties */
720 /* spec says it is mandatory */
721 if (!gst_structure_get_int (structure, "width", &width) ||
722 !gst_structure_get_int (structure, "height", &height))
725 videocontext->pixel_width = width;
726 videocontext->pixel_height = height;
727 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
729 context->default_duration =
730 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
731 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
732 GST_TIME_ARGS (context->default_duration));
734 context->default_duration = 0;
736 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
737 &pixel_width, &pixel_height)) {
738 if (pixel_width > pixel_height) {
739 videocontext->display_width = width * pixel_width / pixel_height;
740 videocontext->display_height = height;
741 } else if (pixel_width < pixel_height) {
742 videocontext->display_width = width;
743 videocontext->display_height = height * pixel_height / pixel_width;
745 videocontext->display_width = 0;
746 videocontext->display_height = 0;
749 videocontext->display_width = 0;
750 videocontext->display_height = 0;
755 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
756 videocontext->fourcc = 0;
758 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
759 * data and other settings
763 /* extract codec_data, may turn out needed */
764 value = gst_structure_get_value (structure, "codec_data");
766 codec_buf = gst_value_get_buffer (value);
769 if (!strcmp (mimetype, "video/x-raw-yuv")) {
770 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
771 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
772 } else if (!strcmp (mimetype, "image/jpeg")) {
773 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
774 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
775 ||!strcmp (mimetype, "video/x-huffyuv")
776 || !strcmp (mimetype, "video/x-divx")
777 || !strcmp (mimetype, "video/x-dv")
778 || !strcmp (mimetype, "video/x-h263")
779 || !strcmp (mimetype, "video/x-msmpeg")
780 || !strcmp (mimetype, "video/x-wmv")) {
781 gst_riff_strf_vids *bih;
782 gint size = sizeof (gst_riff_strf_vids);
785 if (!strcmp (mimetype, "video/x-xvid"))
786 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
787 else if (!strcmp (mimetype, "video/x-huffyuv"))
788 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
789 else if (!strcmp (mimetype, "video/x-dv"))
790 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
791 else if (!strcmp (mimetype, "video/x-h263"))
792 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
793 else if (!strcmp (mimetype, "video/x-divx")) {
796 gst_structure_get_int (structure, "divxversion", &divxversion);
797 switch (divxversion) {
799 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
802 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
805 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
808 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
811 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
812 switch (msmpegversion) {
814 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
817 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
823 } else if (!strcmp (mimetype, "video/x-wmv")) {
826 if (gst_structure_get_fourcc (structure, "format", &format)) {
828 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
829 if (wmvversion == 2) {
830 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
831 } else if (wmvversion == 1) {
832 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
833 } else if (wmvversion == 3) {
834 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
842 bih = g_new0 (gst_riff_strf_vids, 1);
843 GST_WRITE_UINT32_LE (&bih->size, size);
844 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
845 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
846 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
847 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
848 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
849 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
850 videocontext->pixel_height * 3);
852 /* process codec private/initialization data, if any */
854 size += GST_BUFFER_SIZE (codec_buf);
855 bih = g_realloc (bih, size);
856 GST_WRITE_UINT32_LE (&bih->size, size);
857 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
858 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
861 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
862 context->codec_priv = (gpointer) bih;
863 context->codec_priv_size = size;
864 } else if (!strcmp (mimetype, "video/x-h264")) {
865 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
867 if (context->codec_priv != NULL) {
868 g_free (context->codec_priv);
869 context->codec_priv = NULL;
870 context->codec_priv_size = 0;
873 /* Create avcC header */
874 if (codec_buf != NULL) {
875 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
876 context->codec_priv = g_malloc0 (context->codec_priv_size);
877 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
878 context->codec_priv_size);
880 } else if (!strcmp (mimetype, "video/x-theora")) {
881 const GValue *streamheader;
883 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
885 if (context->codec_priv != NULL) {
886 g_free (context->codec_priv);
887 context->codec_priv = NULL;
888 context->codec_priv_size = 0;
891 streamheader = gst_structure_get_value (structure, "streamheader");
892 if (!theora_streamheader_to_codecdata (streamheader, context)) {
893 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
894 ("theora stream headers missing or malformed"));
897 } else if (!strcmp (mimetype, "video/x-dirac")) {
898 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
899 } else if (!strcmp (mimetype, "video/x-vp8")) {
900 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
901 } else if (!strcmp (mimetype, "video/mpeg")) {
904 gst_structure_get_int (structure, "mpegversion", &mpegversion);
905 switch (mpegversion) {
907 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
910 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
913 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
919 /* global headers may be in codec data */
920 if (codec_buf != NULL) {
921 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
922 context->codec_priv = g_malloc0 (context->codec_priv_size);
923 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
924 context->codec_priv_size);
926 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
928 /* can only make it here if preceding case verified it was version 3 */
929 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
930 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
932 const GValue *mdpr_data;
934 gst_structure_get_int (structure, "rmversion", &rmversion);
937 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
940 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
943 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
946 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
952 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
953 if (mdpr_data != NULL) {
954 guint8 *priv_data = NULL;
955 guint priv_data_size = 0;
957 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
959 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
960 priv_data = g_malloc0 (priv_data_size);
962 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
964 context->codec_priv = priv_data;
965 context->codec_priv_size = priv_data_size;
974 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
975 GST_PAD_NAME (pad), caps);
980 /* N > 0 to expect a particular number of headers, negative if the
981 number of headers is variable */
983 xiphN_streamheader_to_codecdata (const GValue * streamheader,
984 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
986 GstBuffer **buf = NULL;
989 guint bufi, i, offset, priv_data_size;
991 if (streamheader == NULL)
992 goto no_stream_headers;
994 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
997 bufarr = g_value_peek_pointer (streamheader);
998 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1000 if (N > 0 && bufarr->len != N)
1003 context->xiph_headers_to_skip = bufarr->len;
1005 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1006 for (i = 0; i < bufarr->len; i++) {
1007 GValue *bufval = &g_array_index (bufarr, GValue, i);
1009 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1011 goto wrong_content_type;
1014 buf[i] = g_value_peek_pointer (bufval);
1018 if (bufarr->len > 0) {
1019 for (i = 0; i < bufarr->len - 1; i++) {
1020 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1024 for (i = 0; i < bufarr->len; ++i) {
1025 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1028 priv_data = g_malloc0 (priv_data_size);
1030 priv_data[0] = bufarr->len - 1;
1033 if (bufarr->len > 0) {
1034 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1035 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1036 priv_data[offset++] = 0xff;
1038 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1042 for (i = 0; i < bufarr->len; ++i) {
1043 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1044 GST_BUFFER_SIZE (buf[i]));
1045 offset += GST_BUFFER_SIZE (buf[i]);
1048 context->codec_priv = priv_data;
1049 context->codec_priv_size = priv_data_size;
1052 *p_buf0 = gst_buffer_ref (buf[0]);
1061 GST_WARNING ("required streamheaders missing in sink caps!");
1066 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1067 G_VALUE_TYPE_NAME (streamheader));
1072 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1077 GST_WARNING ("streamheaders array does not contain GstBuffers");
1083 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1084 GstMatroskaTrackContext * context)
1086 GstBuffer *buf0 = NULL;
1088 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1091 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1092 GST_WARNING ("First vorbis header too small, ignoring");
1094 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1095 GstMatroskaTrackAudioContext *audiocontext;
1098 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1099 audiocontext = (GstMatroskaTrackAudioContext *) context;
1100 audiocontext->channels = GST_READ_UINT8 (hdr);
1101 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1106 gst_buffer_unref (buf0);
1112 theora_streamheader_to_codecdata (const GValue * streamheader,
1113 GstMatroskaTrackContext * context)
1115 GstBuffer *buf0 = NULL;
1117 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1120 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1121 GST_WARNING ("First theora header too small, ignoring");
1122 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1123 GST_WARNING ("First header not a theora identification header, ignoring");
1125 GstMatroskaTrackVideoContext *videocontext;
1126 guint fps_num, fps_denom, par_num, par_denom;
1129 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1131 videocontext = (GstMatroskaTrackVideoContext *) context;
1132 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1133 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1134 hdr += 3 + 3 + 1 + 1;
1135 fps_num = GST_READ_UINT32_BE (hdr);
1136 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1137 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1138 fps_denom, fps_num);
1140 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1141 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1142 if (par_num > 0 && par_num > 0) {
1143 if (par_num > par_denom) {
1144 videocontext->display_width =
1145 videocontext->pixel_width * par_num / par_denom;
1146 videocontext->display_height = videocontext->pixel_height;
1147 } else if (par_num < par_denom) {
1148 videocontext->display_width = videocontext->pixel_width;
1149 videocontext->display_height =
1150 videocontext->pixel_height * par_denom / par_num;
1152 videocontext->display_width = 0;
1153 videocontext->display_height = 0;
1156 videocontext->display_width = 0;
1157 videocontext->display_height = 0;
1163 gst_buffer_unref (buf0);
1169 kate_streamheader_to_codecdata (const GValue * streamheader,
1170 GstMatroskaTrackContext * context)
1172 GstBuffer *buf0 = NULL;
1174 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1177 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1178 GST_WARNING ("First kate header too small, ignoring");
1179 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1180 GST_WARNING ("First header not a kate identification header, ignoring");
1184 gst_buffer_unref (buf0);
1190 flac_streamheader_to_codecdata (const GValue * streamheader,
1191 GstMatroskaTrackContext * context)
1198 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1199 GST_WARNING ("No or invalid streamheader field in the caps");
1203 bufarr = g_value_peek_pointer (streamheader);
1204 if (bufarr->len < 2) {
1205 GST_WARNING ("Too few headers in streamheader field");
1209 context->xiph_headers_to_skip = bufarr->len + 1;
1211 bufval = &g_array_index (bufarr, GValue, 0);
1212 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1213 GST_WARNING ("streamheaders array does not contain GstBuffers");
1217 buffer = g_value_peek_pointer (bufval);
1219 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1220 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1221 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1222 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1223 GST_WARNING ("Invalid streamheader for FLAC");
1227 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1228 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1229 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1230 GST_BUFFER_SIZE (buffer) - 9);
1232 for (i = 1; i < bufarr->len; i++) {
1233 bufval = &g_array_index (bufarr, GValue, i);
1235 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1236 g_free (context->codec_priv);
1237 context->codec_priv = NULL;
1238 context->codec_priv_size = 0;
1239 GST_WARNING ("streamheaders array does not contain GstBuffers");
1243 buffer = g_value_peek_pointer (bufval);
1245 context->codec_priv =
1246 g_realloc (context->codec_priv,
1247 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1248 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1249 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1250 context->codec_priv_size =
1251 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1258 speex_streamheader_to_codecdata (const GValue * streamheader,
1259 GstMatroskaTrackContext * context)
1265 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1266 GST_WARNING ("No or invalid streamheader field in the caps");
1270 bufarr = g_value_peek_pointer (streamheader);
1271 if (bufarr->len != 2) {
1272 GST_WARNING ("Too few headers in streamheader field");
1276 context->xiph_headers_to_skip = bufarr->len + 1;
1278 bufval = &g_array_index (bufarr, GValue, 0);
1279 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1280 GST_WARNING ("streamheaders array does not contain GstBuffers");
1284 buffer = g_value_peek_pointer (bufval);
1286 if (GST_BUFFER_SIZE (buffer) < 80
1287 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1288 GST_WARNING ("Invalid streamheader for Speex");
1292 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1293 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1294 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1295 GST_BUFFER_SIZE (buffer));
1297 bufval = &g_array_index (bufarr, GValue, 1);
1299 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1300 g_free (context->codec_priv);
1301 context->codec_priv = NULL;
1302 context->codec_priv_size = 0;
1303 GST_WARNING ("streamheaders array does not contain GstBuffers");
1307 buffer = g_value_peek_pointer (bufval);
1309 context->codec_priv =
1310 g_realloc (context->codec_priv,
1311 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1312 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1313 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1314 context->codec_priv_size =
1315 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1320 static const gchar *
1321 aac_codec_data_to_codec_id (const GstBuffer * buf)
1323 const gchar *result;
1326 /* default to MAIN */
1329 if (GST_BUFFER_SIZE (buf) >= 2) {
1330 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1348 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1357 * gst_matroska_mux_audio_pad_setcaps:
1358 * @pad: Pad which got the caps.
1361 * Setcaps function for audio sink pad.
1363 * Returns: #TRUE on success.
1366 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1368 GstMatroskaTrackContext *context = NULL;
1369 GstMatroskaTrackAudioContext *audiocontext;
1370 GstMatroskaMux *mux;
1371 GstMatroskaPad *collect_pad;
1372 const gchar *mimetype;
1373 gint samplerate = 0, channels = 0;
1374 GstStructure *structure;
1375 const GValue *codec_data = NULL;
1376 const GstBuffer *buf = NULL;
1377 const gchar *stream_format = NULL;
1379 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1382 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1383 g_assert (collect_pad);
1384 context = collect_pad->track;
1386 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1387 audiocontext = (GstMatroskaTrackAudioContext *) context;
1389 structure = gst_caps_get_structure (caps, 0);
1390 mimetype = gst_structure_get_name (structure);
1393 gst_structure_get_int (structure, "rate", &samplerate);
1394 gst_structure_get_int (structure, "channels", &channels);
1396 audiocontext->samplerate = samplerate;
1397 audiocontext->channels = channels;
1398 audiocontext->bitdepth = 0;
1399 context->default_duration = 0;
1401 codec_data = gst_structure_get_value (structure, "codec_data");
1403 buf = gst_value_get_buffer (codec_data);
1405 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1406 * data and other settings
1410 if (!strcmp (mimetype, "audio/mpeg")) {
1411 gint mpegversion = 0;
1413 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1414 switch (mpegversion) {
1420 gst_structure_get_int (structure, "layer", &layer);
1422 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1423 GST_WARNING_OBJECT (mux,
1424 "Unable to determine MPEG audio version, assuming 1");
1430 else if (layer == 2)
1432 else if (version == 2)
1437 context->default_duration =
1438 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1442 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1445 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1448 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1457 stream_format = gst_structure_get_string (structure, "stream-format");
1458 /* check this is raw aac */
1459 if (stream_format) {
1460 if (strcmp (stream_format, "raw") != 0) {
1461 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1465 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1470 if (mpegversion == 2)
1472 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1473 aac_codec_data_to_codec_id (buf));
1474 else if (mpegversion == 4)
1476 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1477 aac_codec_data_to_codec_id (buf));
1479 g_assert_not_reached ();
1481 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1488 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1490 gint endianness = G_LITTLE_ENDIAN;
1491 gboolean signedness = TRUE;
1493 if (!gst_structure_get_int (structure, "width", &width) ||
1494 !gst_structure_get_int (structure, "depth", &depth) ||
1495 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1496 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1501 !gst_structure_get_int (structure, "endianness", &endianness)) {
1502 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1506 if (width != depth) {
1507 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1511 /* FIXME: where is this spec'ed out? (tpm) */
1512 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1513 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1517 audiocontext->bitdepth = depth;
1518 if (endianness == G_BIG_ENDIAN)
1519 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1521 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1523 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1526 if (!gst_structure_get_int (structure, "width", &width)) {
1527 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1531 audiocontext->bitdepth = width;
1532 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1534 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1535 const GValue *streamheader;
1537 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1539 if (context->codec_priv != NULL) {
1540 g_free (context->codec_priv);
1541 context->codec_priv = NULL;
1542 context->codec_priv_size = 0;
1545 streamheader = gst_structure_get_value (structure, "streamheader");
1546 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1547 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1548 ("vorbis stream headers missing or malformed"));
1551 } else if (!strcmp (mimetype, "audio/x-flac")) {
1552 const GValue *streamheader;
1554 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1555 if (context->codec_priv != NULL) {
1556 g_free (context->codec_priv);
1557 context->codec_priv = NULL;
1558 context->codec_priv_size = 0;
1561 streamheader = gst_structure_get_value (structure, "streamheader");
1562 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1563 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1564 ("flac stream headers missing or malformed"));
1567 } else if (!strcmp (mimetype, "audio/x-speex")) {
1568 const GValue *streamheader;
1570 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1571 if (context->codec_priv != NULL) {
1572 g_free (context->codec_priv);
1573 context->codec_priv = NULL;
1574 context->codec_priv_size = 0;
1577 streamheader = gst_structure_get_value (structure, "streamheader");
1578 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1579 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1580 ("speex stream headers missing or malformed"));
1583 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1584 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1585 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1586 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1587 } else if (!strcmp (mimetype, "audio/x-dts")) {
1588 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1589 } else if (!strcmp (mimetype, "audio/x-tta")) {
1592 /* TTA frame duration */
1593 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1595 gst_structure_get_int (structure, "width", &width);
1596 audiocontext->bitdepth = width;
1597 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1599 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1601 const GValue *mdpr_data;
1603 gst_structure_get_int (structure, "raversion", &raversion);
1604 switch (raversion) {
1606 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1609 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1612 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1618 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1619 if (mdpr_data != NULL) {
1620 guint8 *priv_data = NULL;
1621 guint priv_data_size = 0;
1623 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1625 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1626 priv_data = g_malloc0 (priv_data_size);
1628 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1630 context->codec_priv = priv_data;
1631 context->codec_priv_size = priv_data_size;
1634 } else if (!strcmp (mimetype, "audio/x-wma")) {
1636 guint codec_priv_size;
1643 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1644 || !gst_structure_get_int (structure, "block_align", &block_align)
1645 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1646 || samplerate == 0 || channels == 0) {
1647 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1648 "channels/rate on WMA caps");
1652 switch (wmaversion) {
1654 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1657 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1660 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1663 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1667 if (gst_structure_get_int (structure, "depth", &depth))
1668 audiocontext->bitdepth = depth;
1670 codec_priv_size = WAVEFORMATEX_SIZE;
1672 codec_priv_size += GST_BUFFER_SIZE (buf);
1674 /* serialize waveformatex structure */
1675 codec_priv = g_malloc0 (codec_priv_size);
1676 GST_WRITE_UINT16_LE (codec_priv, format);
1677 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1678 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1679 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1680 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1681 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1683 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1685 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1687 /* process codec private/initialization data, if any */
1689 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1690 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1693 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1694 context->codec_priv = (gpointer) codec_priv;
1695 context->codec_priv_size = codec_priv_size;
1703 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1704 GST_PAD_NAME (pad), caps);
1711 * gst_matroska_mux_subtitle_pad_setcaps:
1712 * @pad: Pad which got the caps.
1715 * Setcaps function for subtitle sink pad.
1717 * Returns: #TRUE on success.
1720 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1723 * Consider this as boilerplate code for now. There is
1724 * no single subtitle creation element in GStreamer,
1725 * neither do I know how subtitling works at all. */
1727 /* There is now (at least) one such alement (kateenc), and I'm going
1728 to handle it here and claim it works when it can be piped back
1729 through GStreamer and VLC */
1731 GstMatroskaTrackContext *context = NULL;
1732 GstMatroskaTrackSubtitleContext *scontext;
1733 GstMatroskaMux *mux;
1734 GstMatroskaPad *collect_pad;
1735 const gchar *mimetype;
1736 GstStructure *structure;
1738 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1741 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1742 g_assert (collect_pad);
1743 context = collect_pad->track;
1745 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1746 scontext = (GstMatroskaTrackSubtitleContext *) context;
1748 structure = gst_caps_get_structure (caps, 0);
1749 mimetype = gst_structure_get_name (structure);
1752 scontext->check_utf8 = 1;
1753 scontext->invalid_utf8 = 0;
1754 context->default_duration = 0;
1756 /* TODO: - other format than Kate */
1758 if (!strcmp (mimetype, "subtitle/x-kate")) {
1759 const GValue *streamheader;
1761 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1763 if (context->codec_priv != NULL) {
1764 g_free (context->codec_priv);
1765 context->codec_priv = NULL;
1766 context->codec_priv_size = 0;
1769 streamheader = gst_structure_get_value (structure, "streamheader");
1770 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1771 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1772 ("kate stream headers missing or malformed"));
1783 * gst_matroska_mux_request_new_pad:
1784 * @element: #GstMatroskaMux.
1785 * @templ: #GstPadTemplate.
1786 * @pad_name: New pad name.
1788 * Request pad function for sink templates.
1790 * Returns: New #GstPad.
1793 gst_matroska_mux_request_new_pad (GstElement * element,
1794 GstPadTemplate * templ, const gchar * req_name)
1796 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1797 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1798 GstMatroskaPad *collect_pad;
1799 GstPad *newpad = NULL;
1801 const gchar *pad_name = NULL;
1802 GstPadSetCapsFunction setcapsfunc = NULL;
1803 GstMatroskaTrackContext *context = NULL;
1806 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1807 /* don't mix named and unnamed pads, if the pad already exists we fail when
1808 * trying to add it */
1809 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1810 pad_name = req_name;
1812 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1815 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1816 context = (GstMatroskaTrackContext *)
1817 g_new0 (GstMatroskaTrackAudioContext, 1);
1818 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1819 context->name = g_strdup ("Audio");
1820 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1821 /* don't mix named and unnamed pads, if the pad already exists we fail when
1822 * trying to add it */
1823 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1824 pad_name = req_name;
1826 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1829 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1830 context = (GstMatroskaTrackContext *)
1831 g_new0 (GstMatroskaTrackVideoContext, 1);
1832 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1833 context->name = g_strdup ("Video");
1834 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1835 /* don't mix named and unnamed pads, if the pad already exists we fail when
1836 * trying to add it */
1837 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1838 pad_name = req_name;
1840 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1843 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1844 context = (GstMatroskaTrackContext *)
1845 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1846 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1847 context->name = g_strdup ("Subtitle");
1849 GST_WARNING_OBJECT (mux, "This is not our template!");
1853 newpad = gst_pad_new_from_template (templ, pad_name);
1855 collect_pad = (GstMatroskaPad *)
1856 gst_collect_pads_add_pad_full (mux->collect, newpad,
1857 sizeof (GstMatroskaPad),
1858 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1860 collect_pad->track = context;
1861 gst_matroska_pad_reset (collect_pad, FALSE);
1863 /* FIXME: hacked way to override/extend the event function of
1864 * GstCollectPads; because it sets its own event function giving the
1865 * element no access to events.
1866 * TODO GstCollectPads should really give its 'users' a clean chance to
1867 * properly handle events that are not meant for collectpads itself.
1868 * Perhaps a callback or so, though rejected (?) in #340060.
1869 * This would allow (clean) transcoding of info from demuxer/streams
1870 * to another muxer */
1871 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1872 gst_pad_set_event_function (newpad,
1873 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1875 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1876 gst_pad_set_active (newpad, TRUE);
1877 if (!gst_element_add_pad (element, newpad))
1878 goto pad_add_failed;
1882 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1889 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1890 gst_object_unref (newpad);
1896 * gst_matroska_mux_release_pad:
1897 * @element: #GstMatroskaMux.
1898 * @pad: Pad to release.
1900 * Release a previously requested pad.
1903 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1905 GstMatroskaMux *mux;
1908 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1910 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1911 GstCollectData *cdata = (GstCollectData *) walk->data;
1912 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1914 if (cdata->pad == pad) {
1915 GstClockTime min_dur; /* observed minimum duration */
1917 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1918 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1919 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1920 if (collect_pad->duration < min_dur)
1921 collect_pad->duration = min_dur;
1924 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1925 mux->duration < collect_pad->duration)
1926 mux->duration = collect_pad->duration;
1932 gst_collect_pads_remove_pad (mux->collect, pad);
1933 if (gst_element_remove_pad (element, pad))
1939 * gst_matroska_mux_track_header:
1940 * @mux: #GstMatroskaMux
1941 * @context: Tack context.
1943 * Write a track header.
1946 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1947 GstMatroskaTrackContext * context)
1949 GstEbmlWrite *ebml = mux->ebml_write;
1952 /* TODO: check if everything necessary is written and check default values */
1954 /* track type goes before the type-specific stuff */
1955 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1956 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1958 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1959 gst_matroska_mux_create_uid ());
1960 if (context->default_duration) {
1961 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1962 context->default_duration);
1964 if (context->language) {
1965 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1969 /* type-specific stuff */
1970 switch (context->type) {
1971 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1972 GstMatroskaTrackVideoContext *videocontext =
1973 (GstMatroskaTrackVideoContext *) context;
1975 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1976 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1977 videocontext->pixel_width);
1978 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1979 videocontext->pixel_height);
1980 if (videocontext->display_width && videocontext->display_height) {
1981 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1982 videocontext->display_width);
1983 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1984 videocontext->display_height);
1986 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1987 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1988 if (videocontext->fourcc) {
1989 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1991 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1992 (gpointer) & fcc_le, 4);
1994 gst_ebml_write_master_finish (ebml, master);
1999 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2000 GstMatroskaTrackAudioContext *audiocontext =
2001 (GstMatroskaTrackAudioContext *) context;
2003 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2004 if (audiocontext->samplerate != 8000)
2005 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2006 audiocontext->samplerate);
2007 if (audiocontext->channels != 1)
2008 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2009 audiocontext->channels);
2010 if (audiocontext->bitdepth) {
2011 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2012 audiocontext->bitdepth);
2014 gst_ebml_write_master_finish (ebml, master);
2020 /* doesn't need type-specific data */
2024 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2025 if (context->codec_priv)
2026 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2027 context->codec_priv, context->codec_priv_size);
2028 /* FIXME: until we have a nice way of getting the codecname
2029 * out of the caps, I'm not going to enable this. Too much
2030 * (useless, double, boring) work... */
2031 /* TODO: Use value from tags if any */
2032 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2033 context->codec_name); */
2034 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2039 * gst_matroska_mux_start:
2040 * @mux: #GstMatroskaMux
2042 * Start a new matroska file (write headers etc...)
2045 gst_matroska_mux_start (GstMatroskaMux * mux)
2047 GstEbmlWrite *ebml = mux->ebml_write;
2048 const gchar *doctype;
2049 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2050 GST_MATROSKA_ID_TRACKS,
2051 GST_MATROSKA_ID_CUES,
2052 GST_MATROSKA_ID_TAGS,
2055 guint64 master, child;
2059 GstClockTime duration = 0;
2060 guint32 segment_uid[4];
2061 GTimeVal time = { 0, 0 };
2063 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2064 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2066 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2068 /* we start with a EBML header */
2069 doctype = mux->doctype;
2070 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2071 doctype, mux->doctype_version);
2072 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2074 /* the rest of the header is cached */
2075 gst_ebml_write_set_cache (ebml, 0x1000);
2077 /* start a segment */
2079 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2080 mux->segment_master = ebml->pos;
2082 if (!mux->streamable) {
2083 /* seekhead (table of contents) - we set the positions later */
2084 mux->seekhead_pos = ebml->pos;
2085 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2086 for (i = 0; seekhead_id[i] != 0; i++) {
2087 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2088 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2089 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2090 gst_ebml_write_master_finish (ebml, child);
2092 gst_ebml_write_master_finish (ebml, master);
2096 mux->info_pos = ebml->pos;
2097 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2098 for (i = 0; i < 4; i++) {
2099 segment_uid[i] = g_random_int ();
2101 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2102 (guint8 *) segment_uid, 16);
2103 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2104 mux->duration_pos = ebml->pos;
2106 if (!mux->streamable) {
2107 for (collected = mux->collect->data; collected;
2108 collected = g_slist_next (collected)) {
2109 GstMatroskaPad *collect_pad;
2110 GstFormat format = GST_FORMAT_TIME;
2112 gint64 trackduration;
2114 collect_pad = (GstMatroskaPad *) collected->data;
2115 thepad = collect_pad->collect.pad;
2117 /* Query the total length of the track. */
2118 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2119 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2120 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2121 GST_TIME_ARGS (trackduration));
2122 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2123 duration = (GstClockTime) trackduration;
2127 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2128 gst_guint64_to_gdouble (duration) /
2129 gst_guint64_to_gdouble (mux->time_scale));
2131 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2132 "GStreamer plugin version " PACKAGE_VERSION);
2133 if (mux->writing_app && mux->writing_app[0]) {
2134 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2136 g_get_current_time (&time);
2137 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2138 gst_ebml_write_master_finish (ebml, master);
2141 mux->tracks_pos = ebml->pos;
2142 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2144 for (collected = mux->collect->data; collected;
2145 collected = g_slist_next (collected)) {
2146 GstMatroskaPad *collect_pad;
2149 collect_pad = (GstMatroskaPad *) collected->data;
2150 thepad = collect_pad->collect.pad;
2152 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2153 collect_pad->track->codec_id != 0) {
2154 collect_pad->track->num = tracknum++;
2155 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2156 gst_matroska_mux_track_header (mux, collect_pad->track);
2157 gst_ebml_write_master_finish (ebml, child);
2160 gst_ebml_write_master_finish (ebml, master);
2162 /* lastly, flush the cache */
2163 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2167 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2170 /* TODO: more sensible tag mappings */
2173 const gchar *matroska_tagname;
2174 const gchar *gstreamer_tagname;
2178 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2179 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2180 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2181 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2182 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2183 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2184 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2185 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2186 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2187 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2188 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2189 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2190 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2191 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2192 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2194 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2196 guint64 simpletag_master;
2198 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2199 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2200 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2202 if (strcmp (tagname_gst, tag) == 0) {
2203 GValue src = { 0, };
2206 if (!gst_tag_list_copy_value (&src, list, tag))
2208 if ((dest = gst_value_serialize (&src))) {
2210 simpletag_master = gst_ebml_write_master_start (ebml,
2211 GST_MATROSKA_ID_SIMPLETAG);
2212 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2213 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2214 gst_ebml_write_master_finish (ebml, simpletag_master);
2217 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2219 g_value_unset (&src);
2227 * gst_matroska_mux_finish:
2228 * @mux: #GstMatroskaMux
2230 * Finish a new matroska file (write index etc...)
2233 gst_matroska_mux_finish (GstMatroskaMux * mux)
2235 GstEbmlWrite *ebml = mux->ebml_write;
2237 guint64 duration = 0;
2239 const GstTagList *tags;
2241 /* finish last cluster */
2243 gst_ebml_write_master_finish (ebml, mux->cluster);
2247 if (mux->index != NULL) {
2249 guint64 master, pointentry_master, trackpos_master;
2251 mux->cues_pos = ebml->pos;
2252 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2253 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2255 for (n = 0; n < mux->num_indexes; n++) {
2256 GstMatroskaIndex *idx = &mux->index[n];
2258 pointentry_master = gst_ebml_write_master_start (ebml,
2259 GST_MATROSKA_ID_POINTENTRY);
2260 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2261 idx->time / mux->time_scale);
2262 trackpos_master = gst_ebml_write_master_start (ebml,
2263 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2264 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2265 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2266 idx->pos - mux->segment_master);
2267 gst_ebml_write_master_finish (ebml, trackpos_master);
2268 gst_ebml_write_master_finish (ebml, pointentry_master);
2271 gst_ebml_write_master_finish (ebml, master);
2272 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2276 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2278 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2279 guint64 master_tags, master_tag;
2281 GST_DEBUG ("Writing tags");
2283 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2284 mux->tags_pos = ebml->pos;
2285 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2286 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2287 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2288 gst_ebml_write_master_finish (ebml, master_tag);
2289 gst_ebml_write_master_finish (ebml, master_tags);
2292 /* update seekhead. We know that:
2293 * - a seekhead contains 4 entries.
2294 * - order of entries is as above.
2295 * - a seekhead has a 4-byte header + 8-byte length
2296 * - each entry is 2-byte master, 2-byte ID pointer,
2297 * 2-byte length pointer, all 8/1-byte length, 4-
2298 * byte ID and 8-byte length pointer, where the
2299 * length pointer starts at 20.
2300 * - all entries are local to the segment (so pos - segment_master).
2301 * - so each entry is at 12 + 20 + num * 28. */
2302 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2303 mux->info_pos - mux->segment_master);
2304 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2305 mux->tracks_pos - mux->segment_master);
2306 if (mux->index != NULL) {
2307 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2308 mux->cues_pos - mux->segment_master);
2311 guint64 my_pos = ebml->pos;
2313 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2314 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2315 gst_ebml_write_seek (ebml, my_pos);
2318 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2319 mux->tags_pos - mux->segment_master);
2322 guint64 my_pos = ebml->pos;
2324 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2325 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2326 gst_ebml_write_seek (ebml, my_pos);
2329 /* update duration */
2330 /* first get the overall duration */
2331 /* a released track may have left a duration in here */
2332 duration = mux->duration;
2333 for (collected = mux->collect->data; collected;
2334 collected = g_slist_next (collected)) {
2335 GstMatroskaPad *collect_pad;
2336 GstClockTime min_duration; /* observed minimum duration */
2338 collect_pad = (GstMatroskaPad *) collected->data;
2340 GST_DEBUG_OBJECT (mux,
2341 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2342 " end ts %" GST_TIME_FORMAT, collect_pad,
2343 GST_TIME_ARGS (collect_pad->start_ts),
2344 GST_TIME_ARGS (collect_pad->end_ts));
2346 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2347 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2349 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2350 if (collect_pad->duration < min_duration)
2351 collect_pad->duration = min_duration;
2352 GST_DEBUG_OBJECT (collect_pad,
2353 "final track duration: %" GST_TIME_FORMAT,
2354 GST_TIME_ARGS (collect_pad->duration));
2357 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2358 duration < collect_pad->duration)
2359 duration = collect_pad->duration;
2361 if (duration != 0) {
2362 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2363 GST_TIME_ARGS (duration));
2364 pos = mux->ebml_write->pos;
2365 gst_ebml_write_seek (ebml, mux->duration_pos);
2366 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2367 gst_guint64_to_gdouble (duration) /
2368 gst_guint64_to_gdouble (mux->time_scale));
2369 gst_ebml_write_seek (ebml, pos);
2372 guint64 my_pos = ebml->pos;
2374 gst_ebml_write_seek (ebml, mux->duration_pos);
2375 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2376 gst_ebml_write_seek (ebml, my_pos);
2378 GST_DEBUG_OBJECT (mux, "finishing segment");
2379 /* finish segment - this also writes element length */
2380 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2385 * gst_matroska_mux_best_pad:
2386 * @mux: #GstMatroskaMux
2387 * @popped: True if at least one buffer was popped from #GstCollectPads
2389 * Find a pad with the oldest data
2390 * (data from this pad should be written first).
2392 * Returns: Selected pad.
2394 static GstMatroskaPad *
2395 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2398 GstMatroskaPad *best = NULL;
2401 for (collected = mux->collect->data; collected;
2402 collected = g_slist_next (collected)) {
2403 GstMatroskaPad *collect_pad;
2405 collect_pad = (GstMatroskaPad *) collected->data;
2406 /* fetch a new buffer if needed */
2407 if (collect_pad->buffer == NULL) {
2408 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2409 (GstCollectData *) collect_pad);
2411 if (collect_pad->buffer != NULL)
2415 /* if we have a buffer check if it is better then the current best one */
2416 if (collect_pad->buffer != NULL) {
2417 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2418 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2419 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2420 GST_BUFFER_TIMESTAMP (best->buffer))) {
2430 * gst_matroska_mux_buffer_header:
2431 * @track: Track context.
2432 * @relative_timestamp: relative timestamp of the buffer
2433 * @flags: Buffer flags.
2435 * Create a buffer containing buffer header.
2437 * Returns: New buffer.
2440 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2441 gint16 relative_timestamp, int flags)
2445 hdr = gst_buffer_new_and_alloc (4);
2446 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2447 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2448 /* time relative to clustertime */
2449 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2452 GST_BUFFER_DATA (hdr)[3] = flags;
2457 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2458 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2459 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2462 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2463 GstMatroskaPad * collect_pad, GstBuffer * buf)
2465 GstMatroskaTrackVideoContext *ctx =
2466 (GstMatroskaTrackVideoContext *) collect_pad->track;
2467 const guint8 *data = GST_BUFFER_DATA (buf);
2468 guint size = GST_BUFFER_SIZE (buf);
2470 guint32 next_parse_offset;
2471 GstBuffer *ret = NULL;
2472 gboolean is_muxing_unit = FALSE;
2474 if (GST_BUFFER_SIZE (buf) < 13) {
2475 gst_buffer_unref (buf);
2479 /* Check if this buffer contains a picture or end-of-sequence packet */
2480 while (size >= 13) {
2481 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2482 gst_buffer_unref (buf);
2486 parse_code = GST_READ_UINT8 (data + 4);
2487 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2488 if (ctx->dirac_unit) {
2489 gst_buffer_unref (ctx->dirac_unit);
2490 ctx->dirac_unit = NULL;
2492 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2493 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2494 is_muxing_unit = TRUE;
2498 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2500 if (G_UNLIKELY (next_parse_offset == 0))
2503 data += next_parse_offset;
2504 size -= next_parse_offset;
2507 if (ctx->dirac_unit)
2508 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2510 ctx->dirac_unit = gst_buffer_ref (buf);
2512 if (is_muxing_unit) {
2513 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2514 ctx->dirac_unit = NULL;
2515 gst_buffer_copy_metadata (ret, buf,
2516 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2517 GST_BUFFER_COPY_CAPS);
2518 gst_buffer_unref (buf);
2520 gst_buffer_unref (buf);
2528 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2532 GValue streamheader = { 0 };
2533 GValue bufval = { 0 };
2534 GstBuffer *streamheader_buffer;
2535 GstEbmlWrite *ebml = mux->ebml_write;
2537 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2538 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2539 caps = gst_caps_new_simple ("video/webm", NULL);
2541 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2543 s = gst_caps_get_structure (caps, 0);
2544 g_value_init (&streamheader, GST_TYPE_ARRAY);
2545 g_value_init (&bufval, GST_TYPE_BUFFER);
2546 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2547 gst_value_set_buffer (&bufval, streamheader_buffer);
2548 gst_value_array_append_value (&streamheader, &bufval);
2549 g_value_unset (&bufval);
2550 gst_structure_set_value (s, "streamheader", &streamheader);
2551 g_value_unset (&streamheader);
2552 gst_caps_replace (&ebml->caps, caps);
2553 gst_buffer_unref (streamheader_buffer);
2554 gst_caps_unref (caps);
2558 * gst_matroska_mux_write_data:
2559 * @mux: #GstMatroskaMux
2560 * @collect_pad: #GstMatroskaPad with the data
2562 * Write collected data (called from gst_matroska_mux_collected).
2564 * Returns: Result of the gst_pad_push issued to write the data.
2566 static GstFlowReturn
2567 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2569 GstEbmlWrite *ebml = mux->ebml_write;
2570 GstBuffer *buf, *hdr;
2572 gboolean write_duration;
2573 gint16 relative_timestamp;
2574 gint64 relative_timestamp64;
2575 guint64 block_duration;
2576 gboolean is_video_keyframe = FALSE;
2579 buf = collect_pad->buffer;
2580 collect_pad->buffer = NULL;
2582 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2583 if (collect_pad->track->xiph_headers_to_skip > 0) {
2584 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2585 gst_buffer_unref (buf);
2586 --collect_pad->track->xiph_headers_to_skip;
2590 /* for dirac we have to queue up everything up to a picture unit */
2591 if (collect_pad->track->codec_id != NULL &&
2592 strcmp (collect_pad->track->codec_id,
2593 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2594 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2599 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2600 * this would wreak havoc with time stored in matroska file */
2601 /* TODO: maybe calculate a timestamp by using the previous timestamp
2602 * and default duration */
2603 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2604 GST_WARNING_OBJECT (collect_pad->collect.pad,
2605 "Invalid buffer timestamp; dropping buffer");
2606 gst_buffer_unref (buf);
2610 /* set the timestamp for outgoing buffers */
2611 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2613 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2614 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2615 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2616 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2617 is_video_keyframe = TRUE;
2621 /* start a new cluster at every keyframe or when we may be reaching the
2622 * limit of the relative timestamp */
2623 if (mux->cluster_time +
2624 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2625 || is_video_keyframe) {
2626 if (!mux->streamable)
2627 gst_ebml_write_master_finish (ebml, mux->cluster);
2628 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2629 mux->cluster_pos = ebml->pos;
2630 gst_ebml_write_set_cache (ebml, 0x20);
2632 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2633 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2634 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2636 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2637 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2639 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2640 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2641 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2642 mux->prev_cluster_size);
2647 mux->cluster_pos = ebml->pos;
2648 gst_ebml_write_set_cache (ebml, 0x20);
2649 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2650 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2651 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2652 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2653 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2656 /* update duration of this track */
2657 if (GST_BUFFER_DURATION_IS_VALID (buf))
2658 collect_pad->duration += GST_BUFFER_DURATION (buf);
2660 /* We currently write index entries for all video tracks or for the audio
2661 * track in a single-track audio file. This could be improved by keeping the
2662 * index only for the *first* video track. */
2664 /* TODO: index is useful for every track, should contain the number of
2665 * the block in the cluster which contains the timestamp, should also work
2666 * for files with multiple audio tracks.
2668 if (is_video_keyframe ||
2669 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2670 (mux->num_streams == 1))) {
2673 if (mux->min_index_interval != 0) {
2674 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2675 if (mux->index[last_idx].track == collect_pad->track->num)
2680 if (last_idx < 0 || mux->min_index_interval == 0 ||
2681 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2682 >= mux->min_index_interval)) {
2683 GstMatroskaIndex *idx;
2685 if (mux->num_indexes % 32 == 0) {
2686 mux->index = g_renew (GstMatroskaIndex, mux->index,
2687 mux->num_indexes + 32);
2689 idx = &mux->index[mux->num_indexes++];
2691 idx->pos = mux->cluster_pos;
2692 idx->time = GST_BUFFER_TIMESTAMP (buf);
2693 idx->track = collect_pad->track->num;
2697 /* Check if the duration differs from the default duration. */
2698 write_duration = FALSE;
2699 block_duration = GST_BUFFER_DURATION (buf);
2700 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2701 if (block_duration != collect_pad->track->default_duration) {
2702 write_duration = TRUE;
2706 /* write the block, for doctype v2 use SimpleBlock if possible
2707 * one slice (*breath*).
2708 * FIXME: Need to do correct lacing! */
2709 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2710 if (relative_timestamp64 >= 0) {
2711 /* round the timestamp */
2712 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2714 /* round the timestamp */
2715 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2717 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2719 if (mux->doctype_version > 1 && !write_duration) {
2721 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2724 gst_matroska_mux_create_buffer_header (collect_pad->track,
2725 relative_timestamp, flags);
2726 gst_ebml_write_set_cache (ebml, 0x40);
2727 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2728 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2729 gst_ebml_write_buffer (ebml, hdr);
2730 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2731 gst_ebml_write_buffer (ebml, buf);
2733 return gst_ebml_last_write_result (ebml);
2735 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2736 /* write and call order slightly unnatural,
2737 * but avoids seek and minizes pushing */
2738 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2740 gst_matroska_mux_create_buffer_header (collect_pad->track,
2741 relative_timestamp, 0);
2742 if (write_duration) {
2743 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2744 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2746 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2747 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2748 gst_ebml_write_buffer (ebml, hdr);
2749 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2750 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2751 gst_ebml_write_buffer (ebml, buf);
2753 return gst_ebml_last_write_result (ebml);
2759 * gst_matroska_mux_collected:
2760 * @pads: #GstCollectPads
2761 * @uuser_data: #GstMatroskaMux
2763 * Collectpads callback.
2765 * Returns: #GstFlowReturn
2767 static GstFlowReturn
2768 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2770 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2771 GstEbmlWrite *ebml = mux->ebml_write;
2772 GstMatroskaPad *best;
2776 GST_DEBUG_OBJECT (mux, "Collected pads");
2778 /* start with a header */
2779 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2780 if (mux->collect->data == NULL) {
2781 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2782 ("No input streams configured"));
2783 return GST_FLOW_ERROR;
2785 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2786 gst_ebml_start_streamheader (ebml);
2787 gst_matroska_mux_start (mux);
2788 gst_matroska_mux_stop_streamheader (mux);
2789 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2793 /* which stream to write from? */
2794 best = gst_matroska_mux_best_pad (mux, &popped);
2796 /* if there is no best pad, we have reached EOS */
2798 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2799 if (!mux->streamable) {
2800 gst_matroska_mux_finish (mux);
2802 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2804 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2805 ret = GST_FLOW_UNEXPECTED;
2808 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2809 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2810 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2811 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2813 /* make note of first and last encountered timestamps, so we can calculate
2814 * the actual duration later when we send an updated header on eos */
2815 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2816 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2817 GstClockTime end_ts = start_ts;
2819 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2820 end_ts += GST_BUFFER_DURATION (best->buffer);
2821 else if (best->track->default_duration)
2822 end_ts += best->track->default_duration;
2824 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2825 best->end_ts = end_ts;
2827 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2828 start_ts < best->start_ts))
2829 best->start_ts = start_ts;
2832 /* write one buffer */
2833 ret = gst_matroska_mux_write_data (mux, best);
2834 } while (ret == GST_FLOW_OK && !popped);
2841 * gst_matroska_mux_change_state:
2842 * @element: #GstMatroskaMux
2843 * @transition: State change transition.
2845 * Change the muxer state.
2847 * Returns: #GstStateChangeReturn
2849 static GstStateChangeReturn
2850 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2852 GstStateChangeReturn ret;
2853 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2855 switch (transition) {
2856 case GST_STATE_CHANGE_NULL_TO_READY:
2858 case GST_STATE_CHANGE_READY_TO_PAUSED:
2859 gst_collect_pads_start (mux->collect);
2861 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2863 case GST_STATE_CHANGE_PAUSED_TO_READY:
2864 gst_collect_pads_stop (mux->collect);
2870 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2872 switch (transition) {
2873 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2875 case GST_STATE_CHANGE_PAUSED_TO_READY:
2876 gst_matroska_mux_reset (GST_ELEMENT (mux));
2878 case GST_STATE_CHANGE_READY_TO_NULL:
2888 gst_matroska_mux_set_property (GObject * object,
2889 guint prop_id, const GValue * value, GParamSpec * pspec)
2891 GstMatroskaMux *mux;
2893 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2894 mux = GST_MATROSKA_MUX (object);
2897 case ARG_WRITING_APP:
2898 if (!g_value_get_string (value)) {
2899 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2902 g_free (mux->writing_app);
2903 mux->writing_app = g_value_dup_string (value);
2905 case ARG_DOCTYPE_VERSION:
2906 mux->doctype_version = g_value_get_int (value);
2908 case ARG_MIN_INDEX_INTERVAL:
2909 mux->min_index_interval = g_value_get_int64 (value);
2911 case ARG_STREAMABLE:
2912 mux->streamable = g_value_get_boolean (value);
2915 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2921 gst_matroska_mux_get_property (GObject * object,
2922 guint prop_id, GValue * value, GParamSpec * pspec)
2924 GstMatroskaMux *mux;
2926 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2927 mux = GST_MATROSKA_MUX (object);
2930 case ARG_WRITING_APP:
2931 g_value_set_string (value, mux->writing_app);
2933 case ARG_DOCTYPE_VERSION:
2934 g_value_set_int (value, mux->doctype_version);
2936 case ARG_MIN_INDEX_INTERVAL:
2937 g_value_set_int64 (value, mux->min_index_interval);
2939 case ARG_STREAMABLE:
2940 g_value_set_boolean (value, mux->streamable);
2943 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);