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 "; "
105 "video/x-h264, stream-format=avc, alignment=au, "
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 ], "
200 COMMON_AUDIO_CAPS ";"
202 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
204 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
207 static GstStaticPadTemplate subtitlesink_templ =
208 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
211 GST_STATIC_CAPS ("subtitle/x-kate"));
213 static GArray *used_uids;
214 G_LOCK_DEFINE_STATIC (used_uids);
216 static void gst_matroska_mux_add_interfaces (GType type);
218 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
219 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
221 /* Matroska muxer destructor */
222 static void gst_matroska_mux_finalize (GObject * object);
224 /* Pads collected callback */
226 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
229 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
231 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
232 GstPadTemplate * templ, const gchar * name);
233 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
235 /* gst internal change state handler */
236 static GstStateChangeReturn
237 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
239 /* gobject bla bla */
240 static void gst_matroska_mux_set_property (GObject * object,
241 guint prop_id, const GValue * value, GParamSpec * pspec);
242 static void gst_matroska_mux_get_property (GObject * object,
243 guint prop_id, GValue * value, GParamSpec * pspec);
246 static void gst_matroska_mux_reset (GstElement * element);
249 static guint64 gst_matroska_mux_create_uid ();
251 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
253 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
254 GstMatroskaTrackContext * context);
255 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
256 GstMatroskaTrackContext * context);
257 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
258 GstMatroskaTrackContext * context);
259 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
260 GstMatroskaTrackContext * context);
262 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
266 gst_matroska_mux_add_interfaces (GType type)
268 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
270 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
274 gst_matroska_mux_base_init (gpointer g_class)
279 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
281 GObjectClass *gobject_class;
282 GstElementClass *gstelement_class;
284 gobject_class = (GObjectClass *) klass;
285 gstelement_class = (GstElementClass *) klass;
287 gst_element_class_add_pad_template (gstelement_class,
288 gst_static_pad_template_get (&videosink_templ));
289 gst_element_class_add_pad_template (gstelement_class,
290 gst_static_pad_template_get (&audiosink_templ));
291 gst_element_class_add_pad_template (gstelement_class,
292 gst_static_pad_template_get (&subtitlesink_templ));
293 gst_element_class_add_pad_template (gstelement_class,
294 gst_static_pad_template_get (&src_templ));
295 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
297 "Muxes video/audio/subtitle streams into a matroska stream",
298 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
300 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
303 gobject_class->finalize = gst_matroska_mux_finalize;
305 gobject_class->get_property = gst_matroska_mux_get_property;
306 gobject_class->set_property = gst_matroska_mux_set_property;
308 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
309 g_param_spec_string ("writing-app", "Writing application.",
310 "The name the application that creates the matroska file.",
311 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
312 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
313 g_param_spec_int ("version", "DocType version",
314 "This parameter determines what Matroska features can be used.",
315 1, 2, DEFAULT_DOCTYPE_VERSION,
316 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
317 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
318 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
319 "entries", "An index entry is created every so many nanoseconds.",
320 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
321 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
322 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
323 g_param_spec_boolean ("streamable", "Determines whether output should "
324 "be streamable", "If set to true, the output should be as if it is "
325 "to be streamed and hence no indexes written or duration written.",
327 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
329 gstelement_class->change_state =
330 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
331 gstelement_class->request_new_pad =
332 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
333 gstelement_class->release_pad =
334 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
339 * gst_matroska_mux_init:
340 * @mux: #GstMatroskaMux that should be initialized.
341 * @g_class: Class of the muxer.
343 * Matroska muxer constructor.
346 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
348 GstPadTemplate *templ;
351 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
352 mux->srcpad = gst_pad_new_from_template (templ, "src");
354 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
355 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
357 mux->collect = gst_collect_pads_new ();
358 gst_collect_pads_set_function (mux->collect,
359 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
362 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
363 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
365 /* property defaults */
366 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
367 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
368 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
369 mux->streamable = DEFAULT_STREAMABLE;
371 /* initialize internal variables */
373 mux->num_streams = 0;
374 mux->num_a_streams = 0;
375 mux->num_t_streams = 0;
376 mux->num_v_streams = 0;
378 /* initialize remaining variables */
379 gst_matroska_mux_reset (GST_ELEMENT (mux));
384 * gst_matroska_mux_finalize:
385 * @object: #GstMatroskaMux that should be finalized.
387 * Finalize matroska muxer.
390 gst_matroska_mux_finalize (GObject * object)
392 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
394 gst_event_replace (&mux->force_key_unit_event, NULL);
396 gst_object_unref (mux->collect);
397 gst_object_unref (mux->ebml_write);
398 if (mux->writing_app)
399 g_free (mux->writing_app);
401 G_OBJECT_CLASS (parent_class)->finalize (object);
406 * gst_matroska_mux_create_uid:
408 * Generate new unused track UID.
410 * Returns: New track UID.
413 gst_matroska_mux_create_uid (void)
420 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
425 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
426 for (i = 0; i < used_uids->len; i++) {
427 if (g_array_index (used_uids, guint64, i) == uid) {
432 g_array_append_val (used_uids, uid);
435 G_UNLOCK (used_uids);
441 * gst_matroska_pad_reset:
442 * @collect_pad: the #GstMatroskaPad
444 * Reset and/or release resources of a matroska collect pad.
447 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
450 GstMatroskaTrackType type = 0;
452 /* free track information */
453 if (collect_pad->track != NULL) {
454 /* retrieve for optional later use */
455 name = collect_pad->track->name;
456 type = collect_pad->track->type;
457 /* extra for video */
458 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
459 GstMatroskaTrackVideoContext *ctx =
460 (GstMatroskaTrackVideoContext *) collect_pad->track;
462 if (ctx->dirac_unit) {
463 gst_buffer_unref (ctx->dirac_unit);
464 ctx->dirac_unit = NULL;
467 g_free (collect_pad->track->codec_id);
468 g_free (collect_pad->track->codec_name);
470 g_free (collect_pad->track->name);
471 g_free (collect_pad->track->language);
472 g_free (collect_pad->track->codec_priv);
473 g_free (collect_pad->track);
474 collect_pad->track = NULL;
477 /* free cached buffer */
478 if (collect_pad->buffer != NULL) {
479 gst_buffer_unref (collect_pad->buffer);
480 collect_pad->buffer = NULL;
483 if (!full && type != 0) {
484 GstMatroskaTrackContext *context;
486 /* create a fresh context */
488 case GST_MATROSKA_TRACK_TYPE_VIDEO:
489 context = (GstMatroskaTrackContext *)
490 g_new0 (GstMatroskaTrackVideoContext, 1);
492 case GST_MATROSKA_TRACK_TYPE_AUDIO:
493 context = (GstMatroskaTrackContext *)
494 g_new0 (GstMatroskaTrackAudioContext, 1);
496 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
497 context = (GstMatroskaTrackContext *)
498 g_new0 (GstMatroskaTrackSubtitleContext, 1);
501 g_assert_not_reached ();
505 context->type = type;
506 context->name = name;
507 /* TODO: check default values for the context */
508 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
509 collect_pad->track = context;
510 collect_pad->buffer = NULL;
511 collect_pad->duration = 0;
512 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
513 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
518 * gst_matroska_pad_free:
519 * @collect_pad: the #GstMatroskaPad
521 * Release resources of a matroska collect pad.
524 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
526 gst_matroska_pad_reset (collect_pad, TRUE);
531 * gst_matroska_mux_reset:
532 * @element: #GstMatroskaMux that should be reseted.
534 * Reset matroska muxer back to initial state.
537 gst_matroska_mux_reset (GstElement * element)
539 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
542 /* reset EBML write */
543 gst_ebml_write_reset (mux->ebml_write);
546 mux->state = GST_MATROSKA_MUX_STATE_START;
548 /* clean up existing streams */
550 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
551 GstMatroskaPad *collect_pad;
553 collect_pad = (GstMatroskaPad *) walk->data;
555 /* reset collect pad to pristine state */
556 gst_matroska_pad_reset (collect_pad, FALSE);
560 mux->num_indexes = 0;
565 mux->time_scale = GST_MSECOND;
566 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
571 mux->cluster_time = 0;
572 mux->cluster_pos = 0;
573 mux->prev_cluster_size = 0;
576 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
580 * gst_matroska_mux_handle_src_event:
581 * @pad: Pad which received the event.
582 * @event: Received event.
584 * handle events - copied from oggmux without understanding
586 * Returns: #TRUE on success.
589 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
593 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
597 /* disable seeking for now */
603 return gst_pad_event_default (pad, event);
607 * gst_matroska_mux_handle_sink_event:
608 * @pad: Pad which received the event.
609 * @event: Received event.
611 * handle events - informational ones like tags
613 * Returns: #TRUE on success.
616 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
618 GstMatroskaTrackContext *context;
619 GstMatroskaPad *collect_pad;
624 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
626 switch (GST_EVENT_TYPE (event)) {
630 GST_DEBUG_OBJECT (mux, "received tag event");
631 gst_event_parse_tag (event, &list);
633 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
634 g_assert (collect_pad);
635 context = collect_pad->track;
638 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
639 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
640 const gchar *lang_code;
642 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
644 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
645 context->language = g_strdup (lang_code);
647 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
652 /* FIXME: what about stream-specific tags? */
653 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
654 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
656 gst_event_unref (event);
657 /* handled this, don't want collectpads to forward it downstream */
661 case GST_EVENT_NEWSEGMENT:
662 /* We don't support NEWSEGMENT events */
664 gst_event_unref (event);
667 case GST_EVENT_CUSTOM_DOWNSTREAM:{
668 const GstStructure *structure;
670 structure = gst_event_get_structure (event);
671 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
672 gst_event_replace (&mux->force_key_unit_event, NULL);
673 mux->force_key_unit_event = event;
682 /* now GstCollectPads can take care of the rest, e.g. EOS */
684 ret = mux->collect_event (pad, event);
686 gst_object_unref (mux);
693 * gst_matroska_mux_video_pad_setcaps:
694 * @pad: Pad which got the caps.
697 * Setcaps function for video sink pad.
699 * Returns: #TRUE on success.
702 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
704 GstMatroskaTrackContext *context = NULL;
705 GstMatroskaTrackVideoContext *videocontext;
707 GstMatroskaPad *collect_pad;
708 GstStructure *structure;
709 const gchar *mimetype;
710 const GValue *value = NULL;
711 const GstBuffer *codec_buf = NULL;
712 gint width, height, pixel_width, pixel_height;
714 gboolean interlaced = FALSE;
716 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
719 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
720 g_assert (collect_pad);
721 context = collect_pad->track;
723 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
724 videocontext = (GstMatroskaTrackVideoContext *) context;
726 /* gst -> matroska ID'ing */
727 structure = gst_caps_get_structure (caps, 0);
729 mimetype = gst_structure_get_name (structure);
731 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
733 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
735 if (!strcmp (mimetype, "video/x-theora")) {
736 /* we'll extract the details later from the theora identification header */
740 /* get general properties */
741 /* spec says it is mandatory */
742 if (!gst_structure_get_int (structure, "width", &width) ||
743 !gst_structure_get_int (structure, "height", &height))
746 videocontext->pixel_width = width;
747 videocontext->pixel_height = height;
748 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
750 context->default_duration =
751 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
752 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
753 GST_TIME_ARGS (context->default_duration));
755 context->default_duration = 0;
757 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
758 &pixel_width, &pixel_height)) {
759 if (pixel_width > pixel_height) {
760 videocontext->display_width = width * pixel_width / pixel_height;
761 videocontext->display_height = height;
762 } else if (pixel_width < pixel_height) {
763 videocontext->display_width = width;
764 videocontext->display_height = height * pixel_height / pixel_width;
766 videocontext->display_width = 0;
767 videocontext->display_height = 0;
770 videocontext->display_width = 0;
771 videocontext->display_height = 0;
776 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
777 videocontext->fourcc = 0;
779 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
780 * data and other settings
784 /* extract codec_data, may turn out needed */
785 value = gst_structure_get_value (structure, "codec_data");
787 codec_buf = gst_value_get_buffer (value);
790 if (!strcmp (mimetype, "video/x-raw-yuv")) {
791 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
792 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
793 } else if (!strcmp (mimetype, "image/jpeg")) {
794 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
795 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
796 ||!strcmp (mimetype, "video/x-huffyuv")
797 || !strcmp (mimetype, "video/x-divx")
798 || !strcmp (mimetype, "video/x-dv")
799 || !strcmp (mimetype, "video/x-h263")
800 || !strcmp (mimetype, "video/x-msmpeg")
801 || !strcmp (mimetype, "video/x-wmv")) {
802 gst_riff_strf_vids *bih;
803 gint size = sizeof (gst_riff_strf_vids);
806 if (!strcmp (mimetype, "video/x-xvid"))
807 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
808 else if (!strcmp (mimetype, "video/x-huffyuv"))
809 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
810 else if (!strcmp (mimetype, "video/x-dv"))
811 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
812 else if (!strcmp (mimetype, "video/x-h263"))
813 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
814 else if (!strcmp (mimetype, "video/x-divx")) {
817 gst_structure_get_int (structure, "divxversion", &divxversion);
818 switch (divxversion) {
820 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
823 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
826 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
829 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
832 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
833 switch (msmpegversion) {
835 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
838 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
844 } else if (!strcmp (mimetype, "video/x-wmv")) {
847 if (gst_structure_get_fourcc (structure, "format", &format)) {
849 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
850 if (wmvversion == 2) {
851 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
852 } else if (wmvversion == 1) {
853 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
854 } else if (wmvversion == 3) {
855 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
863 bih = g_new0 (gst_riff_strf_vids, 1);
864 GST_WRITE_UINT32_LE (&bih->size, size);
865 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
866 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
867 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
868 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
869 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
870 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
871 videocontext->pixel_height * 3);
873 /* process codec private/initialization data, if any */
875 size += GST_BUFFER_SIZE (codec_buf);
876 bih = g_realloc (bih, size);
877 GST_WRITE_UINT32_LE (&bih->size, size);
878 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
879 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
882 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
883 context->codec_priv = (gpointer) bih;
884 context->codec_priv_size = size;
885 } else if (!strcmp (mimetype, "video/x-h264")) {
886 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
888 if (context->codec_priv != NULL) {
889 g_free (context->codec_priv);
890 context->codec_priv = NULL;
891 context->codec_priv_size = 0;
894 /* Create avcC header */
895 if (codec_buf != NULL) {
896 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
897 context->codec_priv = g_malloc0 (context->codec_priv_size);
898 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
899 context->codec_priv_size);
901 } else if (!strcmp (mimetype, "video/x-theora")) {
902 const GValue *streamheader;
904 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
906 if (context->codec_priv != NULL) {
907 g_free (context->codec_priv);
908 context->codec_priv = NULL;
909 context->codec_priv_size = 0;
912 streamheader = gst_structure_get_value (structure, "streamheader");
913 if (!theora_streamheader_to_codecdata (streamheader, context)) {
914 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
915 ("theora stream headers missing or malformed"));
918 } else if (!strcmp (mimetype, "video/x-dirac")) {
919 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
920 } else if (!strcmp (mimetype, "video/x-vp8")) {
921 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
922 } else if (!strcmp (mimetype, "video/mpeg")) {
925 gst_structure_get_int (structure, "mpegversion", &mpegversion);
926 switch (mpegversion) {
928 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
931 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
934 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
940 /* global headers may be in codec data */
941 if (codec_buf != NULL) {
942 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
943 context->codec_priv = g_malloc0 (context->codec_priv_size);
944 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
945 context->codec_priv_size);
947 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
949 /* can only make it here if preceding case verified it was version 3 */
950 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
951 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
953 const GValue *mdpr_data;
955 gst_structure_get_int (structure, "rmversion", &rmversion);
958 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
961 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
964 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
967 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
973 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
974 if (mdpr_data != NULL) {
975 guint8 *priv_data = NULL;
976 guint priv_data_size = 0;
978 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
980 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
981 priv_data = g_malloc0 (priv_data_size);
983 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
985 context->codec_priv = priv_data;
986 context->codec_priv_size = priv_data_size;
995 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
996 GST_PAD_NAME (pad), caps);
1001 /* N > 0 to expect a particular number of headers, negative if the
1002 number of headers is variable */
1004 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1005 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1007 GstBuffer **buf = NULL;
1010 guint bufi, i, offset, priv_data_size;
1012 if (streamheader == NULL)
1013 goto no_stream_headers;
1015 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1018 bufarr = g_value_peek_pointer (streamheader);
1019 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1021 if (N > 0 && bufarr->len != N)
1024 context->xiph_headers_to_skip = bufarr->len;
1026 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1027 for (i = 0; i < bufarr->len; i++) {
1028 GValue *bufval = &g_array_index (bufarr, GValue, i);
1030 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1032 goto wrong_content_type;
1035 buf[i] = g_value_peek_pointer (bufval);
1039 if (bufarr->len > 0) {
1040 for (i = 0; i < bufarr->len - 1; i++) {
1041 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1045 for (i = 0; i < bufarr->len; ++i) {
1046 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1049 priv_data = g_malloc0 (priv_data_size);
1051 priv_data[0] = bufarr->len - 1;
1054 if (bufarr->len > 0) {
1055 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1056 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1057 priv_data[offset++] = 0xff;
1059 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1063 for (i = 0; i < bufarr->len; ++i) {
1064 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1065 GST_BUFFER_SIZE (buf[i]));
1066 offset += GST_BUFFER_SIZE (buf[i]);
1069 context->codec_priv = priv_data;
1070 context->codec_priv_size = priv_data_size;
1073 *p_buf0 = gst_buffer_ref (buf[0]);
1082 GST_WARNING ("required streamheaders missing in sink caps!");
1087 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1088 G_VALUE_TYPE_NAME (streamheader));
1093 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1098 GST_WARNING ("streamheaders array does not contain GstBuffers");
1104 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1105 GstMatroskaTrackContext * context)
1107 GstBuffer *buf0 = NULL;
1109 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1112 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1113 GST_WARNING ("First vorbis header too small, ignoring");
1115 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1116 GstMatroskaTrackAudioContext *audiocontext;
1119 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1120 audiocontext = (GstMatroskaTrackAudioContext *) context;
1121 audiocontext->channels = GST_READ_UINT8 (hdr);
1122 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1127 gst_buffer_unref (buf0);
1133 theora_streamheader_to_codecdata (const GValue * streamheader,
1134 GstMatroskaTrackContext * context)
1136 GstBuffer *buf0 = NULL;
1138 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1141 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1142 GST_WARNING ("First theora header too small, ignoring");
1143 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1144 GST_WARNING ("First header not a theora identification header, ignoring");
1146 GstMatroskaTrackVideoContext *videocontext;
1147 guint fps_num, fps_denom, par_num, par_denom;
1150 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1152 videocontext = (GstMatroskaTrackVideoContext *) context;
1153 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1154 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1155 hdr += 3 + 3 + 1 + 1;
1156 fps_num = GST_READ_UINT32_BE (hdr);
1157 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1158 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1159 fps_denom, fps_num);
1161 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1162 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1163 if (par_num > 0 && par_num > 0) {
1164 if (par_num > par_denom) {
1165 videocontext->display_width =
1166 videocontext->pixel_width * par_num / par_denom;
1167 videocontext->display_height = videocontext->pixel_height;
1168 } else if (par_num < par_denom) {
1169 videocontext->display_width = videocontext->pixel_width;
1170 videocontext->display_height =
1171 videocontext->pixel_height * par_denom / par_num;
1173 videocontext->display_width = 0;
1174 videocontext->display_height = 0;
1177 videocontext->display_width = 0;
1178 videocontext->display_height = 0;
1184 gst_buffer_unref (buf0);
1190 kate_streamheader_to_codecdata (const GValue * streamheader,
1191 GstMatroskaTrackContext * context)
1193 GstBuffer *buf0 = NULL;
1195 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1198 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1199 GST_WARNING ("First kate header too small, ignoring");
1200 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1201 GST_WARNING ("First header not a kate identification header, ignoring");
1205 gst_buffer_unref (buf0);
1211 flac_streamheader_to_codecdata (const GValue * streamheader,
1212 GstMatroskaTrackContext * context)
1219 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1220 GST_WARNING ("No or invalid streamheader field in the caps");
1224 bufarr = g_value_peek_pointer (streamheader);
1225 if (bufarr->len < 2) {
1226 GST_WARNING ("Too few headers in streamheader field");
1230 context->xiph_headers_to_skip = bufarr->len + 1;
1232 bufval = &g_array_index (bufarr, GValue, 0);
1233 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1234 GST_WARNING ("streamheaders array does not contain GstBuffers");
1238 buffer = g_value_peek_pointer (bufval);
1240 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1241 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1242 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1243 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1244 GST_WARNING ("Invalid streamheader for FLAC");
1248 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1249 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1250 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1251 GST_BUFFER_SIZE (buffer) - 9);
1253 for (i = 1; i < bufarr->len; i++) {
1254 bufval = &g_array_index (bufarr, GValue, i);
1256 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1257 g_free (context->codec_priv);
1258 context->codec_priv = NULL;
1259 context->codec_priv_size = 0;
1260 GST_WARNING ("streamheaders array does not contain GstBuffers");
1264 buffer = g_value_peek_pointer (bufval);
1266 context->codec_priv =
1267 g_realloc (context->codec_priv,
1268 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1269 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1270 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1271 context->codec_priv_size =
1272 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1279 speex_streamheader_to_codecdata (const GValue * streamheader,
1280 GstMatroskaTrackContext * context)
1286 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1287 GST_WARNING ("No or invalid streamheader field in the caps");
1291 bufarr = g_value_peek_pointer (streamheader);
1292 if (bufarr->len != 2) {
1293 GST_WARNING ("Too few headers in streamheader field");
1297 context->xiph_headers_to_skip = bufarr->len + 1;
1299 bufval = &g_array_index (bufarr, GValue, 0);
1300 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1301 GST_WARNING ("streamheaders array does not contain GstBuffers");
1305 buffer = g_value_peek_pointer (bufval);
1307 if (GST_BUFFER_SIZE (buffer) < 80
1308 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1309 GST_WARNING ("Invalid streamheader for Speex");
1313 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1314 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1315 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1316 GST_BUFFER_SIZE (buffer));
1318 bufval = &g_array_index (bufarr, GValue, 1);
1320 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1321 g_free (context->codec_priv);
1322 context->codec_priv = NULL;
1323 context->codec_priv_size = 0;
1324 GST_WARNING ("streamheaders array does not contain GstBuffers");
1328 buffer = g_value_peek_pointer (bufval);
1330 context->codec_priv =
1331 g_realloc (context->codec_priv,
1332 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1333 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1334 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1335 context->codec_priv_size =
1336 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1341 static const gchar *
1342 aac_codec_data_to_codec_id (const GstBuffer * buf)
1344 const gchar *result;
1347 /* default to MAIN */
1350 if (GST_BUFFER_SIZE (buf) >= 2) {
1351 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1369 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1378 * gst_matroska_mux_audio_pad_setcaps:
1379 * @pad: Pad which got the caps.
1382 * Setcaps function for audio sink pad.
1384 * Returns: #TRUE on success.
1387 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1389 GstMatroskaTrackContext *context = NULL;
1390 GstMatroskaTrackAudioContext *audiocontext;
1391 GstMatroskaMux *mux;
1392 GstMatroskaPad *collect_pad;
1393 const gchar *mimetype;
1394 gint samplerate = 0, channels = 0;
1395 GstStructure *structure;
1396 const GValue *codec_data = NULL;
1397 const GstBuffer *buf = NULL;
1398 const gchar *stream_format = NULL;
1400 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1403 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1404 g_assert (collect_pad);
1405 context = collect_pad->track;
1407 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1408 audiocontext = (GstMatroskaTrackAudioContext *) context;
1410 structure = gst_caps_get_structure (caps, 0);
1411 mimetype = gst_structure_get_name (structure);
1414 gst_structure_get_int (structure, "rate", &samplerate);
1415 gst_structure_get_int (structure, "channels", &channels);
1417 audiocontext->samplerate = samplerate;
1418 audiocontext->channels = channels;
1419 audiocontext->bitdepth = 0;
1420 context->default_duration = 0;
1422 codec_data = gst_structure_get_value (structure, "codec_data");
1424 buf = gst_value_get_buffer (codec_data);
1426 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1427 * data and other settings
1431 if (!strcmp (mimetype, "audio/mpeg")) {
1432 gint mpegversion = 0;
1434 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1435 switch (mpegversion) {
1441 gst_structure_get_int (structure, "layer", &layer);
1443 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1444 GST_WARNING_OBJECT (mux,
1445 "Unable to determine MPEG audio version, assuming 1");
1451 else if (layer == 2)
1453 else if (version == 2)
1458 context->default_duration =
1459 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1463 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1466 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1469 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1478 stream_format = gst_structure_get_string (structure, "stream-format");
1479 /* check this is raw aac */
1480 if (stream_format) {
1481 if (strcmp (stream_format, "raw") != 0) {
1482 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1486 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1491 if (mpegversion == 2)
1493 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1494 aac_codec_data_to_codec_id (buf));
1495 else if (mpegversion == 4)
1497 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1498 aac_codec_data_to_codec_id (buf));
1500 g_assert_not_reached ();
1502 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1509 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1511 gint endianness = G_LITTLE_ENDIAN;
1512 gboolean signedness = TRUE;
1514 if (!gst_structure_get_int (structure, "width", &width) ||
1515 !gst_structure_get_int (structure, "depth", &depth) ||
1516 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1517 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1522 !gst_structure_get_int (structure, "endianness", &endianness)) {
1523 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1527 if (width != depth) {
1528 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1532 /* FIXME: where is this spec'ed out? (tpm) */
1533 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1534 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1538 audiocontext->bitdepth = depth;
1539 if (endianness == G_BIG_ENDIAN)
1540 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1542 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1544 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1547 if (!gst_structure_get_int (structure, "width", &width)) {
1548 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1552 audiocontext->bitdepth = width;
1553 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1555 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1556 const GValue *streamheader;
1558 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1560 if (context->codec_priv != NULL) {
1561 g_free (context->codec_priv);
1562 context->codec_priv = NULL;
1563 context->codec_priv_size = 0;
1566 streamheader = gst_structure_get_value (structure, "streamheader");
1567 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1568 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1569 ("vorbis stream headers missing or malformed"));
1572 } else if (!strcmp (mimetype, "audio/x-flac")) {
1573 const GValue *streamheader;
1575 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1576 if (context->codec_priv != NULL) {
1577 g_free (context->codec_priv);
1578 context->codec_priv = NULL;
1579 context->codec_priv_size = 0;
1582 streamheader = gst_structure_get_value (structure, "streamheader");
1583 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1584 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1585 ("flac stream headers missing or malformed"));
1588 } else if (!strcmp (mimetype, "audio/x-speex")) {
1589 const GValue *streamheader;
1591 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1592 if (context->codec_priv != NULL) {
1593 g_free (context->codec_priv);
1594 context->codec_priv = NULL;
1595 context->codec_priv_size = 0;
1598 streamheader = gst_structure_get_value (structure, "streamheader");
1599 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1600 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1601 ("speex stream headers missing or malformed"));
1604 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1605 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1606 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1607 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1608 } else if (!strcmp (mimetype, "audio/x-dts")) {
1609 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1610 } else if (!strcmp (mimetype, "audio/x-tta")) {
1613 /* TTA frame duration */
1614 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1616 gst_structure_get_int (structure, "width", &width);
1617 audiocontext->bitdepth = width;
1618 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1620 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1622 const GValue *mdpr_data;
1624 gst_structure_get_int (structure, "raversion", &raversion);
1625 switch (raversion) {
1627 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1630 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1633 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1639 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1640 if (mdpr_data != NULL) {
1641 guint8 *priv_data = NULL;
1642 guint priv_data_size = 0;
1644 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1646 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1647 priv_data = g_malloc0 (priv_data_size);
1649 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1651 context->codec_priv = priv_data;
1652 context->codec_priv_size = priv_data_size;
1655 } else if (!strcmp (mimetype, "audio/x-wma")
1656 || !strcmp (mimetype, "audio/x-alaw")
1657 || !strcmp (mimetype, "audio/x-mulaw")) {
1659 guint codec_priv_size;
1664 if (samplerate == 0 || channels == 0) {
1665 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1669 if (!strcmp (mimetype, "audio/x-wma")) {
1673 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1674 || !gst_structure_get_int (structure, "block_align", &block_align)
1675 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1676 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1681 switch (wmaversion) {
1683 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1686 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1689 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1692 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1696 if (gst_structure_get_int (structure, "depth", &depth))
1697 audiocontext->bitdepth = depth;
1698 } else if (!strcmp (mimetype, "audio/x-alaw")
1699 || !strcmp (mimetype, "audio/x-mulaw")) {
1700 audiocontext->bitdepth = 8;
1701 if (!strcmp (mimetype, "audio/x-alaw"))
1702 format = GST_RIFF_WAVE_FORMAT_ALAW;
1704 format = GST_RIFF_WAVE_FORMAT_MULAW;
1706 block_align = channels;
1707 bitrate = block_align * samplerate;
1709 g_assert (format != 0);
1711 codec_priv_size = WAVEFORMATEX_SIZE;
1713 codec_priv_size += GST_BUFFER_SIZE (buf);
1715 /* serialize waveformatex structure */
1716 codec_priv = g_malloc0 (codec_priv_size);
1717 GST_WRITE_UINT16_LE (codec_priv, format);
1718 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1719 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1720 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1721 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1722 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1724 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1726 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1728 /* process codec private/initialization data, if any */
1730 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1731 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1734 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1735 context->codec_priv = (gpointer) codec_priv;
1736 context->codec_priv_size = codec_priv_size;
1744 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1745 GST_PAD_NAME (pad), caps);
1752 * gst_matroska_mux_subtitle_pad_setcaps:
1753 * @pad: Pad which got the caps.
1756 * Setcaps function for subtitle sink pad.
1758 * Returns: #TRUE on success.
1761 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1764 * Consider this as boilerplate code for now. There is
1765 * no single subtitle creation element in GStreamer,
1766 * neither do I know how subtitling works at all. */
1768 /* There is now (at least) one such alement (kateenc), and I'm going
1769 to handle it here and claim it works when it can be piped back
1770 through GStreamer and VLC */
1772 GstMatroskaTrackContext *context = NULL;
1773 GstMatroskaTrackSubtitleContext *scontext;
1774 GstMatroskaMux *mux;
1775 GstMatroskaPad *collect_pad;
1776 const gchar *mimetype;
1777 GstStructure *structure;
1779 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1782 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1783 g_assert (collect_pad);
1784 context = collect_pad->track;
1786 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1787 scontext = (GstMatroskaTrackSubtitleContext *) context;
1789 structure = gst_caps_get_structure (caps, 0);
1790 mimetype = gst_structure_get_name (structure);
1793 scontext->check_utf8 = 1;
1794 scontext->invalid_utf8 = 0;
1795 context->default_duration = 0;
1797 /* TODO: - other format than Kate */
1799 if (!strcmp (mimetype, "subtitle/x-kate")) {
1800 const GValue *streamheader;
1802 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1804 if (context->codec_priv != NULL) {
1805 g_free (context->codec_priv);
1806 context->codec_priv = NULL;
1807 context->codec_priv_size = 0;
1810 streamheader = gst_structure_get_value (structure, "streamheader");
1811 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1812 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1813 ("kate stream headers missing or malformed"));
1824 * gst_matroska_mux_request_new_pad:
1825 * @element: #GstMatroskaMux.
1826 * @templ: #GstPadTemplate.
1827 * @pad_name: New pad name.
1829 * Request pad function for sink templates.
1831 * Returns: New #GstPad.
1834 gst_matroska_mux_request_new_pad (GstElement * element,
1835 GstPadTemplate * templ, const gchar * req_name)
1837 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1838 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1839 GstMatroskaPad *collect_pad;
1840 GstPad *newpad = NULL;
1842 const gchar *pad_name = NULL;
1843 GstPadSetCapsFunction setcapsfunc = NULL;
1844 GstMatroskaTrackContext *context = NULL;
1847 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1848 /* don't mix named and unnamed pads, if the pad already exists we fail when
1849 * trying to add it */
1850 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1851 pad_name = req_name;
1853 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1856 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1857 context = (GstMatroskaTrackContext *)
1858 g_new0 (GstMatroskaTrackAudioContext, 1);
1859 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1860 context->name = g_strdup ("Audio");
1861 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1862 /* don't mix named and unnamed pads, if the pad already exists we fail when
1863 * trying to add it */
1864 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1865 pad_name = req_name;
1867 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1870 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1871 context = (GstMatroskaTrackContext *)
1872 g_new0 (GstMatroskaTrackVideoContext, 1);
1873 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1874 context->name = g_strdup ("Video");
1875 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1876 /* don't mix named and unnamed pads, if the pad already exists we fail when
1877 * trying to add it */
1878 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1879 pad_name = req_name;
1881 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1884 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1885 context = (GstMatroskaTrackContext *)
1886 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1887 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1888 context->name = g_strdup ("Subtitle");
1890 GST_WARNING_OBJECT (mux, "This is not our template!");
1894 newpad = gst_pad_new_from_template (templ, pad_name);
1896 collect_pad = (GstMatroskaPad *)
1897 gst_collect_pads_add_pad_full (mux->collect, newpad,
1898 sizeof (GstMatroskaPad),
1899 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1901 collect_pad->track = context;
1902 gst_matroska_pad_reset (collect_pad, FALSE);
1904 /* FIXME: hacked way to override/extend the event function of
1905 * GstCollectPads; because it sets its own event function giving the
1906 * element no access to events.
1907 * TODO GstCollectPads should really give its 'users' a clean chance to
1908 * properly handle events that are not meant for collectpads itself.
1909 * Perhaps a callback or so, though rejected (?) in #340060.
1910 * This would allow (clean) transcoding of info from demuxer/streams
1911 * to another muxer */
1912 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1913 gst_pad_set_event_function (newpad,
1914 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1916 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1917 gst_pad_set_active (newpad, TRUE);
1918 if (!gst_element_add_pad (element, newpad))
1919 goto pad_add_failed;
1923 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1930 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1931 gst_object_unref (newpad);
1937 * gst_matroska_mux_release_pad:
1938 * @element: #GstMatroskaMux.
1939 * @pad: Pad to release.
1941 * Release a previously requested pad.
1944 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1946 GstMatroskaMux *mux;
1949 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1951 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1952 GstCollectData *cdata = (GstCollectData *) walk->data;
1953 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1955 if (cdata->pad == pad) {
1956 GstClockTime min_dur; /* observed minimum duration */
1958 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1959 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1960 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1961 if (collect_pad->duration < min_dur)
1962 collect_pad->duration = min_dur;
1965 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1966 mux->duration < collect_pad->duration)
1967 mux->duration = collect_pad->duration;
1973 gst_collect_pads_remove_pad (mux->collect, pad);
1974 if (gst_element_remove_pad (element, pad))
1980 * gst_matroska_mux_track_header:
1981 * @mux: #GstMatroskaMux
1982 * @context: Tack context.
1984 * Write a track header.
1987 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1988 GstMatroskaTrackContext * context)
1990 GstEbmlWrite *ebml = mux->ebml_write;
1993 /* TODO: check if everything necessary is written and check default values */
1995 /* track type goes before the type-specific stuff */
1996 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1997 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1999 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2000 gst_matroska_mux_create_uid ());
2001 if (context->default_duration) {
2002 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2003 context->default_duration);
2005 if (context->language) {
2006 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2010 /* type-specific stuff */
2011 switch (context->type) {
2012 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2013 GstMatroskaTrackVideoContext *videocontext =
2014 (GstMatroskaTrackVideoContext *) context;
2016 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2017 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2018 videocontext->pixel_width);
2019 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2020 videocontext->pixel_height);
2021 if (videocontext->display_width && videocontext->display_height) {
2022 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2023 videocontext->display_width);
2024 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2025 videocontext->display_height);
2027 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2028 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2029 if (videocontext->fourcc) {
2030 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2032 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2033 (gpointer) & fcc_le, 4);
2035 gst_ebml_write_master_finish (ebml, master);
2040 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2041 GstMatroskaTrackAudioContext *audiocontext =
2042 (GstMatroskaTrackAudioContext *) context;
2044 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2045 if (audiocontext->samplerate != 8000)
2046 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2047 audiocontext->samplerate);
2048 if (audiocontext->channels != 1)
2049 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2050 audiocontext->channels);
2051 if (audiocontext->bitdepth) {
2052 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2053 audiocontext->bitdepth);
2055 gst_ebml_write_master_finish (ebml, master);
2061 /* doesn't need type-specific data */
2065 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2066 if (context->codec_priv)
2067 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2068 context->codec_priv, context->codec_priv_size);
2069 /* FIXME: until we have a nice way of getting the codecname
2070 * out of the caps, I'm not going to enable this. Too much
2071 * (useless, double, boring) work... */
2072 /* TODO: Use value from tags if any */
2073 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2074 context->codec_name); */
2075 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2080 * gst_matroska_mux_start:
2081 * @mux: #GstMatroskaMux
2083 * Start a new matroska file (write headers etc...)
2086 gst_matroska_mux_start (GstMatroskaMux * mux)
2088 GstEbmlWrite *ebml = mux->ebml_write;
2089 const gchar *doctype;
2090 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2091 GST_MATROSKA_ID_TRACKS,
2092 GST_MATROSKA_ID_CUES,
2093 GST_MATROSKA_ID_TAGS,
2096 guint64 master, child;
2100 GstClockTime duration = 0;
2101 guint32 segment_uid[4];
2102 GTimeVal time = { 0, 0 };
2104 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2105 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2107 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2109 /* we start with a EBML header */
2110 doctype = mux->doctype;
2111 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2112 doctype, mux->doctype_version);
2113 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2115 /* the rest of the header is cached */
2116 gst_ebml_write_set_cache (ebml, 0x1000);
2118 /* start a segment */
2120 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2121 mux->segment_master = ebml->pos;
2123 if (!mux->streamable) {
2124 /* seekhead (table of contents) - we set the positions later */
2125 mux->seekhead_pos = ebml->pos;
2126 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2127 for (i = 0; seekhead_id[i] != 0; i++) {
2128 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2129 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2130 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2131 gst_ebml_write_master_finish (ebml, child);
2133 gst_ebml_write_master_finish (ebml, master);
2136 if (mux->streamable) {
2137 const GstTagList *tags;
2140 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2142 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2143 guint64 master_tags, master_tag;
2145 GST_DEBUG ("Writing tags");
2147 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2148 mux->tags_pos = ebml->pos;
2149 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2150 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2151 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2152 gst_ebml_write_master_finish (ebml, master_tag);
2153 gst_ebml_write_master_finish (ebml, master_tags);
2158 mux->info_pos = ebml->pos;
2159 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2160 for (i = 0; i < 4; i++) {
2161 segment_uid[i] = g_random_int ();
2163 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2164 (guint8 *) segment_uid, 16);
2165 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2166 mux->duration_pos = ebml->pos;
2168 if (!mux->streamable) {
2169 for (collected = mux->collect->data; collected;
2170 collected = g_slist_next (collected)) {
2171 GstMatroskaPad *collect_pad;
2172 GstFormat format = GST_FORMAT_TIME;
2174 gint64 trackduration;
2176 collect_pad = (GstMatroskaPad *) collected->data;
2177 thepad = collect_pad->collect.pad;
2179 /* Query the total length of the track. */
2180 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2181 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2182 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2183 GST_TIME_ARGS (trackduration));
2184 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2185 duration = (GstClockTime) trackduration;
2189 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2190 gst_guint64_to_gdouble (duration) /
2191 gst_guint64_to_gdouble (mux->time_scale));
2193 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2194 "GStreamer plugin version " PACKAGE_VERSION);
2195 if (mux->writing_app && mux->writing_app[0]) {
2196 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2198 g_get_current_time (&time);
2199 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2200 gst_ebml_write_master_finish (ebml, master);
2203 mux->tracks_pos = ebml->pos;
2204 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2206 for (collected = mux->collect->data; collected;
2207 collected = g_slist_next (collected)) {
2208 GstMatroskaPad *collect_pad;
2211 collect_pad = (GstMatroskaPad *) collected->data;
2212 thepad = collect_pad->collect.pad;
2214 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2215 collect_pad->track->codec_id != 0) {
2216 collect_pad->track->num = tracknum++;
2217 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2218 gst_matroska_mux_track_header (mux, collect_pad->track);
2219 gst_ebml_write_master_finish (ebml, child);
2220 /* some remaing pad/track setup */
2221 collect_pad->default_duration_scaled =
2222 gst_util_uint64_scale (collect_pad->track->default_duration,
2223 1, mux->time_scale);
2226 gst_ebml_write_master_finish (ebml, master);
2228 /* lastly, flush the cache */
2229 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2233 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2236 /* TODO: more sensible tag mappings */
2239 const gchar *matroska_tagname;
2240 const gchar *gstreamer_tagname;
2244 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2245 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2246 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2247 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2248 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2249 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2250 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2251 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2252 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2253 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2254 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2255 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2256 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2257 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2258 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2260 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2262 guint64 simpletag_master;
2264 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2265 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2266 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2268 if (strcmp (tagname_gst, tag) == 0) {
2269 GValue src = { 0, };
2272 if (!gst_tag_list_copy_value (&src, list, tag))
2274 if ((dest = gst_value_serialize (&src))) {
2276 simpletag_master = gst_ebml_write_master_start (ebml,
2277 GST_MATROSKA_ID_SIMPLETAG);
2278 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2279 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2280 gst_ebml_write_master_finish (ebml, simpletag_master);
2283 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2285 g_value_unset (&src);
2293 * gst_matroska_mux_finish:
2294 * @mux: #GstMatroskaMux
2296 * Finish a new matroska file (write index etc...)
2299 gst_matroska_mux_finish (GstMatroskaMux * mux)
2301 GstEbmlWrite *ebml = mux->ebml_write;
2303 guint64 duration = 0;
2305 const GstTagList *tags;
2307 /* finish last cluster */
2309 gst_ebml_write_master_finish (ebml, mux->cluster);
2313 if (mux->index != NULL) {
2315 guint64 master, pointentry_master, trackpos_master;
2317 mux->cues_pos = ebml->pos;
2318 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2319 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2321 for (n = 0; n < mux->num_indexes; n++) {
2322 GstMatroskaIndex *idx = &mux->index[n];
2324 pointentry_master = gst_ebml_write_master_start (ebml,
2325 GST_MATROSKA_ID_POINTENTRY);
2326 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2327 idx->time / mux->time_scale);
2328 trackpos_master = gst_ebml_write_master_start (ebml,
2329 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2330 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2331 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2332 idx->pos - mux->segment_master);
2333 gst_ebml_write_master_finish (ebml, trackpos_master);
2334 gst_ebml_write_master_finish (ebml, pointentry_master);
2337 gst_ebml_write_master_finish (ebml, master);
2338 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2342 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2344 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2345 guint64 master_tags, master_tag;
2347 GST_DEBUG ("Writing tags");
2349 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2350 mux->tags_pos = ebml->pos;
2351 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2352 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2353 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2354 gst_ebml_write_master_finish (ebml, master_tag);
2355 gst_ebml_write_master_finish (ebml, master_tags);
2358 /* update seekhead. We know that:
2359 * - a seekhead contains 4 entries.
2360 * - order of entries is as above.
2361 * - a seekhead has a 4-byte header + 8-byte length
2362 * - each entry is 2-byte master, 2-byte ID pointer,
2363 * 2-byte length pointer, all 8/1-byte length, 4-
2364 * byte ID and 8-byte length pointer, where the
2365 * length pointer starts at 20.
2366 * - all entries are local to the segment (so pos - segment_master).
2367 * - so each entry is at 12 + 20 + num * 28. */
2368 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2369 mux->info_pos - mux->segment_master);
2370 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2371 mux->tracks_pos - mux->segment_master);
2372 if (mux->index != NULL) {
2373 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2374 mux->cues_pos - mux->segment_master);
2377 guint64 my_pos = ebml->pos;
2379 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2380 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2381 gst_ebml_write_seek (ebml, my_pos);
2384 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2385 mux->tags_pos - mux->segment_master);
2388 guint64 my_pos = ebml->pos;
2390 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2391 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2392 gst_ebml_write_seek (ebml, my_pos);
2395 /* update duration */
2396 /* first get the overall duration */
2397 /* a released track may have left a duration in here */
2398 duration = mux->duration;
2399 for (collected = mux->collect->data; collected;
2400 collected = g_slist_next (collected)) {
2401 GstMatroskaPad *collect_pad;
2402 GstClockTime min_duration; /* observed minimum duration */
2404 collect_pad = (GstMatroskaPad *) collected->data;
2406 GST_DEBUG_OBJECT (mux,
2407 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2408 " end ts %" GST_TIME_FORMAT, collect_pad,
2409 GST_TIME_ARGS (collect_pad->start_ts),
2410 GST_TIME_ARGS (collect_pad->end_ts));
2412 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2413 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2415 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2416 if (collect_pad->duration < min_duration)
2417 collect_pad->duration = min_duration;
2418 GST_DEBUG_OBJECT (collect_pad,
2419 "final track duration: %" GST_TIME_FORMAT,
2420 GST_TIME_ARGS (collect_pad->duration));
2423 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2424 duration < collect_pad->duration)
2425 duration = collect_pad->duration;
2427 if (duration != 0) {
2428 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2429 GST_TIME_ARGS (duration));
2430 pos = mux->ebml_write->pos;
2431 gst_ebml_write_seek (ebml, mux->duration_pos);
2432 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2433 gst_guint64_to_gdouble (duration) /
2434 gst_guint64_to_gdouble (mux->time_scale));
2435 gst_ebml_write_seek (ebml, pos);
2438 guint64 my_pos = ebml->pos;
2440 gst_ebml_write_seek (ebml, mux->duration_pos);
2441 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2442 gst_ebml_write_seek (ebml, my_pos);
2444 GST_DEBUG_OBJECT (mux, "finishing segment");
2445 /* finish segment - this also writes element length */
2446 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2451 * gst_matroska_mux_best_pad:
2452 * @mux: #GstMatroskaMux
2453 * @popped: True if at least one buffer was popped from #GstCollectPads
2455 * Find a pad with the oldest data
2456 * (data from this pad should be written first).
2458 * Returns: Selected pad.
2460 static GstMatroskaPad *
2461 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2464 GstMatroskaPad *best = NULL;
2467 for (collected = mux->collect->data; collected;
2468 collected = g_slist_next (collected)) {
2469 GstMatroskaPad *collect_pad;
2471 collect_pad = (GstMatroskaPad *) collected->data;
2472 /* fetch a new buffer if needed */
2473 if (collect_pad->buffer == NULL) {
2474 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2475 (GstCollectData *) collect_pad);
2477 if (collect_pad->buffer != NULL) {
2481 /* convert to running time */
2482 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2483 /* invalid should pass */
2484 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2485 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2486 GST_FORMAT_TIME, time);
2487 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2488 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2489 GST_PAD_NAME (collect_pad->collect.pad));
2490 gst_buffer_unref (collect_pad->buffer);
2491 collect_pad->buffer = NULL;
2494 collect_pad->buffer =
2495 gst_buffer_make_metadata_writable (collect_pad->buffer);
2496 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2502 /* if we have a buffer check if it is better then the current best one */
2503 if (collect_pad->buffer != NULL) {
2504 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2505 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2506 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2507 GST_BUFFER_TIMESTAMP (best->buffer))) {
2517 * gst_matroska_mux_buffer_header:
2518 * @track: Track context.
2519 * @relative_timestamp: relative timestamp of the buffer
2520 * @flags: Buffer flags.
2522 * Create a buffer containing buffer header.
2524 * Returns: New buffer.
2527 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2528 gint16 relative_timestamp, int flags)
2532 hdr = gst_buffer_new_and_alloc (4);
2533 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2534 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2535 /* time relative to clustertime */
2536 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2539 GST_BUFFER_DATA (hdr)[3] = flags;
2544 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2545 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2546 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2549 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2550 GstMatroskaPad * collect_pad, GstBuffer * buf)
2552 GstMatroskaTrackVideoContext *ctx =
2553 (GstMatroskaTrackVideoContext *) collect_pad->track;
2554 const guint8 *data = GST_BUFFER_DATA (buf);
2555 guint size = GST_BUFFER_SIZE (buf);
2557 guint32 next_parse_offset;
2558 GstBuffer *ret = NULL;
2559 gboolean is_muxing_unit = FALSE;
2561 if (GST_BUFFER_SIZE (buf) < 13) {
2562 gst_buffer_unref (buf);
2566 /* Check if this buffer contains a picture or end-of-sequence packet */
2567 while (size >= 13) {
2568 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2569 gst_buffer_unref (buf);
2573 parse_code = GST_READ_UINT8 (data + 4);
2574 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2575 if (ctx->dirac_unit) {
2576 gst_buffer_unref (ctx->dirac_unit);
2577 ctx->dirac_unit = NULL;
2579 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2580 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2581 is_muxing_unit = TRUE;
2585 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2587 if (G_UNLIKELY (next_parse_offset == 0))
2590 data += next_parse_offset;
2591 size -= next_parse_offset;
2594 if (ctx->dirac_unit)
2595 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2597 ctx->dirac_unit = gst_buffer_ref (buf);
2599 if (is_muxing_unit) {
2600 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2601 ctx->dirac_unit = NULL;
2602 gst_buffer_copy_metadata (ret, buf,
2603 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2604 GST_BUFFER_COPY_CAPS);
2605 gst_buffer_unref (buf);
2607 gst_buffer_unref (buf);
2615 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2619 GValue streamheader = { 0 };
2620 GValue bufval = { 0 };
2621 GstBuffer *streamheader_buffer;
2622 GstEbmlWrite *ebml = mux->ebml_write;
2624 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2625 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2626 caps = gst_caps_new_simple ("video/webm", NULL);
2628 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2630 s = gst_caps_get_structure (caps, 0);
2631 g_value_init (&streamheader, GST_TYPE_ARRAY);
2632 g_value_init (&bufval, GST_TYPE_BUFFER);
2633 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2634 gst_value_set_buffer (&bufval, streamheader_buffer);
2635 gst_value_array_append_value (&streamheader, &bufval);
2636 g_value_unset (&bufval);
2637 gst_structure_set_value (s, "streamheader", &streamheader);
2638 g_value_unset (&streamheader);
2639 gst_caps_replace (&ebml->caps, caps);
2640 gst_buffer_unref (streamheader_buffer);
2641 gst_caps_unref (caps);
2645 * gst_matroska_mux_write_data:
2646 * @mux: #GstMatroskaMux
2647 * @collect_pad: #GstMatroskaPad with the data
2649 * Write collected data (called from gst_matroska_mux_collected).
2651 * Returns: Result of the gst_pad_push issued to write the data.
2653 static GstFlowReturn
2654 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2656 GstEbmlWrite *ebml = mux->ebml_write;
2657 GstBuffer *buf, *hdr;
2659 gboolean write_duration;
2660 gint16 relative_timestamp;
2661 gint64 relative_timestamp64;
2662 guint64 block_duration;
2663 gboolean is_video_keyframe = FALSE;
2666 buf = collect_pad->buffer;
2667 collect_pad->buffer = NULL;
2669 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2670 if (collect_pad->track->xiph_headers_to_skip > 0) {
2671 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2672 gst_buffer_unref (buf);
2673 --collect_pad->track->xiph_headers_to_skip;
2677 /* for dirac we have to queue up everything up to a picture unit */
2678 if (collect_pad->track->codec_id != NULL &&
2679 strcmp (collect_pad->track->codec_id,
2680 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2681 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2686 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2687 * this would wreak havoc with time stored in matroska file */
2688 /* TODO: maybe calculate a timestamp by using the previous timestamp
2689 * and default duration */
2690 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2691 GST_WARNING_OBJECT (collect_pad->collect.pad,
2692 "Invalid buffer timestamp; dropping buffer");
2693 gst_buffer_unref (buf);
2697 /* set the timestamp for outgoing buffers */
2698 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2700 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2701 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2702 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2703 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2704 is_video_keyframe = TRUE;
2708 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2709 * or when we may be reaching the limit of the relative timestamp */
2710 if (mux->cluster_time +
2711 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2712 || is_video_keyframe || mux->force_key_unit_event) {
2713 if (!mux->streamable)
2714 gst_ebml_write_master_finish (ebml, mux->cluster);
2716 /* Forward the GstForceKeyUnit event after finishing the cluster */
2717 if (mux->force_key_unit_event) {
2718 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2719 mux->force_key_unit_event = NULL;
2722 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2723 mux->cluster_pos = ebml->pos;
2724 gst_ebml_write_set_cache (ebml, 0x20);
2726 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2727 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2728 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2730 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2731 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2733 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2734 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2735 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2736 mux->prev_cluster_size);
2741 mux->cluster_pos = ebml->pos;
2742 gst_ebml_write_set_cache (ebml, 0x20);
2743 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2744 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2745 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2746 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2747 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2750 /* update duration of this track */
2751 if (GST_BUFFER_DURATION_IS_VALID (buf))
2752 collect_pad->duration += GST_BUFFER_DURATION (buf);
2754 /* We currently write index entries for all video tracks or for the audio
2755 * track in a single-track audio file. This could be improved by keeping the
2756 * index only for the *first* video track. */
2758 /* TODO: index is useful for every track, should contain the number of
2759 * the block in the cluster which contains the timestamp, should also work
2760 * for files with multiple audio tracks.
2762 if (!mux->streamable &&
2763 (is_video_keyframe ||
2764 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2765 (mux->num_streams == 1)))) {
2768 if (mux->min_index_interval != 0) {
2769 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2770 if (mux->index[last_idx].track == collect_pad->track->num)
2775 if (last_idx < 0 || mux->min_index_interval == 0 ||
2776 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2777 >= mux->min_index_interval)) {
2778 GstMatroskaIndex *idx;
2780 if (mux->num_indexes % 32 == 0) {
2781 mux->index = g_renew (GstMatroskaIndex, mux->index,
2782 mux->num_indexes + 32);
2784 idx = &mux->index[mux->num_indexes++];
2786 idx->pos = mux->cluster_pos;
2787 idx->time = GST_BUFFER_TIMESTAMP (buf);
2788 idx->track = collect_pad->track->num;
2792 /* Check if the duration differs from the default duration. */
2793 write_duration = FALSE;
2795 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2796 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
2797 1, mux->time_scale);
2799 /* small difference should be ok. */
2800 if (block_duration > collect_pad->default_duration_scaled + 1 ||
2801 block_duration < collect_pad->default_duration_scaled - 1) {
2802 write_duration = TRUE;
2806 /* write the block, for doctype v2 use SimpleBlock if possible
2807 * one slice (*breath*).
2808 * FIXME: Need to do correct lacing! */
2809 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2810 if (relative_timestamp64 >= 0) {
2811 /* round the timestamp */
2812 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2814 /* round the timestamp */
2815 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2817 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2819 if (mux->doctype_version > 1 && !write_duration) {
2821 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2824 gst_matroska_mux_create_buffer_header (collect_pad->track,
2825 relative_timestamp, flags);
2826 gst_ebml_write_set_cache (ebml, 0x40);
2827 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2828 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2829 gst_ebml_write_buffer (ebml, hdr);
2830 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2831 gst_ebml_write_buffer (ebml, buf);
2833 return gst_ebml_last_write_result (ebml);
2835 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2836 /* write and call order slightly unnatural,
2837 * but avoids seek and minizes pushing */
2838 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2840 gst_matroska_mux_create_buffer_header (collect_pad->track,
2841 relative_timestamp, 0);
2843 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
2844 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2845 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2846 gst_ebml_write_buffer (ebml, hdr);
2847 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2848 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2849 gst_ebml_write_buffer (ebml, buf);
2851 return gst_ebml_last_write_result (ebml);
2857 * gst_matroska_mux_collected:
2858 * @pads: #GstCollectPads
2859 * @uuser_data: #GstMatroskaMux
2861 * Collectpads callback.
2863 * Returns: #GstFlowReturn
2865 static GstFlowReturn
2866 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2868 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2869 GstEbmlWrite *ebml = mux->ebml_write;
2870 GstMatroskaPad *best;
2872 GstFlowReturn ret = GST_FLOW_OK;
2874 GST_DEBUG_OBJECT (mux, "Collected pads");
2876 /* start with a header */
2877 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2878 if (mux->collect->data == NULL) {
2879 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2880 ("No input streams configured"));
2881 return GST_FLOW_ERROR;
2883 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2884 gst_ebml_start_streamheader (ebml);
2885 gst_matroska_mux_start (mux);
2886 gst_matroska_mux_stop_streamheader (mux);
2887 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2891 /* which stream to write from? */
2892 best = gst_matroska_mux_best_pad (mux, &popped);
2894 /* if there is no best pad, we have reached EOS */
2896 /* buffer popped, but none returned means it was clipped */
2899 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2900 if (!mux->streamable) {
2901 gst_matroska_mux_finish (mux);
2903 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2905 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2906 ret = GST_FLOW_UNEXPECTED;
2909 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2910 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2911 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2912 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2914 /* make note of first and last encountered timestamps, so we can calculate
2915 * the actual duration later when we send an updated header on eos */
2916 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2917 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2918 GstClockTime end_ts = start_ts;
2920 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2921 end_ts += GST_BUFFER_DURATION (best->buffer);
2922 else if (best->track->default_duration)
2923 end_ts += best->track->default_duration;
2925 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2926 best->end_ts = end_ts;
2928 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2929 start_ts < best->start_ts))
2930 best->start_ts = start_ts;
2933 /* write one buffer */
2934 ret = gst_matroska_mux_write_data (mux, best);
2935 } while (ret == GST_FLOW_OK && !popped);
2942 * gst_matroska_mux_change_state:
2943 * @element: #GstMatroskaMux
2944 * @transition: State change transition.
2946 * Change the muxer state.
2948 * Returns: #GstStateChangeReturn
2950 static GstStateChangeReturn
2951 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2953 GstStateChangeReturn ret;
2954 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2956 switch (transition) {
2957 case GST_STATE_CHANGE_NULL_TO_READY:
2959 case GST_STATE_CHANGE_READY_TO_PAUSED:
2960 gst_collect_pads_start (mux->collect);
2962 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2964 case GST_STATE_CHANGE_PAUSED_TO_READY:
2965 gst_collect_pads_stop (mux->collect);
2971 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2973 switch (transition) {
2974 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2976 case GST_STATE_CHANGE_PAUSED_TO_READY:
2977 gst_matroska_mux_reset (GST_ELEMENT (mux));
2979 case GST_STATE_CHANGE_READY_TO_NULL:
2989 gst_matroska_mux_set_property (GObject * object,
2990 guint prop_id, const GValue * value, GParamSpec * pspec)
2992 GstMatroskaMux *mux;
2994 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2995 mux = GST_MATROSKA_MUX (object);
2998 case ARG_WRITING_APP:
2999 if (!g_value_get_string (value)) {
3000 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3003 g_free (mux->writing_app);
3004 mux->writing_app = g_value_dup_string (value);
3006 case ARG_DOCTYPE_VERSION:
3007 mux->doctype_version = g_value_get_int (value);
3009 case ARG_MIN_INDEX_INTERVAL:
3010 mux->min_index_interval = g_value_get_int64 (value);
3012 case ARG_STREAMABLE:
3013 mux->streamable = g_value_get_boolean (value);
3016 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3022 gst_matroska_mux_get_property (GObject * object,
3023 guint prop_id, GValue * value, GParamSpec * pspec)
3025 GstMatroskaMux *mux;
3027 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3028 mux = GST_MATROSKA_MUX (object);
3031 case ARG_WRITING_APP:
3032 g_value_set_string (value, mux->writing_app);
3034 case ARG_DOCTYPE_VERSION:
3035 g_value_set_int (value, mux->doctype_version);
3037 case ARG_MIN_INDEX_INTERVAL:
3038 g_value_set_int64 (value, mux->min_index_interval);
3040 case ARG_STREAMABLE:
3041 g_value_set_boolean (value, mux->streamable);
3044 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);