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);
389 gst_object_unref (mux->ebml_write);
390 if (mux->writing_app)
391 g_free (mux->writing_app);
393 G_OBJECT_CLASS (parent_class)->finalize (object);
398 * gst_matroska_mux_create_uid:
400 * Generate new unused track UID.
402 * Returns: New track UID.
405 gst_matroska_mux_create_uid (void)
412 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
417 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
418 for (i = 0; i < used_uids->len; i++) {
419 if (g_array_index (used_uids, guint64, i) == uid) {
424 g_array_append_val (used_uids, uid);
427 G_UNLOCK (used_uids);
433 * gst_matroska_pad_reset:
434 * @collect_pad: the #GstMatroskaPad
436 * Reset and/or release resources of a matroska collect pad.
439 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
442 GstMatroskaTrackType type = 0;
444 /* free track information */
445 if (collect_pad->track != NULL) {
446 /* retrieve for optional later use */
447 name = collect_pad->track->name;
448 type = collect_pad->track->type;
449 /* extra for video */
450 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
451 GstMatroskaTrackVideoContext *ctx =
452 (GstMatroskaTrackVideoContext *) collect_pad->track;
454 if (ctx->dirac_unit) {
455 gst_buffer_unref (ctx->dirac_unit);
456 ctx->dirac_unit = NULL;
459 g_free (collect_pad->track->codec_id);
460 g_free (collect_pad->track->codec_name);
462 g_free (collect_pad->track->name);
463 g_free (collect_pad->track->language);
464 g_free (collect_pad->track->codec_priv);
465 g_free (collect_pad->track);
466 collect_pad->track = NULL;
469 /* free cached buffer */
470 if (collect_pad->buffer != NULL) {
471 gst_buffer_unref (collect_pad->buffer);
472 collect_pad->buffer = NULL;
475 if (!full && type != 0) {
476 GstMatroskaTrackContext *context;
478 /* create a fresh context */
480 case GST_MATROSKA_TRACK_TYPE_VIDEO:
481 context = (GstMatroskaTrackContext *)
482 g_new0 (GstMatroskaTrackVideoContext, 1);
484 case GST_MATROSKA_TRACK_TYPE_AUDIO:
485 context = (GstMatroskaTrackContext *)
486 g_new0 (GstMatroskaTrackAudioContext, 1);
488 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
489 context = (GstMatroskaTrackContext *)
490 g_new0 (GstMatroskaTrackSubtitleContext, 1);
493 g_assert_not_reached ();
497 context->type = type;
498 context->name = name;
499 /* TODO: check default values for the context */
500 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
501 collect_pad->track = context;
502 collect_pad->buffer = NULL;
503 collect_pad->duration = 0;
504 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
505 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
510 * gst_matroska_pad_free:
511 * @collect_pad: the #GstMatroskaPad
513 * Release resources of a matroska collect pad.
516 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
518 gst_matroska_pad_reset (collect_pad, TRUE);
523 * gst_matroska_mux_reset:
524 * @element: #GstMatroskaMux that should be reseted.
526 * Reset matroska muxer back to initial state.
529 gst_matroska_mux_reset (GstElement * element)
531 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
534 /* reset EBML write */
535 gst_ebml_write_reset (mux->ebml_write);
538 mux->state = GST_MATROSKA_MUX_STATE_START;
540 /* clean up existing streams */
542 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
543 GstMatroskaPad *collect_pad;
545 collect_pad = (GstMatroskaPad *) walk->data;
547 /* reset collect pad to pristine state */
548 gst_matroska_pad_reset (collect_pad, FALSE);
552 mux->num_indexes = 0;
557 mux->time_scale = GST_MSECOND;
558 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
563 mux->cluster_time = 0;
564 mux->cluster_pos = 0;
565 mux->prev_cluster_size = 0;
568 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
572 * gst_matroska_mux_handle_src_event:
573 * @pad: Pad which received the event.
574 * @event: Received event.
576 * handle events - copied from oggmux without understanding
578 * Returns: #TRUE on success.
581 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
585 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
589 /* disable seeking for now */
595 return gst_pad_event_default (pad, event);
599 * gst_matroska_mux_handle_sink_event:
600 * @pad: Pad which received the event.
601 * @event: Received event.
603 * handle events - informational ones like tags
605 * Returns: #TRUE on success.
608 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
610 GstMatroskaTrackContext *context;
611 GstMatroskaPad *collect_pad;
616 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
618 switch (GST_EVENT_TYPE (event)) {
622 GST_DEBUG_OBJECT (mux, "received tag event");
623 gst_event_parse_tag (event, &list);
625 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
626 g_assert (collect_pad);
627 context = collect_pad->track;
630 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
631 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
632 const gchar *lang_code;
634 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
636 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
637 context->language = g_strdup (lang_code);
639 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
644 /* FIXME: what about stream-specific tags? */
645 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
646 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
648 /* handled this, don't want collectpads to forward it downstream */
650 gst_event_unref (event);
653 case GST_EVENT_NEWSEGMENT:
654 /* We don't support NEWSEGMENT events */
656 gst_event_unref (event);
662 /* now GstCollectPads can take care of the rest, e.g. EOS */
664 ret = mux->collect_event (pad, event);
666 gst_object_unref (mux);
673 * gst_matroska_mux_video_pad_setcaps:
674 * @pad: Pad which got the caps.
677 * Setcaps function for video sink pad.
679 * Returns: #TRUE on success.
682 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
684 GstMatroskaTrackContext *context = NULL;
685 GstMatroskaTrackVideoContext *videocontext;
687 GstMatroskaPad *collect_pad;
688 GstStructure *structure;
689 const gchar *mimetype;
690 const GValue *value = NULL;
691 const GstBuffer *codec_buf = NULL;
692 gint width, height, pixel_width, pixel_height;
694 gboolean interlaced = FALSE;
696 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
699 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
700 g_assert (collect_pad);
701 context = collect_pad->track;
703 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
704 videocontext = (GstMatroskaTrackVideoContext *) context;
706 /* gst -> matroska ID'ing */
707 structure = gst_caps_get_structure (caps, 0);
709 mimetype = gst_structure_get_name (structure);
711 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
713 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
715 if (!strcmp (mimetype, "video/x-theora")) {
716 /* we'll extract the details later from the theora identification header */
720 /* get general properties */
721 /* spec says it is mandatory */
722 if (!gst_structure_get_int (structure, "width", &width) ||
723 !gst_structure_get_int (structure, "height", &height))
726 videocontext->pixel_width = width;
727 videocontext->pixel_height = height;
728 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
730 context->default_duration =
731 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
732 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
733 GST_TIME_ARGS (context->default_duration));
735 context->default_duration = 0;
737 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
738 &pixel_width, &pixel_height)) {
739 if (pixel_width > pixel_height) {
740 videocontext->display_width = width * pixel_width / pixel_height;
741 videocontext->display_height = height;
742 } else if (pixel_width < pixel_height) {
743 videocontext->display_width = width;
744 videocontext->display_height = height * pixel_height / pixel_width;
746 videocontext->display_width = 0;
747 videocontext->display_height = 0;
750 videocontext->display_width = 0;
751 videocontext->display_height = 0;
756 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
757 videocontext->fourcc = 0;
759 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
760 * data and other settings
764 /* extract codec_data, may turn out needed */
765 value = gst_structure_get_value (structure, "codec_data");
767 codec_buf = gst_value_get_buffer (value);
770 if (!strcmp (mimetype, "video/x-raw-yuv")) {
771 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
772 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
773 } else if (!strcmp (mimetype, "image/jpeg")) {
774 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
775 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
776 ||!strcmp (mimetype, "video/x-huffyuv")
777 || !strcmp (mimetype, "video/x-divx")
778 || !strcmp (mimetype, "video/x-dv")
779 || !strcmp (mimetype, "video/x-h263")
780 || !strcmp (mimetype, "video/x-msmpeg")
781 || !strcmp (mimetype, "video/x-wmv")) {
782 gst_riff_strf_vids *bih;
783 gint size = sizeof (gst_riff_strf_vids);
786 if (!strcmp (mimetype, "video/x-xvid"))
787 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
788 else if (!strcmp (mimetype, "video/x-huffyuv"))
789 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
790 else if (!strcmp (mimetype, "video/x-dv"))
791 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
792 else if (!strcmp (mimetype, "video/x-h263"))
793 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
794 else if (!strcmp (mimetype, "video/x-divx")) {
797 gst_structure_get_int (structure, "divxversion", &divxversion);
798 switch (divxversion) {
800 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
803 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
806 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
809 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
812 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
813 switch (msmpegversion) {
815 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
818 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
824 } else if (!strcmp (mimetype, "video/x-wmv")) {
827 if (gst_structure_get_fourcc (structure, "format", &format)) {
829 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
830 if (wmvversion == 2) {
831 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
832 } else if (wmvversion == 1) {
833 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
834 } else if (wmvversion == 3) {
835 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
843 bih = g_new0 (gst_riff_strf_vids, 1);
844 GST_WRITE_UINT32_LE (&bih->size, size);
845 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
846 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
847 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
848 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
849 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
850 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
851 videocontext->pixel_height * 3);
853 /* process codec private/initialization data, if any */
855 size += GST_BUFFER_SIZE (codec_buf);
856 bih = g_realloc (bih, size);
857 GST_WRITE_UINT32_LE (&bih->size, size);
858 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
859 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
862 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
863 context->codec_priv = (gpointer) bih;
864 context->codec_priv_size = size;
865 } else if (!strcmp (mimetype, "video/x-h264")) {
866 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
868 if (context->codec_priv != NULL) {
869 g_free (context->codec_priv);
870 context->codec_priv = NULL;
871 context->codec_priv_size = 0;
874 /* Create avcC header */
875 if (codec_buf != NULL) {
876 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
877 context->codec_priv = g_malloc0 (context->codec_priv_size);
878 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
879 context->codec_priv_size);
881 } else if (!strcmp (mimetype, "video/x-theora")) {
882 const GValue *streamheader;
884 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
886 if (context->codec_priv != NULL) {
887 g_free (context->codec_priv);
888 context->codec_priv = NULL;
889 context->codec_priv_size = 0;
892 streamheader = gst_structure_get_value (structure, "streamheader");
893 if (!theora_streamheader_to_codecdata (streamheader, context)) {
894 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
895 ("theora stream headers missing or malformed"));
898 } else if (!strcmp (mimetype, "video/x-dirac")) {
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
900 } else if (!strcmp (mimetype, "video/x-vp8")) {
901 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
902 } else if (!strcmp (mimetype, "video/mpeg")) {
905 gst_structure_get_int (structure, "mpegversion", &mpegversion);
906 switch (mpegversion) {
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
914 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
920 /* global headers may be in codec data */
921 if (codec_buf != NULL) {
922 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
923 context->codec_priv = g_malloc0 (context->codec_priv_size);
924 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
925 context->codec_priv_size);
927 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
929 /* can only make it here if preceding case verified it was version 3 */
930 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
931 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
933 const GValue *mdpr_data;
935 gst_structure_get_int (structure, "rmversion", &rmversion);
938 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
941 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
944 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
947 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
953 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
954 if (mdpr_data != NULL) {
955 guint8 *priv_data = NULL;
956 guint priv_data_size = 0;
958 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
960 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
961 priv_data = g_malloc0 (priv_data_size);
963 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
965 context->codec_priv = priv_data;
966 context->codec_priv_size = priv_data_size;
975 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
976 GST_PAD_NAME (pad), caps);
981 /* N > 0 to expect a particular number of headers, negative if the
982 number of headers is variable */
984 xiphN_streamheader_to_codecdata (const GValue * streamheader,
985 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
987 GstBuffer **buf = NULL;
990 guint bufi, i, offset, priv_data_size;
992 if (streamheader == NULL)
993 goto no_stream_headers;
995 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
998 bufarr = g_value_peek_pointer (streamheader);
999 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1001 if (N > 0 && bufarr->len != N)
1004 context->xiph_headers_to_skip = bufarr->len;
1006 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1007 for (i = 0; i < bufarr->len; i++) {
1008 GValue *bufval = &g_array_index (bufarr, GValue, i);
1010 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1012 goto wrong_content_type;
1015 buf[i] = g_value_peek_pointer (bufval);
1019 if (bufarr->len > 0) {
1020 for (i = 0; i < bufarr->len - 1; i++) {
1021 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1025 for (i = 0; i < bufarr->len; ++i) {
1026 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1029 priv_data = g_malloc0 (priv_data_size);
1031 priv_data[0] = bufarr->len - 1;
1034 if (bufarr->len > 0) {
1035 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1036 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1037 priv_data[offset++] = 0xff;
1039 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1043 for (i = 0; i < bufarr->len; ++i) {
1044 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1045 GST_BUFFER_SIZE (buf[i]));
1046 offset += GST_BUFFER_SIZE (buf[i]);
1049 context->codec_priv = priv_data;
1050 context->codec_priv_size = priv_data_size;
1053 *p_buf0 = gst_buffer_ref (buf[0]);
1062 GST_WARNING ("required streamheaders missing in sink caps!");
1067 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1068 G_VALUE_TYPE_NAME (streamheader));
1073 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1078 GST_WARNING ("streamheaders array does not contain GstBuffers");
1084 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1085 GstMatroskaTrackContext * context)
1087 GstBuffer *buf0 = NULL;
1089 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1092 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1093 GST_WARNING ("First vorbis header too small, ignoring");
1095 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1096 GstMatroskaTrackAudioContext *audiocontext;
1099 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1100 audiocontext = (GstMatroskaTrackAudioContext *) context;
1101 audiocontext->channels = GST_READ_UINT8 (hdr);
1102 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1107 gst_buffer_unref (buf0);
1113 theora_streamheader_to_codecdata (const GValue * streamheader,
1114 GstMatroskaTrackContext * context)
1116 GstBuffer *buf0 = NULL;
1118 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1121 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1122 GST_WARNING ("First theora header too small, ignoring");
1123 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1124 GST_WARNING ("First header not a theora identification header, ignoring");
1126 GstMatroskaTrackVideoContext *videocontext;
1127 guint fps_num, fps_denom, par_num, par_denom;
1130 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1132 videocontext = (GstMatroskaTrackVideoContext *) context;
1133 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1134 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1135 hdr += 3 + 3 + 1 + 1;
1136 fps_num = GST_READ_UINT32_BE (hdr);
1137 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1138 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1139 fps_denom, fps_num);
1141 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1142 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1143 if (par_num > 0 && par_num > 0) {
1144 if (par_num > par_denom) {
1145 videocontext->display_width =
1146 videocontext->pixel_width * par_num / par_denom;
1147 videocontext->display_height = videocontext->pixel_height;
1148 } else if (par_num < par_denom) {
1149 videocontext->display_width = videocontext->pixel_width;
1150 videocontext->display_height =
1151 videocontext->pixel_height * par_denom / par_num;
1153 videocontext->display_width = 0;
1154 videocontext->display_height = 0;
1157 videocontext->display_width = 0;
1158 videocontext->display_height = 0;
1164 gst_buffer_unref (buf0);
1170 kate_streamheader_to_codecdata (const GValue * streamheader,
1171 GstMatroskaTrackContext * context)
1173 GstBuffer *buf0 = NULL;
1175 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1178 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1179 GST_WARNING ("First kate header too small, ignoring");
1180 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1181 GST_WARNING ("First header not a kate identification header, ignoring");
1185 gst_buffer_unref (buf0);
1191 flac_streamheader_to_codecdata (const GValue * streamheader,
1192 GstMatroskaTrackContext * context)
1199 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1200 GST_WARNING ("No or invalid streamheader field in the caps");
1204 bufarr = g_value_peek_pointer (streamheader);
1205 if (bufarr->len < 2) {
1206 GST_WARNING ("Too few headers in streamheader field");
1210 context->xiph_headers_to_skip = bufarr->len + 1;
1212 bufval = &g_array_index (bufarr, GValue, 0);
1213 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1214 GST_WARNING ("streamheaders array does not contain GstBuffers");
1218 buffer = g_value_peek_pointer (bufval);
1220 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1221 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1222 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1223 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1224 GST_WARNING ("Invalid streamheader for FLAC");
1228 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1229 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1230 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1231 GST_BUFFER_SIZE (buffer) - 9);
1233 for (i = 1; i < bufarr->len; i++) {
1234 bufval = &g_array_index (bufarr, GValue, i);
1236 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1237 g_free (context->codec_priv);
1238 context->codec_priv = NULL;
1239 context->codec_priv_size = 0;
1240 GST_WARNING ("streamheaders array does not contain GstBuffers");
1244 buffer = g_value_peek_pointer (bufval);
1246 context->codec_priv =
1247 g_realloc (context->codec_priv,
1248 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1249 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1250 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1251 context->codec_priv_size =
1252 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1259 speex_streamheader_to_codecdata (const GValue * streamheader,
1260 GstMatroskaTrackContext * context)
1266 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1267 GST_WARNING ("No or invalid streamheader field in the caps");
1271 bufarr = g_value_peek_pointer (streamheader);
1272 if (bufarr->len != 2) {
1273 GST_WARNING ("Too few headers in streamheader field");
1277 context->xiph_headers_to_skip = bufarr->len + 1;
1279 bufval = &g_array_index (bufarr, GValue, 0);
1280 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1281 GST_WARNING ("streamheaders array does not contain GstBuffers");
1285 buffer = g_value_peek_pointer (bufval);
1287 if (GST_BUFFER_SIZE (buffer) < 80
1288 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1289 GST_WARNING ("Invalid streamheader for Speex");
1293 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1294 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1295 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1296 GST_BUFFER_SIZE (buffer));
1298 bufval = &g_array_index (bufarr, GValue, 1);
1300 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1301 g_free (context->codec_priv);
1302 context->codec_priv = NULL;
1303 context->codec_priv_size = 0;
1304 GST_WARNING ("streamheaders array does not contain GstBuffers");
1308 buffer = g_value_peek_pointer (bufval);
1310 context->codec_priv =
1311 g_realloc (context->codec_priv,
1312 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1313 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1314 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1315 context->codec_priv_size =
1316 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1321 static const gchar *
1322 aac_codec_data_to_codec_id (const GstBuffer * buf)
1324 const gchar *result;
1327 /* default to MAIN */
1330 if (GST_BUFFER_SIZE (buf) >= 2) {
1331 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1349 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1358 * gst_matroska_mux_audio_pad_setcaps:
1359 * @pad: Pad which got the caps.
1362 * Setcaps function for audio sink pad.
1364 * Returns: #TRUE on success.
1367 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1369 GstMatroskaTrackContext *context = NULL;
1370 GstMatroskaTrackAudioContext *audiocontext;
1371 GstMatroskaMux *mux;
1372 GstMatroskaPad *collect_pad;
1373 const gchar *mimetype;
1374 gint samplerate = 0, channels = 0;
1375 GstStructure *structure;
1376 const GValue *codec_data = NULL;
1377 const GstBuffer *buf = NULL;
1378 const gchar *stream_format = NULL;
1380 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1383 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1384 g_assert (collect_pad);
1385 context = collect_pad->track;
1387 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1388 audiocontext = (GstMatroskaTrackAudioContext *) context;
1390 structure = gst_caps_get_structure (caps, 0);
1391 mimetype = gst_structure_get_name (structure);
1394 gst_structure_get_int (structure, "rate", &samplerate);
1395 gst_structure_get_int (structure, "channels", &channels);
1397 audiocontext->samplerate = samplerate;
1398 audiocontext->channels = channels;
1399 audiocontext->bitdepth = 0;
1400 context->default_duration = 0;
1402 codec_data = gst_structure_get_value (structure, "codec_data");
1404 buf = gst_value_get_buffer (codec_data);
1406 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1407 * data and other settings
1411 if (!strcmp (mimetype, "audio/mpeg")) {
1412 gint mpegversion = 0;
1414 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1415 switch (mpegversion) {
1421 gst_structure_get_int (structure, "layer", &layer);
1423 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1424 GST_WARNING_OBJECT (mux,
1425 "Unable to determine MPEG audio version, assuming 1");
1431 else if (layer == 2)
1433 else if (version == 2)
1438 context->default_duration =
1439 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1443 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1446 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1449 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1458 stream_format = gst_structure_get_string (structure, "stream-format");
1459 /* check this is raw aac */
1460 if (stream_format) {
1461 if (strcmp (stream_format, "raw") != 0) {
1462 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1466 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1471 if (mpegversion == 2)
1473 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1474 aac_codec_data_to_codec_id (buf));
1475 else if (mpegversion == 4)
1477 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1478 aac_codec_data_to_codec_id (buf));
1480 g_assert_not_reached ();
1482 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1489 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1491 gint endianness = G_LITTLE_ENDIAN;
1492 gboolean signedness = TRUE;
1494 if (!gst_structure_get_int (structure, "width", &width) ||
1495 !gst_structure_get_int (structure, "depth", &depth) ||
1496 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1497 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1502 !gst_structure_get_int (structure, "endianness", &endianness)) {
1503 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1507 if (width != depth) {
1508 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1512 /* FIXME: where is this spec'ed out? (tpm) */
1513 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1514 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1518 audiocontext->bitdepth = depth;
1519 if (endianness == G_BIG_ENDIAN)
1520 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1522 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1524 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1527 if (!gst_structure_get_int (structure, "width", &width)) {
1528 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1532 audiocontext->bitdepth = width;
1533 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1535 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1536 const GValue *streamheader;
1538 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1540 if (context->codec_priv != NULL) {
1541 g_free (context->codec_priv);
1542 context->codec_priv = NULL;
1543 context->codec_priv_size = 0;
1546 streamheader = gst_structure_get_value (structure, "streamheader");
1547 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1548 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1549 ("vorbis stream headers missing or malformed"));
1552 } else if (!strcmp (mimetype, "audio/x-flac")) {
1553 const GValue *streamheader;
1555 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1556 if (context->codec_priv != NULL) {
1557 g_free (context->codec_priv);
1558 context->codec_priv = NULL;
1559 context->codec_priv_size = 0;
1562 streamheader = gst_structure_get_value (structure, "streamheader");
1563 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1564 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1565 ("flac stream headers missing or malformed"));
1568 } else if (!strcmp (mimetype, "audio/x-speex")) {
1569 const GValue *streamheader;
1571 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1572 if (context->codec_priv != NULL) {
1573 g_free (context->codec_priv);
1574 context->codec_priv = NULL;
1575 context->codec_priv_size = 0;
1578 streamheader = gst_structure_get_value (structure, "streamheader");
1579 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1580 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1581 ("speex stream headers missing or malformed"));
1584 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1585 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1586 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1588 } else if (!strcmp (mimetype, "audio/x-dts")) {
1589 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1590 } else if (!strcmp (mimetype, "audio/x-tta")) {
1593 /* TTA frame duration */
1594 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1596 gst_structure_get_int (structure, "width", &width);
1597 audiocontext->bitdepth = width;
1598 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1600 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1602 const GValue *mdpr_data;
1604 gst_structure_get_int (structure, "raversion", &raversion);
1605 switch (raversion) {
1607 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1610 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1613 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1619 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1620 if (mdpr_data != NULL) {
1621 guint8 *priv_data = NULL;
1622 guint priv_data_size = 0;
1624 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1626 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1627 priv_data = g_malloc0 (priv_data_size);
1629 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1631 context->codec_priv = priv_data;
1632 context->codec_priv_size = priv_data_size;
1635 } else if (!strcmp (mimetype, "audio/x-wma")) {
1637 guint codec_priv_size;
1644 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1645 || !gst_structure_get_int (structure, "block_align", &block_align)
1646 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1647 || samplerate == 0 || channels == 0) {
1648 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1649 "channels/rate on WMA caps");
1653 switch (wmaversion) {
1655 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1658 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1661 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1664 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1668 if (gst_structure_get_int (structure, "depth", &depth))
1669 audiocontext->bitdepth = depth;
1671 codec_priv_size = WAVEFORMATEX_SIZE;
1673 codec_priv_size += GST_BUFFER_SIZE (buf);
1675 /* serialize waveformatex structure */
1676 codec_priv = g_malloc0 (codec_priv_size);
1677 GST_WRITE_UINT16_LE (codec_priv, format);
1678 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1679 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1680 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1681 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1682 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1684 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1686 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1688 /* process codec private/initialization data, if any */
1690 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1691 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1694 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1695 context->codec_priv = (gpointer) codec_priv;
1696 context->codec_priv_size = codec_priv_size;
1704 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1705 GST_PAD_NAME (pad), caps);
1712 * gst_matroska_mux_subtitle_pad_setcaps:
1713 * @pad: Pad which got the caps.
1716 * Setcaps function for subtitle sink pad.
1718 * Returns: #TRUE on success.
1721 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1724 * Consider this as boilerplate code for now. There is
1725 * no single subtitle creation element in GStreamer,
1726 * neither do I know how subtitling works at all. */
1728 /* There is now (at least) one such alement (kateenc), and I'm going
1729 to handle it here and claim it works when it can be piped back
1730 through GStreamer and VLC */
1732 GstMatroskaTrackContext *context = NULL;
1733 GstMatroskaTrackSubtitleContext *scontext;
1734 GstMatroskaMux *mux;
1735 GstMatroskaPad *collect_pad;
1736 const gchar *mimetype;
1737 GstStructure *structure;
1739 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1742 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1743 g_assert (collect_pad);
1744 context = collect_pad->track;
1746 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1747 scontext = (GstMatroskaTrackSubtitleContext *) context;
1749 structure = gst_caps_get_structure (caps, 0);
1750 mimetype = gst_structure_get_name (structure);
1753 scontext->check_utf8 = 1;
1754 scontext->invalid_utf8 = 0;
1755 context->default_duration = 0;
1757 /* TODO: - other format than Kate */
1759 if (!strcmp (mimetype, "subtitle/x-kate")) {
1760 const GValue *streamheader;
1762 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1764 if (context->codec_priv != NULL) {
1765 g_free (context->codec_priv);
1766 context->codec_priv = NULL;
1767 context->codec_priv_size = 0;
1770 streamheader = gst_structure_get_value (structure, "streamheader");
1771 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1772 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1773 ("kate stream headers missing or malformed"));
1784 * gst_matroska_mux_request_new_pad:
1785 * @element: #GstMatroskaMux.
1786 * @templ: #GstPadTemplate.
1787 * @pad_name: New pad name.
1789 * Request pad function for sink templates.
1791 * Returns: New #GstPad.
1794 gst_matroska_mux_request_new_pad (GstElement * element,
1795 GstPadTemplate * templ, const gchar * req_name)
1797 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1798 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1799 GstMatroskaPad *collect_pad;
1800 GstPad *newpad = NULL;
1802 const gchar *pad_name = NULL;
1803 GstPadSetCapsFunction setcapsfunc = NULL;
1804 GstMatroskaTrackContext *context = NULL;
1807 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1808 /* don't mix named and unnamed pads, if the pad already exists we fail when
1809 * trying to add it */
1810 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1811 pad_name = req_name;
1813 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1816 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1817 context = (GstMatroskaTrackContext *)
1818 g_new0 (GstMatroskaTrackAudioContext, 1);
1819 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1820 context->name = g_strdup ("Audio");
1821 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1822 /* don't mix named and unnamed pads, if the pad already exists we fail when
1823 * trying to add it */
1824 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1825 pad_name = req_name;
1827 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1830 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1831 context = (GstMatroskaTrackContext *)
1832 g_new0 (GstMatroskaTrackVideoContext, 1);
1833 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1834 context->name = g_strdup ("Video");
1835 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1836 /* don't mix named and unnamed pads, if the pad already exists we fail when
1837 * trying to add it */
1838 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1839 pad_name = req_name;
1841 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1844 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1845 context = (GstMatroskaTrackContext *)
1846 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1847 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1848 context->name = g_strdup ("Subtitle");
1850 GST_WARNING_OBJECT (mux, "This is not our template!");
1854 newpad = gst_pad_new_from_template (templ, pad_name);
1856 collect_pad = (GstMatroskaPad *)
1857 gst_collect_pads_add_pad_full (mux->collect, newpad,
1858 sizeof (GstMatroskaPad),
1859 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1861 collect_pad->track = context;
1862 gst_matroska_pad_reset (collect_pad, FALSE);
1864 /* FIXME: hacked way to override/extend the event function of
1865 * GstCollectPads; because it sets its own event function giving the
1866 * element no access to events.
1867 * TODO GstCollectPads should really give its 'users' a clean chance to
1868 * properly handle events that are not meant for collectpads itself.
1869 * Perhaps a callback or so, though rejected (?) in #340060.
1870 * This would allow (clean) transcoding of info from demuxer/streams
1871 * to another muxer */
1872 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1873 gst_pad_set_event_function (newpad,
1874 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1876 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1877 gst_pad_set_active (newpad, TRUE);
1878 if (!gst_element_add_pad (element, newpad))
1879 goto pad_add_failed;
1883 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1890 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1891 gst_object_unref (newpad);
1897 * gst_matroska_mux_release_pad:
1898 * @element: #GstMatroskaMux.
1899 * @pad: Pad to release.
1901 * Release a previously requested pad.
1904 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1906 GstMatroskaMux *mux;
1909 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1911 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1912 GstCollectData *cdata = (GstCollectData *) walk->data;
1913 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1915 if (cdata->pad == pad) {
1916 GstClockTime min_dur; /* observed minimum duration */
1918 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1919 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1920 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1921 if (collect_pad->duration < min_dur)
1922 collect_pad->duration = min_dur;
1925 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1926 mux->duration < collect_pad->duration)
1927 mux->duration = collect_pad->duration;
1934 gst_collect_pads_remove_pad (mux->collect, pad);
1936 if (gst_element_remove_pad (element, pad))
1942 * gst_matroska_mux_track_header:
1943 * @mux: #GstMatroskaMux
1944 * @context: Tack context.
1946 * Write a track header.
1949 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1950 GstMatroskaTrackContext * context)
1952 GstEbmlWrite *ebml = mux->ebml_write;
1955 /* TODO: check if everything necessary is written and check default values */
1957 /* track type goes before the type-specific stuff */
1958 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1959 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1961 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1962 gst_matroska_mux_create_uid ());
1963 if (context->default_duration) {
1964 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1965 context->default_duration);
1967 if (context->language) {
1968 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1972 /* type-specific stuff */
1973 switch (context->type) {
1974 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1975 GstMatroskaTrackVideoContext *videocontext =
1976 (GstMatroskaTrackVideoContext *) context;
1978 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1979 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1980 videocontext->pixel_width);
1981 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1982 videocontext->pixel_height);
1983 if (videocontext->display_width && videocontext->display_height) {
1984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1985 videocontext->display_width);
1986 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1987 videocontext->display_height);
1989 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1990 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1991 if (videocontext->fourcc) {
1992 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1994 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1995 (gpointer) & fcc_le, 4);
1997 gst_ebml_write_master_finish (ebml, master);
2002 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2003 GstMatroskaTrackAudioContext *audiocontext =
2004 (GstMatroskaTrackAudioContext *) context;
2006 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2007 if (audiocontext->samplerate != 8000)
2008 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2009 audiocontext->samplerate);
2010 if (audiocontext->channels != 1)
2011 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2012 audiocontext->channels);
2013 if (audiocontext->bitdepth) {
2014 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2015 audiocontext->bitdepth);
2017 gst_ebml_write_master_finish (ebml, master);
2023 /* doesn't need type-specific data */
2027 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2028 if (context->codec_priv)
2029 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2030 context->codec_priv, context->codec_priv_size);
2031 /* FIXME: until we have a nice way of getting the codecname
2032 * out of the caps, I'm not going to enable this. Too much
2033 * (useless, double, boring) work... */
2034 /* TODO: Use value from tags if any */
2035 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2036 context->codec_name); */
2037 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2042 * gst_matroska_mux_start:
2043 * @mux: #GstMatroskaMux
2045 * Start a new matroska file (write headers etc...)
2048 gst_matroska_mux_start (GstMatroskaMux * mux)
2050 GstEbmlWrite *ebml = mux->ebml_write;
2051 const gchar *doctype;
2052 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2053 GST_MATROSKA_ID_TRACKS,
2054 GST_MATROSKA_ID_CUES,
2055 GST_MATROSKA_ID_TAGS,
2058 guint64 master, child;
2062 GstClockTime duration = 0;
2063 guint32 segment_uid[4];
2064 GTimeVal time = { 0, 0 };
2066 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2067 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2069 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2071 /* we start with a EBML header */
2072 doctype = mux->doctype;
2073 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2074 doctype, mux->doctype_version);
2075 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2077 /* the rest of the header is cached */
2078 gst_ebml_write_set_cache (ebml, 0x1000);
2080 /* start a segment */
2082 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2083 mux->segment_master = ebml->pos;
2085 if (!mux->streamable) {
2086 /* seekhead (table of contents) - we set the positions later */
2087 mux->seekhead_pos = ebml->pos;
2088 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2089 for (i = 0; seekhead_id[i] != 0; i++) {
2090 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2091 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2092 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2093 gst_ebml_write_master_finish (ebml, child);
2095 gst_ebml_write_master_finish (ebml, master);
2099 mux->info_pos = ebml->pos;
2100 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2101 for (i = 0; i < 4; i++) {
2102 segment_uid[i] = g_random_int ();
2104 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2105 (guint8 *) segment_uid, 16);
2106 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2107 mux->duration_pos = ebml->pos;
2109 if (!mux->streamable) {
2110 for (collected = mux->collect->data; collected;
2111 collected = g_slist_next (collected)) {
2112 GstMatroskaPad *collect_pad;
2113 GstFormat format = GST_FORMAT_TIME;
2115 gint64 trackduration;
2117 collect_pad = (GstMatroskaPad *) collected->data;
2118 thepad = collect_pad->collect.pad;
2120 /* Query the total length of the track. */
2121 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2122 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2123 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2124 GST_TIME_ARGS (trackduration));
2125 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2126 duration = (GstClockTime) trackduration;
2130 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2131 gst_guint64_to_gdouble (duration) /
2132 gst_guint64_to_gdouble (mux->time_scale));
2134 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2135 "GStreamer plugin version " PACKAGE_VERSION);
2136 if (mux->writing_app && mux->writing_app[0]) {
2137 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2139 g_get_current_time (&time);
2140 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2141 gst_ebml_write_master_finish (ebml, master);
2144 mux->tracks_pos = ebml->pos;
2145 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2147 for (collected = mux->collect->data; collected;
2148 collected = g_slist_next (collected)) {
2149 GstMatroskaPad *collect_pad;
2152 collect_pad = (GstMatroskaPad *) collected->data;
2153 thepad = collect_pad->collect.pad;
2155 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2156 collect_pad->track->codec_id != 0) {
2157 collect_pad->track->num = tracknum++;
2158 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2159 gst_matroska_mux_track_header (mux, collect_pad->track);
2160 gst_ebml_write_master_finish (ebml, child);
2163 gst_ebml_write_master_finish (ebml, master);
2165 /* lastly, flush the cache */
2166 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2170 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2173 /* TODO: more sensible tag mappings */
2176 const gchar *matroska_tagname;
2177 const gchar *gstreamer_tagname;
2181 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2182 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2183 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2184 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2185 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2186 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2187 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2188 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2189 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2190 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2191 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2192 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2193 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2194 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2195 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2197 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2199 guint64 simpletag_master;
2201 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2202 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2203 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2205 if (strcmp (tagname_gst, tag) == 0) {
2206 GValue src = { 0, };
2209 if (!gst_tag_list_copy_value (&src, list, tag))
2211 if ((dest = gst_value_serialize (&src))) {
2213 simpletag_master = gst_ebml_write_master_start (ebml,
2214 GST_MATROSKA_ID_SIMPLETAG);
2215 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2216 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2217 gst_ebml_write_master_finish (ebml, simpletag_master);
2220 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2222 g_value_unset (&src);
2230 * gst_matroska_mux_finish:
2231 * @mux: #GstMatroskaMux
2233 * Finish a new matroska file (write index etc...)
2236 gst_matroska_mux_finish (GstMatroskaMux * mux)
2238 GstEbmlWrite *ebml = mux->ebml_write;
2240 guint64 duration = 0;
2242 const GstTagList *tags;
2244 /* finish last cluster */
2246 gst_ebml_write_master_finish (ebml, mux->cluster);
2250 if (mux->index != NULL) {
2252 guint64 master, pointentry_master, trackpos_master;
2254 mux->cues_pos = ebml->pos;
2255 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2256 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2258 for (n = 0; n < mux->num_indexes; n++) {
2259 GstMatroskaIndex *idx = &mux->index[n];
2261 pointentry_master = gst_ebml_write_master_start (ebml,
2262 GST_MATROSKA_ID_POINTENTRY);
2263 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2264 idx->time / mux->time_scale);
2265 trackpos_master = gst_ebml_write_master_start (ebml,
2266 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2267 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2268 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2269 idx->pos - mux->segment_master);
2270 gst_ebml_write_master_finish (ebml, trackpos_master);
2271 gst_ebml_write_master_finish (ebml, pointentry_master);
2274 gst_ebml_write_master_finish (ebml, master);
2275 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2279 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2281 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2282 guint64 master_tags, master_tag;
2284 GST_DEBUG ("Writing tags");
2286 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2287 mux->tags_pos = ebml->pos;
2288 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2289 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2290 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2291 gst_ebml_write_master_finish (ebml, master_tag);
2292 gst_ebml_write_master_finish (ebml, master_tags);
2295 /* update seekhead. We know that:
2296 * - a seekhead contains 4 entries.
2297 * - order of entries is as above.
2298 * - a seekhead has a 4-byte header + 8-byte length
2299 * - each entry is 2-byte master, 2-byte ID pointer,
2300 * 2-byte length pointer, all 8/1-byte length, 4-
2301 * byte ID and 8-byte length pointer, where the
2302 * length pointer starts at 20.
2303 * - all entries are local to the segment (so pos - segment_master).
2304 * - so each entry is at 12 + 20 + num * 28. */
2305 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2306 mux->info_pos - mux->segment_master);
2307 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2308 mux->tracks_pos - mux->segment_master);
2309 if (mux->index != NULL) {
2310 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2311 mux->cues_pos - mux->segment_master);
2314 guint64 my_pos = ebml->pos;
2316 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2317 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2318 gst_ebml_write_seek (ebml, my_pos);
2321 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2322 mux->tags_pos - mux->segment_master);
2325 guint64 my_pos = ebml->pos;
2327 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2328 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2329 gst_ebml_write_seek (ebml, my_pos);
2332 /* update duration */
2333 /* first get the overall duration */
2334 /* a released track may have left a duration in here */
2335 duration = mux->duration;
2336 for (collected = mux->collect->data; collected;
2337 collected = g_slist_next (collected)) {
2338 GstMatroskaPad *collect_pad;
2339 GstClockTime min_duration; /* observed minimum duration */
2341 collect_pad = (GstMatroskaPad *) collected->data;
2343 GST_DEBUG_OBJECT (mux,
2344 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2345 " end ts %" GST_TIME_FORMAT, collect_pad,
2346 GST_TIME_ARGS (collect_pad->start_ts),
2347 GST_TIME_ARGS (collect_pad->end_ts));
2349 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2350 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2352 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2353 if (collect_pad->duration < min_duration)
2354 collect_pad->duration = min_duration;
2355 GST_DEBUG_OBJECT (collect_pad,
2356 "final track duration: %" GST_TIME_FORMAT,
2357 GST_TIME_ARGS (collect_pad->duration));
2360 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2361 duration < collect_pad->duration)
2362 duration = collect_pad->duration;
2364 if (duration != 0) {
2365 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2366 GST_TIME_ARGS (duration));
2367 pos = mux->ebml_write->pos;
2368 gst_ebml_write_seek (ebml, mux->duration_pos);
2369 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2370 gst_guint64_to_gdouble (duration) /
2371 gst_guint64_to_gdouble (mux->time_scale));
2372 gst_ebml_write_seek (ebml, pos);
2375 guint64 my_pos = ebml->pos;
2377 gst_ebml_write_seek (ebml, mux->duration_pos);
2378 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2379 gst_ebml_write_seek (ebml, my_pos);
2381 GST_DEBUG_OBJECT (mux, "finishing segment");
2382 /* finish segment - this also writes element length */
2383 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2388 * gst_matroska_mux_best_pad:
2389 * @mux: #GstMatroskaMux
2390 * @popped: True if at least one buffer was popped from #GstCollectPads
2392 * Find a pad with the oldest data
2393 * (data from this pad should be written first).
2395 * Returns: Selected pad.
2397 static GstMatroskaPad *
2398 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2401 GstMatroskaPad *best = NULL;
2404 for (collected = mux->collect->data; collected;
2405 collected = g_slist_next (collected)) {
2406 GstMatroskaPad *collect_pad;
2408 collect_pad = (GstMatroskaPad *) collected->data;
2409 /* fetch a new buffer if needed */
2410 if (collect_pad->buffer == NULL) {
2411 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2412 (GstCollectData *) collect_pad);
2414 if (collect_pad->buffer != NULL)
2418 /* if we have a buffer check if it is better then the current best one */
2419 if (collect_pad->buffer != NULL) {
2420 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2421 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2422 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2423 GST_BUFFER_TIMESTAMP (best->buffer))) {
2433 * gst_matroska_mux_buffer_header:
2434 * @track: Track context.
2435 * @relative_timestamp: relative timestamp of the buffer
2436 * @flags: Buffer flags.
2438 * Create a buffer containing buffer header.
2440 * Returns: New buffer.
2443 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2444 gint16 relative_timestamp, int flags)
2448 hdr = gst_buffer_new_and_alloc (4);
2449 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2450 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2451 /* time relative to clustertime */
2452 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2455 GST_BUFFER_DATA (hdr)[3] = flags;
2460 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2461 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2462 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2465 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2466 GstMatroskaPad * collect_pad, GstBuffer * buf)
2468 GstMatroskaTrackVideoContext *ctx =
2469 (GstMatroskaTrackVideoContext *) collect_pad->track;
2470 const guint8 *data = GST_BUFFER_DATA (buf);
2471 guint size = GST_BUFFER_SIZE (buf);
2473 guint32 next_parse_offset;
2474 GstBuffer *ret = NULL;
2475 gboolean is_muxing_unit = FALSE;
2477 if (GST_BUFFER_SIZE (buf) < 13) {
2478 gst_buffer_unref (buf);
2482 /* Check if this buffer contains a picture or end-of-sequence packet */
2483 while (size >= 13) {
2484 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2485 gst_buffer_unref (buf);
2489 parse_code = GST_READ_UINT8 (data + 4);
2490 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2491 if (ctx->dirac_unit) {
2492 gst_buffer_unref (ctx->dirac_unit);
2493 ctx->dirac_unit = NULL;
2495 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2496 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2497 is_muxing_unit = TRUE;
2501 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2503 if (G_UNLIKELY (next_parse_offset == 0))
2506 data += next_parse_offset;
2507 size -= next_parse_offset;
2510 if (ctx->dirac_unit)
2511 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2513 ctx->dirac_unit = gst_buffer_ref (buf);
2515 if (is_muxing_unit) {
2516 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2517 ctx->dirac_unit = NULL;
2518 gst_buffer_copy_metadata (ret, buf,
2519 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2520 GST_BUFFER_COPY_CAPS);
2521 gst_buffer_unref (buf);
2523 gst_buffer_unref (buf);
2531 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2535 GValue streamheader = { 0 };
2536 GValue bufval = { 0 };
2537 GstBuffer *streamheader_buffer;
2538 GstEbmlWrite *ebml = mux->ebml_write;
2540 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2541 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2542 caps = gst_caps_new_simple ("video/webm", NULL);
2544 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2546 s = gst_caps_get_structure (caps, 0);
2547 g_value_init (&streamheader, GST_TYPE_ARRAY);
2548 g_value_init (&bufval, GST_TYPE_BUFFER);
2549 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2550 gst_value_set_buffer (&bufval, streamheader_buffer);
2551 gst_value_array_append_value (&streamheader, &bufval);
2552 g_value_unset (&bufval);
2553 gst_structure_set_value (s, "streamheader", &streamheader);
2554 g_value_unset (&streamheader);
2555 gst_caps_replace (&ebml->caps, caps);
2556 gst_buffer_unref (streamheader_buffer);
2557 gst_caps_unref (caps);
2561 * gst_matroska_mux_write_data:
2562 * @mux: #GstMatroskaMux
2563 * @collect_pad: #GstMatroskaPad with the data
2565 * Write collected data (called from gst_matroska_mux_collected).
2567 * Returns: Result of the gst_pad_push issued to write the data.
2569 static GstFlowReturn
2570 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2572 GstEbmlWrite *ebml = mux->ebml_write;
2573 GstBuffer *buf, *hdr;
2575 gboolean write_duration;
2576 gint16 relative_timestamp;
2577 gint64 relative_timestamp64;
2578 guint64 block_duration;
2579 gboolean is_video_keyframe = FALSE;
2582 buf = collect_pad->buffer;
2583 collect_pad->buffer = NULL;
2585 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2586 if (collect_pad->track->xiph_headers_to_skip > 0) {
2587 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2588 gst_buffer_unref (buf);
2589 --collect_pad->track->xiph_headers_to_skip;
2593 /* for dirac we have to queue up everything up to a picture unit */
2594 if (collect_pad->track->codec_id != NULL &&
2595 strcmp (collect_pad->track->codec_id,
2596 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2597 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2602 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2603 * this would wreak havoc with time stored in matroska file */
2604 /* TODO: maybe calculate a timestamp by using the previous timestamp
2605 * and default duration */
2606 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2607 GST_WARNING_OBJECT (collect_pad->collect.pad,
2608 "Invalid buffer timestamp; dropping buffer");
2609 gst_buffer_unref (buf);
2613 /* set the timestamp for outgoing buffers */
2614 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2616 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2617 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2618 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2619 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2620 is_video_keyframe = TRUE;
2624 /* start a new cluster at every keyframe or when we may be reaching the
2625 * limit of the relative timestamp */
2626 if (mux->cluster_time +
2627 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2628 || is_video_keyframe) {
2629 if (!mux->streamable)
2630 gst_ebml_write_master_finish (ebml, mux->cluster);
2631 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2632 mux->cluster_pos = ebml->pos;
2633 gst_ebml_write_set_cache (ebml, 0x20);
2635 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2636 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2637 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2639 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2640 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2642 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2643 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2644 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2645 mux->prev_cluster_size);
2650 mux->cluster_pos = ebml->pos;
2651 gst_ebml_write_set_cache (ebml, 0x20);
2652 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2653 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2654 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2655 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2656 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2659 /* update duration of this track */
2660 if (GST_BUFFER_DURATION_IS_VALID (buf))
2661 collect_pad->duration += GST_BUFFER_DURATION (buf);
2663 /* We currently write index entries for all video tracks or for the audio
2664 * track in a single-track audio file. This could be improved by keeping the
2665 * index only for the *first* video track. */
2667 /* TODO: index is useful for every track, should contain the number of
2668 * the block in the cluster which contains the timestamp, should also work
2669 * for files with multiple audio tracks.
2671 if (is_video_keyframe ||
2672 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2673 (mux->num_streams == 1))) {
2676 if (mux->min_index_interval != 0) {
2677 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2678 if (mux->index[last_idx].track == collect_pad->track->num)
2683 if (last_idx < 0 || mux->min_index_interval == 0 ||
2684 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2685 >= mux->min_index_interval)) {
2686 GstMatroskaIndex *idx;
2688 if (mux->num_indexes % 32 == 0) {
2689 mux->index = g_renew (GstMatroskaIndex, mux->index,
2690 mux->num_indexes + 32);
2692 idx = &mux->index[mux->num_indexes++];
2694 idx->pos = mux->cluster_pos;
2695 idx->time = GST_BUFFER_TIMESTAMP (buf);
2696 idx->track = collect_pad->track->num;
2700 /* Check if the duration differs from the default duration. */
2701 write_duration = FALSE;
2702 block_duration = GST_BUFFER_DURATION (buf);
2703 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2704 if (block_duration != collect_pad->track->default_duration) {
2705 write_duration = TRUE;
2709 /* write the block, for doctype v2 use SimpleBlock if possible
2710 * one slice (*breath*).
2711 * FIXME: Need to do correct lacing! */
2712 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2713 if (relative_timestamp64 >= 0) {
2714 /* round the timestamp */
2715 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2717 /* round the timestamp */
2718 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2720 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2722 if (mux->doctype_version > 1 && !write_duration) {
2724 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2727 gst_matroska_mux_create_buffer_header (collect_pad->track,
2728 relative_timestamp, flags);
2729 gst_ebml_write_set_cache (ebml, 0x40);
2730 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2731 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2732 gst_ebml_write_buffer (ebml, hdr);
2733 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2734 gst_ebml_write_buffer (ebml, buf);
2736 return gst_ebml_last_write_result (ebml);
2738 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2739 /* write and call order slightly unnatural,
2740 * but avoids seek and minizes pushing */
2741 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2743 gst_matroska_mux_create_buffer_header (collect_pad->track,
2744 relative_timestamp, 0);
2745 if (write_duration) {
2746 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2747 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2749 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2750 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2751 gst_ebml_write_buffer (ebml, hdr);
2752 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2753 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2754 gst_ebml_write_buffer (ebml, buf);
2756 return gst_ebml_last_write_result (ebml);
2762 * gst_matroska_mux_collected:
2763 * @pads: #GstCollectPads
2764 * @uuser_data: #GstMatroskaMux
2766 * Collectpads callback.
2768 * Returns: #GstFlowReturn
2770 static GstFlowReturn
2771 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2773 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2774 GstEbmlWrite *ebml = mux->ebml_write;
2775 GstMatroskaPad *best;
2779 GST_DEBUG_OBJECT (mux, "Collected pads");
2781 /* start with a header */
2782 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2783 if (mux->collect->data == NULL) {
2784 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2785 ("No input streams configured"));
2786 return GST_FLOW_ERROR;
2788 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2789 gst_ebml_start_streamheader (ebml);
2790 gst_matroska_mux_start (mux);
2791 gst_matroska_mux_stop_streamheader (mux);
2792 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2796 /* which stream to write from? */
2797 best = gst_matroska_mux_best_pad (mux, &popped);
2799 /* if there is no best pad, we have reached EOS */
2801 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2802 if (!mux->streamable) {
2803 gst_matroska_mux_finish (mux);
2805 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2807 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2808 ret = GST_FLOW_UNEXPECTED;
2811 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2812 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2813 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2814 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2816 /* make note of first and last encountered timestamps, so we can calculate
2817 * the actual duration later when we send an updated header on eos */
2818 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2819 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2820 GstClockTime end_ts = start_ts;
2822 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2823 end_ts += GST_BUFFER_DURATION (best->buffer);
2824 else if (best->track->default_duration)
2825 end_ts += best->track->default_duration;
2827 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2828 best->end_ts = end_ts;
2830 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2831 start_ts < best->start_ts))
2832 best->start_ts = start_ts;
2835 /* write one buffer */
2836 ret = gst_matroska_mux_write_data (mux, best);
2837 } while (ret == GST_FLOW_OK && !popped);
2844 * gst_matroska_mux_change_state:
2845 * @element: #GstMatroskaMux
2846 * @transition: State change transition.
2848 * Change the muxer state.
2850 * Returns: #GstStateChangeReturn
2852 static GstStateChangeReturn
2853 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2855 GstStateChangeReturn ret;
2856 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2858 switch (transition) {
2859 case GST_STATE_CHANGE_NULL_TO_READY:
2861 case GST_STATE_CHANGE_READY_TO_PAUSED:
2862 gst_collect_pads_start (mux->collect);
2864 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2866 case GST_STATE_CHANGE_PAUSED_TO_READY:
2867 gst_collect_pads_stop (mux->collect);
2873 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2875 switch (transition) {
2876 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2878 case GST_STATE_CHANGE_PAUSED_TO_READY:
2879 gst_matroska_mux_reset (GST_ELEMENT (mux));
2881 case GST_STATE_CHANGE_READY_TO_NULL:
2891 gst_matroska_mux_set_property (GObject * object,
2892 guint prop_id, const GValue * value, GParamSpec * pspec)
2894 GstMatroskaMux *mux;
2896 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2897 mux = GST_MATROSKA_MUX (object);
2900 case ARG_WRITING_APP:
2901 if (!g_value_get_string (value)) {
2902 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2905 g_free (mux->writing_app);
2906 mux->writing_app = g_value_dup_string (value);
2908 case ARG_DOCTYPE_VERSION:
2909 mux->doctype_version = g_value_get_int (value);
2911 case ARG_MIN_INDEX_INTERVAL:
2912 mux->min_index_interval = g_value_get_int64 (value);
2914 case ARG_STREAMABLE:
2915 mux->streamable = g_value_get_boolean (value);
2918 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2924 gst_matroska_mux_get_property (GObject * object,
2925 guint prop_id, GValue * value, GParamSpec * pspec)
2927 GstMatroskaMux *mux;
2929 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2930 mux = GST_MATROSKA_MUX (object);
2933 case ARG_WRITING_APP:
2934 g_value_set_string (value, mux->writing_app);
2936 case ARG_DOCTYPE_VERSION:
2937 g_value_set_int (value, mux->doctype_version);
2939 case ARG_MIN_INDEX_INTERVAL:
2940 g_value_set_int64 (value, mux->min_index_interval);
2942 case ARG_STREAMABLE:
2943 g_value_set_boolean (value, mux->streamable);
2946 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);