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 COMMON_AUDIO_CAPS "; "
151 "mpegversion = (int) { 2, 4 }, "
152 "stream-format = (string) raw, "
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 */
225 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
226 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
227 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
228 GstCollectData2 * data, GstEvent * event, gpointer user_data);
231 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
233 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
234 GstPadTemplate * templ, const gchar * name);
235 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
237 /* gst internal change state handler */
238 static GstStateChangeReturn
239 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
241 /* gobject bla bla */
242 static void gst_matroska_mux_set_property (GObject * object,
243 guint prop_id, const GValue * value, GParamSpec * pspec);
244 static void gst_matroska_mux_get_property (GObject * object,
245 guint prop_id, GValue * value, GParamSpec * pspec);
248 static void gst_matroska_mux_reset (GstElement * element);
251 static guint64 gst_matroska_mux_create_uid ();
253 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
254 GstMatroskaTrackContext * context);
255 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
256 GstMatroskaTrackContext * context);
257 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
258 GstMatroskaTrackContext * context);
259 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
260 GstMatroskaTrackContext * context);
261 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
262 GstMatroskaTrackContext * context);
264 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
268 gst_matroska_mux_add_interfaces (GType type)
270 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
272 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
276 gst_matroska_mux_base_init (gpointer g_class)
281 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
283 GObjectClass *gobject_class;
284 GstElementClass *gstelement_class;
286 gobject_class = (GObjectClass *) klass;
287 gstelement_class = (GstElementClass *) klass;
289 gst_element_class_add_static_pad_template (gstelement_class,
291 gst_element_class_add_static_pad_template (gstelement_class,
293 gst_element_class_add_static_pad_template (gstelement_class,
294 &subtitlesink_templ);
295 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
296 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
298 "Muxes video/audio/subtitle streams into a matroska stream",
299 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
301 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
304 gobject_class->finalize = gst_matroska_mux_finalize;
306 gobject_class->get_property = gst_matroska_mux_get_property;
307 gobject_class->set_property = gst_matroska_mux_set_property;
309 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
310 g_param_spec_string ("writing-app", "Writing application.",
311 "The name the application that creates the matroska file.",
312 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
313 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
314 g_param_spec_int ("version", "DocType version",
315 "This parameter determines what Matroska features can be used.",
316 1, 2, DEFAULT_DOCTYPE_VERSION,
317 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
318 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
319 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
320 "entries", "An index entry is created every so many nanoseconds.",
321 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
323 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
324 g_param_spec_boolean ("streamable", "Determines whether output should "
325 "be streamable", "If set to true, the output should be as if it is "
326 "to be streamed and hence no indexes written or duration written.",
328 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
330 gstelement_class->change_state =
331 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
332 gstelement_class->request_new_pad =
333 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
334 gstelement_class->release_pad =
335 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
339 * Start of pad option handler code
341 #define DEFAULT_PAD_FRAME_DURATION TRUE
342 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
347 PROP_PAD_FRAME_DURATION
353 gboolean frame_duration;
354 gboolean frame_duration_user;
357 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
360 gst_matroskamux_pad_get_type (void)
362 static GType type = 0;
364 if (G_UNLIKELY (type == 0)) {
365 type = g_type_register_static_simple (GST_TYPE_PAD,
366 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
367 (GClassInitFunc) gst_matroskamux_pad_class_init,
368 sizeof (GstMatroskamuxPad), NULL, 0);
373 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
374 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
375 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
376 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
379 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
380 GValue * value, GParamSpec * pspec)
382 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
385 case PROP_PAD_FRAME_DURATION:
386 g_value_set_boolean (value, pad->frame_duration);
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
396 const GValue * value, GParamSpec * pspec)
398 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
401 case PROP_PAD_FRAME_DURATION:
402 pad->frame_duration = g_value_get_boolean (value);
403 pad->frame_duration_user = TRUE;
406 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
412 gst_matroskamux_pad_class_init (GstPadClass * klass)
414 GObjectClass *gobject_class = (GObjectClass *) klass;
416 gobject_class->set_property = gst_matroskamux_pad_set_property;
417 gobject_class->get_property = gst_matroskamux_pad_get_property;
419 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
420 g_param_spec_boolean ("frame-duration", "Frame duration",
421 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
422 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
426 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
428 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
429 pad->frame_duration_user = FALSE;
433 * End of pad option handler code
437 * gst_matroska_mux_init:
438 * @mux: #GstMatroskaMux that should be initialized.
439 * @g_class: Class of the muxer.
441 * Matroska muxer constructor.
444 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
446 GstPadTemplate *templ;
449 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
450 mux->srcpad = gst_pad_new_from_template (templ, "src");
452 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
453 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
455 mux->collect = gst_collect_pads2_new ();
456 gst_collect_pads2_set_clip_function (mux->collect,
457 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
458 gst_collect_pads2_set_buffer_function (mux->collect,
459 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
460 gst_collect_pads2_set_event_function (mux->collect,
461 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
463 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
464 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
466 /* property defaults */
467 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
468 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
469 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
470 mux->streamable = DEFAULT_STREAMABLE;
472 /* initialize internal variables */
474 mux->num_streams = 0;
475 mux->num_a_streams = 0;
476 mux->num_t_streams = 0;
477 mux->num_v_streams = 0;
479 /* initialize remaining variables */
480 gst_matroska_mux_reset (GST_ELEMENT (mux));
485 * gst_matroska_mux_finalize:
486 * @object: #GstMatroskaMux that should be finalized.
488 * Finalize matroska muxer.
491 gst_matroska_mux_finalize (GObject * object)
493 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
495 gst_event_replace (&mux->force_key_unit_event, NULL);
497 gst_object_unref (mux->collect);
498 gst_object_unref (mux->ebml_write);
499 if (mux->writing_app)
500 g_free (mux->writing_app);
502 G_OBJECT_CLASS (parent_class)->finalize (object);
507 * gst_matroska_mux_create_uid:
509 * Generate new unused track UID.
511 * Returns: New track UID.
514 gst_matroska_mux_create_uid (void)
521 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
526 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
527 for (i = 0; i < used_uids->len; i++) {
528 if (g_array_index (used_uids, guint64, i) == uid) {
533 g_array_append_val (used_uids, uid);
536 G_UNLOCK (used_uids);
542 * gst_matroska_pad_reset:
543 * @collect_pad: the #GstMatroskaPad
545 * Reset and/or release resources of a matroska collect pad.
548 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
551 GstMatroskaTrackType type = 0;
553 /* free track information */
554 if (collect_pad->track != NULL) {
555 /* retrieve for optional later use */
556 name = collect_pad->track->name;
557 type = collect_pad->track->type;
558 /* extra for video */
559 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
560 GstMatroskaTrackVideoContext *ctx =
561 (GstMatroskaTrackVideoContext *) collect_pad->track;
563 if (ctx->dirac_unit) {
564 gst_buffer_unref (ctx->dirac_unit);
565 ctx->dirac_unit = NULL;
568 g_free (collect_pad->track->codec_id);
569 g_free (collect_pad->track->codec_name);
571 g_free (collect_pad->track->name);
572 g_free (collect_pad->track->language);
573 g_free (collect_pad->track->codec_priv);
574 g_free (collect_pad->track);
575 collect_pad->track = NULL;
578 /* free cached buffer */
579 if (collect_pad->buffer != NULL) {
580 gst_buffer_unref (collect_pad->buffer);
581 collect_pad->buffer = NULL;
584 if (!full && type != 0) {
585 GstMatroskaTrackContext *context;
587 /* create a fresh context */
589 case GST_MATROSKA_TRACK_TYPE_VIDEO:
590 context = (GstMatroskaTrackContext *)
591 g_new0 (GstMatroskaTrackVideoContext, 1);
593 case GST_MATROSKA_TRACK_TYPE_AUDIO:
594 context = (GstMatroskaTrackContext *)
595 g_new0 (GstMatroskaTrackAudioContext, 1);
597 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
598 context = (GstMatroskaTrackContext *)
599 g_new0 (GstMatroskaTrackSubtitleContext, 1);
602 g_assert_not_reached ();
606 context->type = type;
607 context->name = name;
608 /* TODO: check default values for the context */
609 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
610 collect_pad->track = context;
611 collect_pad->buffer = NULL;
612 collect_pad->duration = 0;
613 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
614 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
619 * gst_matroska_pad_free:
620 * @collect_pad: the #GstMatroskaPad
622 * Release resources of a matroska collect pad.
625 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
627 gst_matroska_pad_reset (collect_pad, TRUE);
632 * gst_matroska_mux_reset:
633 * @element: #GstMatroskaMux that should be reseted.
635 * Reset matroska muxer back to initial state.
638 gst_matroska_mux_reset (GstElement * element)
640 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
643 /* reset EBML write */
644 gst_ebml_write_reset (mux->ebml_write);
647 mux->state = GST_MATROSKA_MUX_STATE_START;
649 /* clean up existing streams */
651 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
652 GstMatroskaPad *collect_pad;
654 collect_pad = (GstMatroskaPad *) walk->data;
656 /* reset collect pad to pristine state */
657 gst_matroska_pad_reset (collect_pad, FALSE);
661 mux->num_indexes = 0;
666 mux->time_scale = GST_MSECOND;
667 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
672 mux->cluster_time = 0;
673 mux->cluster_pos = 0;
674 mux->prev_cluster_size = 0;
677 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
681 * gst_matroska_mux_handle_src_event:
682 * @pad: Pad which received the event.
683 * @event: Received event.
685 * handle events - copied from oggmux without understanding
687 * Returns: #TRUE on success.
690 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
694 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
698 /* disable seeking for now */
704 return gst_pad_event_default (pad, event);
708 * gst_matroska_mux_handle_sink_event:
709 * @pad: Pad which received the event.
710 * @event: Received event.
712 * handle events - informational ones like tags
714 * Returns: #TRUE on success.
717 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
718 GstCollectData2 * data, GstEvent * event, gpointer user_data)
720 GstMatroskaTrackContext *context;
721 GstMatroskaPad *collect_pad;
726 mux = GST_MATROSKA_MUX (user_data);
727 collect_pad = (GstMatroskaPad *) data;
730 switch (GST_EVENT_TYPE (event)) {
734 GST_DEBUG_OBJECT (mux, "received tag event");
735 gst_event_parse_tag (event, &list);
737 context = collect_pad->track;
740 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
741 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
742 const gchar *lang_code;
744 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
746 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
747 context->language = g_strdup (lang_code);
749 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
754 /* FIXME: what about stream-specific tags? */
755 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
756 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
758 gst_event_unref (event);
759 /* handled this, don't want collectpads to forward it downstream */
763 case GST_EVENT_NEWSEGMENT:{
766 gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
768 if (format != GST_FORMAT_TIME) {
769 gst_event_unref (event);
774 case GST_EVENT_CUSTOM_DOWNSTREAM:{
775 const GstStructure *structure;
777 structure = gst_event_get_structure (event);
778 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
779 gst_event_replace (&mux->force_key_unit_event, NULL);
780 mux->force_key_unit_event = event;
789 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
798 * gst_matroska_mux_video_pad_setcaps:
799 * @pad: Pad which got the caps.
802 * Setcaps function for video sink pad.
804 * Returns: #TRUE on success.
807 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
809 GstMatroskaTrackContext *context = NULL;
810 GstMatroskaTrackVideoContext *videocontext;
812 GstMatroskaPad *collect_pad;
813 GstStructure *structure;
814 const gchar *mimetype;
815 const GValue *value = NULL;
816 const GstBuffer *codec_buf = NULL;
817 gint width, height, pixel_width, pixel_height;
819 gboolean interlaced = FALSE;
821 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
824 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
825 g_assert (collect_pad);
826 context = collect_pad->track;
828 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
829 videocontext = (GstMatroskaTrackVideoContext *) context;
831 /* gst -> matroska ID'ing */
832 structure = gst_caps_get_structure (caps, 0);
834 mimetype = gst_structure_get_name (structure);
836 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
838 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
840 if (!strcmp (mimetype, "video/x-theora")) {
841 /* we'll extract the details later from the theora identification header */
845 /* get general properties */
846 /* spec says it is mandatory */
847 if (!gst_structure_get_int (structure, "width", &width) ||
848 !gst_structure_get_int (structure, "height", &height))
851 videocontext->pixel_width = width;
852 videocontext->pixel_height = height;
854 /* set vp8 defaults or let user override it */
855 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
856 && (!strcmp (mimetype, "video/x-vp8")))
857 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
858 DEFAULT_PAD_FRAME_DURATION_VP8;
860 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
861 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
863 context->default_duration =
864 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
865 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
866 GST_TIME_ARGS (context->default_duration));
868 context->default_duration = 0;
870 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
871 &pixel_width, &pixel_height)) {
872 if (pixel_width > pixel_height) {
873 videocontext->display_width = width * pixel_width / pixel_height;
874 videocontext->display_height = height;
875 } else if (pixel_width < pixel_height) {
876 videocontext->display_width = width;
877 videocontext->display_height = height * pixel_height / pixel_width;
879 videocontext->display_width = 0;
880 videocontext->display_height = 0;
883 videocontext->display_width = 0;
884 videocontext->display_height = 0;
889 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
890 videocontext->fourcc = 0;
892 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
893 * data and other settings
897 /* extract codec_data, may turn out needed */
898 value = gst_structure_get_value (structure, "codec_data");
900 codec_buf = gst_value_get_buffer (value);
903 if (!strcmp (mimetype, "video/x-raw-yuv")) {
904 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
905 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
906 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
907 ||!strcmp (mimetype, "video/x-huffyuv")
908 || !strcmp (mimetype, "video/x-divx")
909 || !strcmp (mimetype, "video/x-dv")
910 || !strcmp (mimetype, "video/x-h263")
911 || !strcmp (mimetype, "video/x-msmpeg")
912 || !strcmp (mimetype, "video/x-wmv")
913 || !strcmp (mimetype, "image/jpeg")) {
914 gst_riff_strf_vids *bih;
915 gint size = sizeof (gst_riff_strf_vids);
918 if (!strcmp (mimetype, "video/x-xvid"))
919 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
920 else if (!strcmp (mimetype, "video/x-huffyuv"))
921 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
922 else if (!strcmp (mimetype, "video/x-dv"))
923 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
924 else if (!strcmp (mimetype, "video/x-h263"))
925 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
926 else if (!strcmp (mimetype, "video/x-divx")) {
929 gst_structure_get_int (structure, "divxversion", &divxversion);
930 switch (divxversion) {
932 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
935 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
938 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
941 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
944 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
945 switch (msmpegversion) {
947 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
950 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
956 } else if (!strcmp (mimetype, "video/x-wmv")) {
959 if (gst_structure_get_fourcc (structure, "format", &format)) {
961 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
962 if (wmvversion == 2) {
963 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
964 } else if (wmvversion == 1) {
965 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
966 } else if (wmvversion == 3) {
967 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
970 } else if (!strcmp (mimetype, "image/jpeg")) {
971 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
977 bih = g_new0 (gst_riff_strf_vids, 1);
978 GST_WRITE_UINT32_LE (&bih->size, size);
979 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
980 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
981 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
982 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
983 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
984 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
985 videocontext->pixel_height * 3);
987 /* process codec private/initialization data, if any */
989 size += GST_BUFFER_SIZE (codec_buf);
990 bih = g_realloc (bih, size);
991 GST_WRITE_UINT32_LE (&bih->size, size);
992 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
993 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
996 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
997 context->codec_priv = (gpointer) bih;
998 context->codec_priv_size = size;
999 } else if (!strcmp (mimetype, "video/x-h264")) {
1000 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1002 if (context->codec_priv != NULL) {
1003 g_free (context->codec_priv);
1004 context->codec_priv = NULL;
1005 context->codec_priv_size = 0;
1008 /* Create avcC header */
1009 if (codec_buf != NULL) {
1010 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1011 context->codec_priv = g_malloc0 (context->codec_priv_size);
1012 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1013 context->codec_priv_size);
1015 } else if (!strcmp (mimetype, "video/x-theora")) {
1016 const GValue *streamheader;
1018 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1020 if (context->codec_priv != NULL) {
1021 g_free (context->codec_priv);
1022 context->codec_priv = NULL;
1023 context->codec_priv_size = 0;
1026 streamheader = gst_structure_get_value (structure, "streamheader");
1027 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1028 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1029 ("theora stream headers missing or malformed"));
1032 } else if (!strcmp (mimetype, "video/x-dirac")) {
1033 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1034 } else if (!strcmp (mimetype, "video/x-vp8")) {
1035 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1036 } else if (!strcmp (mimetype, "video/mpeg")) {
1039 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1040 switch (mpegversion) {
1042 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1045 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1048 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1054 /* global headers may be in codec data */
1055 if (codec_buf != NULL) {
1056 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1057 context->codec_priv = g_malloc0 (context->codec_priv_size);
1058 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1059 context->codec_priv_size);
1061 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1063 /* can only make it here if preceding case verified it was version 3 */
1064 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1065 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1067 const GValue *mdpr_data;
1069 gst_structure_get_int (structure, "rmversion", &rmversion);
1070 switch (rmversion) {
1072 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1075 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1078 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1081 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1087 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1088 if (mdpr_data != NULL) {
1089 guint8 *priv_data = NULL;
1090 guint priv_data_size = 0;
1092 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1094 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1095 priv_data = g_malloc0 (priv_data_size);
1097 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1099 context->codec_priv = priv_data;
1100 context->codec_priv_size = priv_data_size;
1109 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1110 GST_PAD_NAME (pad), caps);
1115 /* N > 0 to expect a particular number of headers, negative if the
1116 number of headers is variable */
1118 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1119 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1121 GstBuffer **buf = NULL;
1124 guint bufi, i, offset, priv_data_size;
1126 if (streamheader == NULL)
1127 goto no_stream_headers;
1129 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1132 bufarr = g_value_peek_pointer (streamheader);
1133 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1135 if (N > 0 && bufarr->len != N)
1138 context->xiph_headers_to_skip = bufarr->len;
1140 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1141 for (i = 0; i < bufarr->len; i++) {
1142 GValue *bufval = &g_array_index (bufarr, GValue, i);
1144 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1146 goto wrong_content_type;
1149 buf[i] = g_value_peek_pointer (bufval);
1153 if (bufarr->len > 0) {
1154 for (i = 0; i < bufarr->len - 1; i++) {
1155 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1159 for (i = 0; i < bufarr->len; ++i) {
1160 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1163 priv_data = g_malloc0 (priv_data_size);
1165 priv_data[0] = bufarr->len - 1;
1168 if (bufarr->len > 0) {
1169 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1170 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1171 priv_data[offset++] = 0xff;
1173 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1177 for (i = 0; i < bufarr->len; ++i) {
1178 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1179 GST_BUFFER_SIZE (buf[i]));
1180 offset += GST_BUFFER_SIZE (buf[i]);
1183 context->codec_priv = priv_data;
1184 context->codec_priv_size = priv_data_size;
1187 *p_buf0 = gst_buffer_ref (buf[0]);
1196 GST_WARNING ("required streamheaders missing in sink caps!");
1201 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1202 G_VALUE_TYPE_NAME (streamheader));
1207 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1212 GST_WARNING ("streamheaders array does not contain GstBuffers");
1218 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1219 GstMatroskaTrackContext * context)
1221 GstBuffer *buf0 = NULL;
1223 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1226 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1227 GST_WARNING ("First vorbis header too small, ignoring");
1229 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1230 GstMatroskaTrackAudioContext *audiocontext;
1233 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1234 audiocontext = (GstMatroskaTrackAudioContext *) context;
1235 audiocontext->channels = GST_READ_UINT8 (hdr);
1236 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1241 gst_buffer_unref (buf0);
1247 theora_streamheader_to_codecdata (const GValue * streamheader,
1248 GstMatroskaTrackContext * context)
1250 GstBuffer *buf0 = NULL;
1252 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1255 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1256 GST_WARNING ("First theora header too small, ignoring");
1257 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1258 GST_WARNING ("First header not a theora identification header, ignoring");
1260 GstMatroskaTrackVideoContext *videocontext;
1261 guint fps_num, fps_denom, par_num, par_denom;
1264 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1266 videocontext = (GstMatroskaTrackVideoContext *) context;
1267 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1268 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1269 hdr += 3 + 3 + 1 + 1;
1270 fps_num = GST_READ_UINT32_BE (hdr);
1271 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1272 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1273 fps_denom, fps_num);
1275 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1276 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1277 if (par_num > 0 && par_num > 0) {
1278 if (par_num > par_denom) {
1279 videocontext->display_width =
1280 videocontext->pixel_width * par_num / par_denom;
1281 videocontext->display_height = videocontext->pixel_height;
1282 } else if (par_num < par_denom) {
1283 videocontext->display_width = videocontext->pixel_width;
1284 videocontext->display_height =
1285 videocontext->pixel_height * par_denom / par_num;
1287 videocontext->display_width = 0;
1288 videocontext->display_height = 0;
1291 videocontext->display_width = 0;
1292 videocontext->display_height = 0;
1298 gst_buffer_unref (buf0);
1304 kate_streamheader_to_codecdata (const GValue * streamheader,
1305 GstMatroskaTrackContext * context)
1307 GstBuffer *buf0 = NULL;
1309 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1312 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1313 GST_WARNING ("First kate header too small, ignoring");
1314 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1315 GST_WARNING ("First header not a kate identification header, ignoring");
1319 gst_buffer_unref (buf0);
1325 flac_streamheader_to_codecdata (const GValue * streamheader,
1326 GstMatroskaTrackContext * context)
1333 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1334 GST_WARNING ("No or invalid streamheader field in the caps");
1338 bufarr = g_value_peek_pointer (streamheader);
1339 if (bufarr->len < 2) {
1340 GST_WARNING ("Too few headers in streamheader field");
1344 context->xiph_headers_to_skip = bufarr->len + 1;
1346 bufval = &g_array_index (bufarr, GValue, 0);
1347 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1348 GST_WARNING ("streamheaders array does not contain GstBuffers");
1352 buffer = g_value_peek_pointer (bufval);
1354 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1355 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1356 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1357 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1358 GST_WARNING ("Invalid streamheader for FLAC");
1362 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1363 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1364 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1365 GST_BUFFER_SIZE (buffer) - 9);
1367 for (i = 1; i < bufarr->len; i++) {
1368 bufval = &g_array_index (bufarr, GValue, i);
1370 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1371 g_free (context->codec_priv);
1372 context->codec_priv = NULL;
1373 context->codec_priv_size = 0;
1374 GST_WARNING ("streamheaders array does not contain GstBuffers");
1378 buffer = g_value_peek_pointer (bufval);
1380 context->codec_priv =
1381 g_realloc (context->codec_priv,
1382 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1383 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1384 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1385 context->codec_priv_size =
1386 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1393 speex_streamheader_to_codecdata (const GValue * streamheader,
1394 GstMatroskaTrackContext * context)
1400 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1401 GST_WARNING ("No or invalid streamheader field in the caps");
1405 bufarr = g_value_peek_pointer (streamheader);
1406 if (bufarr->len != 2) {
1407 GST_WARNING ("Too few headers in streamheader field");
1411 context->xiph_headers_to_skip = bufarr->len + 1;
1413 bufval = &g_array_index (bufarr, GValue, 0);
1414 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1415 GST_WARNING ("streamheaders array does not contain GstBuffers");
1419 buffer = g_value_peek_pointer (bufval);
1421 if (GST_BUFFER_SIZE (buffer) < 80
1422 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1423 GST_WARNING ("Invalid streamheader for Speex");
1427 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1428 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1429 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1430 GST_BUFFER_SIZE (buffer));
1432 bufval = &g_array_index (bufarr, GValue, 1);
1434 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1435 g_free (context->codec_priv);
1436 context->codec_priv = NULL;
1437 context->codec_priv_size = 0;
1438 GST_WARNING ("streamheaders array does not contain GstBuffers");
1442 buffer = g_value_peek_pointer (bufval);
1444 context->codec_priv =
1445 g_realloc (context->codec_priv,
1446 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1447 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1448 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1449 context->codec_priv_size =
1450 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1455 static const gchar *
1456 aac_codec_data_to_codec_id (const GstBuffer * buf)
1458 const gchar *result;
1461 /* default to MAIN */
1464 if (GST_BUFFER_SIZE (buf) >= 2) {
1465 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1483 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1492 * gst_matroska_mux_audio_pad_setcaps:
1493 * @pad: Pad which got the caps.
1496 * Setcaps function for audio sink pad.
1498 * Returns: #TRUE on success.
1501 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1503 GstMatroskaTrackContext *context = NULL;
1504 GstMatroskaTrackAudioContext *audiocontext;
1505 GstMatroskaMux *mux;
1506 GstMatroskaPad *collect_pad;
1507 const gchar *mimetype;
1508 gint samplerate = 0, channels = 0;
1509 GstStructure *structure;
1510 const GValue *codec_data = NULL;
1511 const GstBuffer *buf = NULL;
1512 const gchar *stream_format = NULL;
1514 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1517 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1518 g_assert (collect_pad);
1519 context = collect_pad->track;
1521 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1522 audiocontext = (GstMatroskaTrackAudioContext *) context;
1524 structure = gst_caps_get_structure (caps, 0);
1525 mimetype = gst_structure_get_name (structure);
1528 gst_structure_get_int (structure, "rate", &samplerate);
1529 gst_structure_get_int (structure, "channels", &channels);
1531 audiocontext->samplerate = samplerate;
1532 audiocontext->channels = channels;
1533 audiocontext->bitdepth = 0;
1534 context->default_duration = 0;
1536 codec_data = gst_structure_get_value (structure, "codec_data");
1538 buf = gst_value_get_buffer (codec_data);
1540 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1541 * data and other settings
1545 if (!strcmp (mimetype, "audio/mpeg")) {
1546 gint mpegversion = 0;
1548 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1549 switch (mpegversion) {
1555 gst_structure_get_int (structure, "layer", &layer);
1557 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1558 GST_WARNING_OBJECT (mux,
1559 "Unable to determine MPEG audio version, assuming 1");
1565 else if (layer == 2)
1567 else if (version == 2)
1572 context->default_duration =
1573 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1577 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1580 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1583 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1592 stream_format = gst_structure_get_string (structure, "stream-format");
1593 /* check this is raw aac */
1594 if (stream_format) {
1595 if (strcmp (stream_format, "raw") != 0) {
1596 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1600 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1605 if (mpegversion == 2)
1607 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1608 aac_codec_data_to_codec_id (buf));
1609 else if (mpegversion == 4)
1611 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1612 aac_codec_data_to_codec_id (buf));
1614 g_assert_not_reached ();
1616 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1623 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1625 gint endianness = G_LITTLE_ENDIAN;
1626 gboolean signedness = TRUE;
1628 if (!gst_structure_get_int (structure, "width", &width) ||
1629 !gst_structure_get_int (structure, "depth", &depth) ||
1630 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1631 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1636 !gst_structure_get_int (structure, "endianness", &endianness)) {
1637 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1641 if (width != depth) {
1642 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1646 /* FIXME: where is this spec'ed out? (tpm) */
1647 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1648 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1652 audiocontext->bitdepth = depth;
1653 if (endianness == G_BIG_ENDIAN)
1654 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1656 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1658 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1661 if (!gst_structure_get_int (structure, "width", &width)) {
1662 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1666 audiocontext->bitdepth = width;
1667 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1669 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1670 const GValue *streamheader;
1672 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1674 if (context->codec_priv != NULL) {
1675 g_free (context->codec_priv);
1676 context->codec_priv = NULL;
1677 context->codec_priv_size = 0;
1680 streamheader = gst_structure_get_value (structure, "streamheader");
1681 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1682 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1683 ("vorbis stream headers missing or malformed"));
1686 } else if (!strcmp (mimetype, "audio/x-flac")) {
1687 const GValue *streamheader;
1689 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1690 if (context->codec_priv != NULL) {
1691 g_free (context->codec_priv);
1692 context->codec_priv = NULL;
1693 context->codec_priv_size = 0;
1696 streamheader = gst_structure_get_value (structure, "streamheader");
1697 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1698 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1699 ("flac stream headers missing or malformed"));
1702 } else if (!strcmp (mimetype, "audio/x-speex")) {
1703 const GValue *streamheader;
1705 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1706 if (context->codec_priv != NULL) {
1707 g_free (context->codec_priv);
1708 context->codec_priv = NULL;
1709 context->codec_priv_size = 0;
1712 streamheader = gst_structure_get_value (structure, "streamheader");
1713 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1714 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1715 ("speex stream headers missing or malformed"));
1718 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1719 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1720 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1721 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1722 } else if (!strcmp (mimetype, "audio/x-dts")) {
1723 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1724 } else if (!strcmp (mimetype, "audio/x-tta")) {
1727 /* TTA frame duration */
1728 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1730 gst_structure_get_int (structure, "width", &width);
1731 audiocontext->bitdepth = width;
1732 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1734 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1736 const GValue *mdpr_data;
1738 gst_structure_get_int (structure, "raversion", &raversion);
1739 switch (raversion) {
1741 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1744 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1747 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1753 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1754 if (mdpr_data != NULL) {
1755 guint8 *priv_data = NULL;
1756 guint priv_data_size = 0;
1758 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1760 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1761 priv_data = g_malloc0 (priv_data_size);
1763 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1765 context->codec_priv = priv_data;
1766 context->codec_priv_size = priv_data_size;
1769 } else if (!strcmp (mimetype, "audio/x-wma")
1770 || !strcmp (mimetype, "audio/x-alaw")
1771 || !strcmp (mimetype, "audio/x-mulaw")) {
1773 guint codec_priv_size;
1778 if (samplerate == 0 || channels == 0) {
1779 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1783 if (!strcmp (mimetype, "audio/x-wma")) {
1787 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1788 || !gst_structure_get_int (structure, "block_align", &block_align)
1789 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1790 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1795 switch (wmaversion) {
1797 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1800 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1803 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1806 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1810 if (gst_structure_get_int (structure, "depth", &depth))
1811 audiocontext->bitdepth = depth;
1812 } else if (!strcmp (mimetype, "audio/x-alaw")
1813 || !strcmp (mimetype, "audio/x-mulaw")) {
1814 audiocontext->bitdepth = 8;
1815 if (!strcmp (mimetype, "audio/x-alaw"))
1816 format = GST_RIFF_WAVE_FORMAT_ALAW;
1818 format = GST_RIFF_WAVE_FORMAT_MULAW;
1820 block_align = channels;
1821 bitrate = block_align * samplerate;
1823 g_assert (format != 0);
1825 codec_priv_size = WAVEFORMATEX_SIZE;
1827 codec_priv_size += GST_BUFFER_SIZE (buf);
1829 /* serialize waveformatex structure */
1830 codec_priv = g_malloc0 (codec_priv_size);
1831 GST_WRITE_UINT16_LE (codec_priv, format);
1832 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1833 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1834 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1835 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1836 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1838 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1840 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1842 /* process codec private/initialization data, if any */
1844 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1845 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1848 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1849 context->codec_priv = (gpointer) codec_priv;
1850 context->codec_priv_size = codec_priv_size;
1858 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1859 GST_PAD_NAME (pad), caps);
1866 * gst_matroska_mux_subtitle_pad_setcaps:
1867 * @pad: Pad which got the caps.
1870 * Setcaps function for subtitle sink pad.
1872 * Returns: #TRUE on success.
1875 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1878 * Consider this as boilerplate code for now. There is
1879 * no single subtitle creation element in GStreamer,
1880 * neither do I know how subtitling works at all. */
1882 /* There is now (at least) one such alement (kateenc), and I'm going
1883 to handle it here and claim it works when it can be piped back
1884 through GStreamer and VLC */
1886 GstMatroskaTrackContext *context = NULL;
1887 GstMatroskaTrackSubtitleContext *scontext;
1888 GstMatroskaMux *mux;
1889 GstMatroskaPad *collect_pad;
1890 const gchar *mimetype;
1891 GstStructure *structure;
1893 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1896 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1897 g_assert (collect_pad);
1898 context = collect_pad->track;
1900 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1901 scontext = (GstMatroskaTrackSubtitleContext *) context;
1903 structure = gst_caps_get_structure (caps, 0);
1904 mimetype = gst_structure_get_name (structure);
1907 scontext->check_utf8 = 1;
1908 scontext->invalid_utf8 = 0;
1909 context->default_duration = 0;
1911 /* TODO: - other format than Kate */
1913 if (!strcmp (mimetype, "subtitle/x-kate")) {
1914 const GValue *streamheader;
1916 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1918 if (context->codec_priv != NULL) {
1919 g_free (context->codec_priv);
1920 context->codec_priv = NULL;
1921 context->codec_priv_size = 0;
1924 streamheader = gst_structure_get_value (structure, "streamheader");
1925 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1926 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1927 ("kate stream headers missing or malformed"));
1938 * gst_matroska_mux_request_new_pad:
1939 * @element: #GstMatroskaMux.
1940 * @templ: #GstPadTemplate.
1941 * @pad_name: New pad name.
1943 * Request pad function for sink templates.
1945 * Returns: New #GstPad.
1948 gst_matroska_mux_request_new_pad (GstElement * element,
1949 GstPadTemplate * templ, const gchar * req_name)
1951 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1952 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1953 GstMatroskaPad *collect_pad;
1954 GstMatroskamuxPad *newpad;
1956 const gchar *pad_name = NULL;
1957 GstPadSetCapsFunction setcapsfunc = NULL;
1958 GstMatroskaTrackContext *context = NULL;
1961 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1962 /* don't mix named and unnamed pads, if the pad already exists we fail when
1963 * trying to add it */
1964 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1965 pad_name = req_name;
1967 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1970 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1971 context = (GstMatroskaTrackContext *)
1972 g_new0 (GstMatroskaTrackAudioContext, 1);
1973 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1974 context->name = g_strdup ("Audio");
1975 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1976 /* don't mix named and unnamed pads, if the pad already exists we fail when
1977 * trying to add it */
1978 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1979 pad_name = req_name;
1981 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1984 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1985 context = (GstMatroskaTrackContext *)
1986 g_new0 (GstMatroskaTrackVideoContext, 1);
1987 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1988 context->name = g_strdup ("Video");
1989 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1990 /* don't mix named and unnamed pads, if the pad already exists we fail when
1991 * trying to add it */
1992 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1993 pad_name = req_name;
1995 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1998 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1999 context = (GstMatroskaTrackContext *)
2000 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2001 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2002 context->name = g_strdup ("Subtitle");
2004 GST_WARNING_OBJECT (mux, "This is not our template!");
2008 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2009 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2012 gst_matroskamux_pad_init (newpad);
2013 collect_pad = (GstMatroskaPad *)
2014 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2015 sizeof (GstMatroskamuxPad),
2016 (GstCollectData2DestroyNotify) gst_matroska_pad_free, TRUE);
2018 collect_pad->track = context;
2019 gst_matroska_pad_reset (collect_pad, FALSE);
2021 gst_pad_set_setcaps_function (GST_PAD (newpad), setcapsfunc);
2022 gst_pad_set_active (GST_PAD (newpad), TRUE);
2023 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2024 goto pad_add_failed;
2028 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2030 return GST_PAD (newpad);
2035 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2036 gst_object_unref (newpad);
2042 * gst_matroska_mux_release_pad:
2043 * @element: #GstMatroskaMux.
2044 * @pad: Pad to release.
2046 * Release a previously requested pad.
2049 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2051 GstMatroskaMux *mux;
2054 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2056 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2057 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2058 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2060 if (cdata->pad == pad) {
2061 GstClockTime min_dur; /* observed minimum duration */
2063 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2064 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2065 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2066 if (collect_pad->duration < min_dur)
2067 collect_pad->duration = min_dur;
2070 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2071 mux->duration < collect_pad->duration)
2072 mux->duration = collect_pad->duration;
2078 gst_collect_pads2_remove_pad (mux->collect, pad);
2079 if (gst_element_remove_pad (element, pad))
2085 * gst_matroska_mux_track_header:
2086 * @mux: #GstMatroskaMux
2087 * @context: Tack context.
2089 * Write a track header.
2092 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2093 GstMatroskaTrackContext * context)
2095 GstEbmlWrite *ebml = mux->ebml_write;
2098 /* TODO: check if everything necessary is written and check default values */
2100 /* track type goes before the type-specific stuff */
2101 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2102 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2104 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2105 gst_matroska_mux_create_uid ());
2106 if (context->default_duration) {
2107 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2108 context->default_duration);
2110 if (context->language) {
2111 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2115 /* type-specific stuff */
2116 switch (context->type) {
2117 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2118 GstMatroskaTrackVideoContext *videocontext =
2119 (GstMatroskaTrackVideoContext *) context;
2121 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2122 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2123 videocontext->pixel_width);
2124 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2125 videocontext->pixel_height);
2126 if (videocontext->display_width && videocontext->display_height) {
2127 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2128 videocontext->display_width);
2129 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2130 videocontext->display_height);
2132 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2133 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2134 if (videocontext->fourcc) {
2135 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2137 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2138 (gpointer) & fcc_le, 4);
2140 gst_ebml_write_master_finish (ebml, master);
2145 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2146 GstMatroskaTrackAudioContext *audiocontext =
2147 (GstMatroskaTrackAudioContext *) context;
2149 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2150 if (audiocontext->samplerate != 8000)
2151 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2152 audiocontext->samplerate);
2153 if (audiocontext->channels != 1)
2154 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2155 audiocontext->channels);
2156 if (audiocontext->bitdepth) {
2157 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2158 audiocontext->bitdepth);
2160 gst_ebml_write_master_finish (ebml, master);
2166 /* doesn't need type-specific data */
2170 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2171 if (context->codec_priv)
2172 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2173 context->codec_priv, context->codec_priv_size);
2174 /* FIXME: until we have a nice way of getting the codecname
2175 * out of the caps, I'm not going to enable this. Too much
2176 * (useless, double, boring) work... */
2177 /* TODO: Use value from tags if any */
2178 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2179 context->codec_name); */
2180 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2185 * gst_matroska_mux_start:
2186 * @mux: #GstMatroskaMux
2188 * Start a new matroska file (write headers etc...)
2191 gst_matroska_mux_start (GstMatroskaMux * mux)
2193 GstEbmlWrite *ebml = mux->ebml_write;
2194 const gchar *doctype;
2195 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2196 GST_MATROSKA_ID_TRACKS,
2197 GST_MATROSKA_ID_CUES,
2198 GST_MATROSKA_ID_TAGS,
2201 guint64 master, child;
2205 GstClockTime duration = 0;
2206 guint32 segment_uid[4];
2207 GTimeVal time = { 0, 0 };
2209 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2210 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2212 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2214 /* we start with a EBML header */
2215 doctype = mux->doctype;
2216 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2217 doctype, mux->doctype_version);
2218 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2220 /* the rest of the header is cached */
2221 gst_ebml_write_set_cache (ebml, 0x1000);
2223 /* start a segment */
2225 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2226 mux->segment_master = ebml->pos;
2228 if (!mux->streamable) {
2229 /* seekhead (table of contents) - we set the positions later */
2230 mux->seekhead_pos = ebml->pos;
2231 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2232 for (i = 0; seekhead_id[i] != 0; i++) {
2233 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2234 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2235 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2236 gst_ebml_write_master_finish (ebml, child);
2238 gst_ebml_write_master_finish (ebml, master);
2241 if (mux->streamable) {
2242 const GstTagList *tags;
2245 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2247 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2248 guint64 master_tags, master_tag;
2250 GST_DEBUG ("Writing tags");
2252 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2253 mux->tags_pos = ebml->pos;
2254 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2255 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2256 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2257 gst_ebml_write_master_finish (ebml, master_tag);
2258 gst_ebml_write_master_finish (ebml, master_tags);
2263 mux->info_pos = ebml->pos;
2264 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2265 for (i = 0; i < 4; i++) {
2266 segment_uid[i] = g_random_int ();
2268 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2269 (guint8 *) segment_uid, 16);
2270 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2271 mux->duration_pos = ebml->pos;
2273 if (!mux->streamable) {
2274 for (collected = mux->collect->data; collected;
2275 collected = g_slist_next (collected)) {
2276 GstMatroskaPad *collect_pad;
2277 GstFormat format = GST_FORMAT_TIME;
2279 gint64 trackduration;
2281 collect_pad = (GstMatroskaPad *) collected->data;
2282 thepad = collect_pad->collect.pad;
2284 /* Query the total length of the track. */
2285 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2286 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2287 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2288 GST_TIME_ARGS (trackduration));
2289 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2290 duration = (GstClockTime) trackduration;
2294 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2295 gst_guint64_to_gdouble (duration) /
2296 gst_guint64_to_gdouble (mux->time_scale));
2298 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2299 "GStreamer plugin version " PACKAGE_VERSION);
2300 if (mux->writing_app && mux->writing_app[0]) {
2301 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2303 g_get_current_time (&time);
2304 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2305 gst_ebml_write_master_finish (ebml, master);
2308 mux->tracks_pos = ebml->pos;
2309 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2311 for (collected = mux->collect->data; collected;
2312 collected = g_slist_next (collected)) {
2313 GstMatroskaPad *collect_pad;
2316 collect_pad = (GstMatroskaPad *) collected->data;
2317 thepad = collect_pad->collect.pad;
2319 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2320 collect_pad->track->codec_id != 0) {
2321 collect_pad->track->num = tracknum++;
2322 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2323 gst_matroska_mux_track_header (mux, collect_pad->track);
2324 gst_ebml_write_master_finish (ebml, child);
2325 /* some remaing pad/track setup */
2326 collect_pad->default_duration_scaled =
2327 gst_util_uint64_scale (collect_pad->track->default_duration,
2328 1, mux->time_scale);
2331 gst_ebml_write_master_finish (ebml, master);
2333 /* lastly, flush the cache */
2334 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2338 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2341 /* TODO: more sensible tag mappings */
2344 const gchar *matroska_tagname;
2345 const gchar *gstreamer_tagname;
2349 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2350 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2351 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2352 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2353 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2354 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2355 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2356 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2357 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2358 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2359 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2360 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2361 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2362 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2363 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2365 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2367 guint64 simpletag_master;
2369 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2370 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2371 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2373 if (strcmp (tagname_gst, tag) == 0) {
2374 GValue src = { 0, };
2377 if (!gst_tag_list_copy_value (&src, list, tag))
2379 if ((dest = gst_value_serialize (&src))) {
2381 simpletag_master = gst_ebml_write_master_start (ebml,
2382 GST_MATROSKA_ID_SIMPLETAG);
2383 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2384 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2385 gst_ebml_write_master_finish (ebml, simpletag_master);
2388 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2390 g_value_unset (&src);
2398 * gst_matroska_mux_finish:
2399 * @mux: #GstMatroskaMux
2401 * Finish a new matroska file (write index etc...)
2404 gst_matroska_mux_finish (GstMatroskaMux * mux)
2406 GstEbmlWrite *ebml = mux->ebml_write;
2408 guint64 duration = 0;
2410 const GstTagList *tags;
2412 /* finish last cluster */
2414 gst_ebml_write_master_finish (ebml, mux->cluster);
2418 if (mux->index != NULL) {
2420 guint64 master, pointentry_master, trackpos_master;
2422 mux->cues_pos = ebml->pos;
2423 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2424 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2426 for (n = 0; n < mux->num_indexes; n++) {
2427 GstMatroskaIndex *idx = &mux->index[n];
2429 pointentry_master = gst_ebml_write_master_start (ebml,
2430 GST_MATROSKA_ID_POINTENTRY);
2431 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2432 idx->time / mux->time_scale);
2433 trackpos_master = gst_ebml_write_master_start (ebml,
2434 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2435 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2436 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2437 idx->pos - mux->segment_master);
2438 gst_ebml_write_master_finish (ebml, trackpos_master);
2439 gst_ebml_write_master_finish (ebml, pointentry_master);
2442 gst_ebml_write_master_finish (ebml, master);
2443 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2447 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2449 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2450 guint64 master_tags, master_tag;
2452 GST_DEBUG ("Writing tags");
2454 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2455 mux->tags_pos = ebml->pos;
2456 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2457 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2458 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2459 gst_ebml_write_master_finish (ebml, master_tag);
2460 gst_ebml_write_master_finish (ebml, master_tags);
2463 /* update seekhead. We know that:
2464 * - a seekhead contains 4 entries.
2465 * - order of entries is as above.
2466 * - a seekhead has a 4-byte header + 8-byte length
2467 * - each entry is 2-byte master, 2-byte ID pointer,
2468 * 2-byte length pointer, all 8/1-byte length, 4-
2469 * byte ID and 8-byte length pointer, where the
2470 * length pointer starts at 20.
2471 * - all entries are local to the segment (so pos - segment_master).
2472 * - so each entry is at 12 + 20 + num * 28. */
2473 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2474 mux->info_pos - mux->segment_master);
2475 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2476 mux->tracks_pos - mux->segment_master);
2477 if (mux->index != NULL) {
2478 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2479 mux->cues_pos - mux->segment_master);
2482 guint64 my_pos = ebml->pos;
2484 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2485 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2486 gst_ebml_write_seek (ebml, my_pos);
2489 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2490 mux->tags_pos - mux->segment_master);
2493 guint64 my_pos = ebml->pos;
2495 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2496 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2497 gst_ebml_write_seek (ebml, my_pos);
2500 /* update duration */
2501 /* first get the overall duration */
2502 /* a released track may have left a duration in here */
2503 duration = mux->duration;
2504 for (collected = mux->collect->data; collected;
2505 collected = g_slist_next (collected)) {
2506 GstMatroskaPad *collect_pad;
2507 GstClockTime min_duration; /* observed minimum duration */
2509 collect_pad = (GstMatroskaPad *) collected->data;
2511 GST_DEBUG_OBJECT (mux,
2512 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2513 " end ts %" GST_TIME_FORMAT, collect_pad,
2514 GST_TIME_ARGS (collect_pad->start_ts),
2515 GST_TIME_ARGS (collect_pad->end_ts));
2517 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2518 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2520 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2521 if (collect_pad->duration < min_duration)
2522 collect_pad->duration = min_duration;
2523 GST_DEBUG_OBJECT (collect_pad,
2524 "final track duration: %" GST_TIME_FORMAT,
2525 GST_TIME_ARGS (collect_pad->duration));
2528 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2529 duration < collect_pad->duration)
2530 duration = collect_pad->duration;
2532 if (duration != 0) {
2533 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2534 GST_TIME_ARGS (duration));
2535 pos = mux->ebml_write->pos;
2536 gst_ebml_write_seek (ebml, mux->duration_pos);
2537 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2538 gst_guint64_to_gdouble (duration) /
2539 gst_guint64_to_gdouble (mux->time_scale));
2540 gst_ebml_write_seek (ebml, pos);
2543 guint64 my_pos = ebml->pos;
2545 gst_ebml_write_seek (ebml, mux->duration_pos);
2546 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2547 gst_ebml_write_seek (ebml, my_pos);
2549 GST_DEBUG_OBJECT (mux, "finishing segment");
2550 /* finish segment - this also writes element length */
2551 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2555 * gst_matroska_mux_buffer_header:
2556 * @track: Track context.
2557 * @relative_timestamp: relative timestamp of the buffer
2558 * @flags: Buffer flags.
2560 * Create a buffer containing buffer header.
2562 * Returns: New buffer.
2565 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2566 gint16 relative_timestamp, int flags)
2570 hdr = gst_buffer_new_and_alloc (4);
2571 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2572 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2573 /* time relative to clustertime */
2574 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2577 GST_BUFFER_DATA (hdr)[3] = flags;
2582 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2583 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2584 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2587 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2588 GstMatroskaPad * collect_pad, GstBuffer * buf)
2590 GstMatroskaTrackVideoContext *ctx =
2591 (GstMatroskaTrackVideoContext *) collect_pad->track;
2592 const guint8 *data = GST_BUFFER_DATA (buf);
2593 guint size = GST_BUFFER_SIZE (buf);
2595 guint32 next_parse_offset;
2596 GstBuffer *ret = NULL;
2597 gboolean is_muxing_unit = FALSE;
2599 if (GST_BUFFER_SIZE (buf) < 13) {
2600 gst_buffer_unref (buf);
2604 /* Check if this buffer contains a picture or end-of-sequence packet */
2605 while (size >= 13) {
2606 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2607 gst_buffer_unref (buf);
2611 parse_code = GST_READ_UINT8 (data + 4);
2612 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2613 if (ctx->dirac_unit) {
2614 gst_buffer_unref (ctx->dirac_unit);
2615 ctx->dirac_unit = NULL;
2617 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2618 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2619 is_muxing_unit = TRUE;
2623 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2625 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2628 data += next_parse_offset;
2629 size -= next_parse_offset;
2632 if (ctx->dirac_unit)
2633 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2635 ctx->dirac_unit = gst_buffer_ref (buf);
2637 if (is_muxing_unit) {
2638 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2639 ctx->dirac_unit = NULL;
2640 gst_buffer_copy_metadata (ret, buf,
2641 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2642 GST_BUFFER_COPY_CAPS);
2643 gst_buffer_unref (buf);
2645 gst_buffer_unref (buf);
2653 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2657 GValue streamheader = { 0 };
2658 GValue bufval = { 0 };
2659 GstBuffer *streamheader_buffer;
2660 GstEbmlWrite *ebml = mux->ebml_write;
2662 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2663 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2664 caps = gst_caps_new_simple ("video/webm", NULL);
2666 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2668 s = gst_caps_get_structure (caps, 0);
2669 g_value_init (&streamheader, GST_TYPE_ARRAY);
2670 g_value_init (&bufval, GST_TYPE_BUFFER);
2671 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2672 gst_value_set_buffer (&bufval, streamheader_buffer);
2673 gst_value_array_append_value (&streamheader, &bufval);
2674 g_value_unset (&bufval);
2675 gst_structure_set_value (s, "streamheader", &streamheader);
2676 g_value_unset (&streamheader);
2677 gst_caps_replace (&ebml->caps, caps);
2678 gst_buffer_unref (streamheader_buffer);
2679 gst_caps_unref (caps);
2683 * gst_matroska_mux_write_data:
2684 * @mux: #GstMatroskaMux
2685 * @collect_pad: #GstMatroskaPad with the data
2687 * Write collected data (called from gst_matroska_mux_collected).
2689 * Returns: Result of the gst_pad_push issued to write the data.
2691 static GstFlowReturn
2692 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2694 GstEbmlWrite *ebml = mux->ebml_write;
2695 GstBuffer *buf, *hdr;
2697 gboolean write_duration;
2698 gint16 relative_timestamp;
2699 gint64 relative_timestamp64;
2700 guint64 block_duration;
2701 gboolean is_video_keyframe = FALSE;
2702 GstMatroskamuxPad *pad;
2705 buf = collect_pad->buffer;
2706 collect_pad->buffer = NULL;
2707 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2709 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2710 if (collect_pad->track->xiph_headers_to_skip > 0) {
2711 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2712 gst_buffer_unref (buf);
2713 --collect_pad->track->xiph_headers_to_skip;
2717 /* for dirac we have to queue up everything up to a picture unit */
2718 if (collect_pad->track->codec_id != NULL &&
2719 strcmp (collect_pad->track->codec_id,
2720 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2721 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2726 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2727 * this would wreak havoc with time stored in matroska file */
2728 /* TODO: maybe calculate a timestamp by using the previous timestamp
2729 * and default duration */
2730 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2731 GST_WARNING_OBJECT (collect_pad->collect.pad,
2732 "Invalid buffer timestamp; dropping buffer");
2733 gst_buffer_unref (buf);
2737 /* set the timestamp for outgoing buffers */
2738 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2740 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2741 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2742 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2743 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2744 is_video_keyframe = TRUE;
2748 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2749 * or when we may be reaching the limit of the relative timestamp */
2750 if (mux->cluster_time +
2751 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2752 || is_video_keyframe || mux->force_key_unit_event) {
2753 if (!mux->streamable)
2754 gst_ebml_write_master_finish (ebml, mux->cluster);
2756 /* Forward the GstForceKeyUnit event after finishing the cluster */
2757 if (mux->force_key_unit_event) {
2758 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2759 mux->force_key_unit_event = NULL;
2762 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2763 mux->cluster_pos = ebml->pos;
2764 gst_ebml_write_set_cache (ebml, 0x20);
2766 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2767 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2768 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2770 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2771 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2773 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2774 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2775 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2776 mux->prev_cluster_size);
2781 mux->cluster_pos = ebml->pos;
2782 gst_ebml_write_set_cache (ebml, 0x20);
2783 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2784 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2785 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2786 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2787 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2790 /* update duration of this track */
2791 if (GST_BUFFER_DURATION_IS_VALID (buf))
2792 collect_pad->duration += GST_BUFFER_DURATION (buf);
2794 /* We currently write index entries for all video tracks or for the audio
2795 * track in a single-track audio file. This could be improved by keeping the
2796 * index only for the *first* video track. */
2798 /* TODO: index is useful for every track, should contain the number of
2799 * the block in the cluster which contains the timestamp, should also work
2800 * for files with multiple audio tracks.
2802 if (!mux->streamable &&
2803 (is_video_keyframe ||
2804 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2805 (mux->num_streams == 1)))) {
2808 if (mux->min_index_interval != 0) {
2809 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2810 if (mux->index[last_idx].track == collect_pad->track->num)
2815 if (last_idx < 0 || mux->min_index_interval == 0 ||
2816 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2817 >= mux->min_index_interval)) {
2818 GstMatroskaIndex *idx;
2820 if (mux->num_indexes % 32 == 0) {
2821 mux->index = g_renew (GstMatroskaIndex, mux->index,
2822 mux->num_indexes + 32);
2824 idx = &mux->index[mux->num_indexes++];
2826 idx->pos = mux->cluster_pos;
2827 idx->time = GST_BUFFER_TIMESTAMP (buf);
2828 idx->track = collect_pad->track->num;
2832 /* Check if the duration differs from the default duration. */
2833 write_duration = FALSE;
2835 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
2836 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
2837 1, mux->time_scale);
2839 /* small difference should be ok. */
2840 if (block_duration > collect_pad->default_duration_scaled + 1 ||
2841 block_duration < collect_pad->default_duration_scaled - 1) {
2842 write_duration = TRUE;
2846 /* write the block, for doctype v2 use SimpleBlock if possible
2847 * one slice (*breath*).
2848 * FIXME: Need to do correct lacing! */
2849 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2850 if (relative_timestamp64 >= 0) {
2851 /* round the timestamp */
2852 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2854 /* round the timestamp */
2855 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2857 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2859 if (mux->doctype_version > 1 && !write_duration) {
2861 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2864 gst_matroska_mux_create_buffer_header (collect_pad->track,
2865 relative_timestamp, flags);
2866 gst_ebml_write_set_cache (ebml, 0x40);
2867 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2868 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2869 gst_ebml_write_buffer (ebml, hdr);
2870 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2871 gst_ebml_write_buffer (ebml, buf);
2873 return gst_ebml_last_write_result (ebml);
2875 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2876 /* write and call order slightly unnatural,
2877 * but avoids seek and minizes pushing */
2878 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2880 gst_matroska_mux_create_buffer_header (collect_pad->track,
2881 relative_timestamp, 0);
2883 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
2884 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2885 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2886 gst_ebml_write_buffer (ebml, hdr);
2887 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2888 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2889 gst_ebml_write_buffer (ebml, buf);
2891 return gst_ebml_last_write_result (ebml);
2896 * gst_matroska_mux_handle_buffer:
2897 * @pads: #GstCollectPads2
2898 * @uuser_data: #GstMatroskaMux
2900 * Collectpads callback.
2902 * Returns: #GstFlowReturn
2904 static GstFlowReturn
2905 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
2906 GstBuffer * buf, gpointer user_data)
2908 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2909 GstEbmlWrite *ebml = mux->ebml_write;
2910 GstMatroskaPad *best;
2912 GstFlowReturn ret = GST_FLOW_OK;
2914 GST_DEBUG_OBJECT (mux, "Collected pads");
2916 /* start with a header */
2917 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2918 if (mux->collect->data == NULL) {
2919 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2920 ("No input streams configured"));
2921 return GST_FLOW_ERROR;
2923 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2924 gst_ebml_start_streamheader (ebml);
2925 gst_matroska_mux_start (mux);
2926 gst_matroska_mux_stop_streamheader (mux);
2927 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2931 /* provided with stream to write from */
2932 best = (GstMatroskaPad *) data;
2934 /* if there is no best pad, we have reached EOS */
2936 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2937 if (!mux->streamable) {
2938 gst_matroska_mux_finish (mux);
2940 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2942 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2943 ret = GST_FLOW_UNEXPECTED;
2950 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2951 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2952 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2953 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2955 /* make note of first and last encountered timestamps, so we can calculate
2956 * the actual duration later when we send an updated header on eos */
2957 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2958 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2959 GstClockTime end_ts = start_ts;
2961 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2962 end_ts += GST_BUFFER_DURATION (best->buffer);
2963 else if (best->track->default_duration)
2964 end_ts += best->track->default_duration;
2966 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2967 best->end_ts = end_ts;
2969 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2970 start_ts < best->start_ts))
2971 best->start_ts = start_ts;
2974 /* write one buffer */
2975 ret = gst_matroska_mux_write_data (mux, best);
2976 } while (ret == GST_FLOW_OK && !popped);
2983 * gst_matroska_mux_change_state:
2984 * @element: #GstMatroskaMux
2985 * @transition: State change transition.
2987 * Change the muxer state.
2989 * Returns: #GstStateChangeReturn
2991 static GstStateChangeReturn
2992 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2994 GstStateChangeReturn ret;
2995 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2997 switch (transition) {
2998 case GST_STATE_CHANGE_NULL_TO_READY:
3000 case GST_STATE_CHANGE_READY_TO_PAUSED:
3001 gst_collect_pads2_start (mux->collect);
3003 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3005 case GST_STATE_CHANGE_PAUSED_TO_READY:
3006 gst_collect_pads2_stop (mux->collect);
3012 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3014 switch (transition) {
3015 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3017 case GST_STATE_CHANGE_PAUSED_TO_READY:
3018 gst_matroska_mux_reset (GST_ELEMENT (mux));
3020 case GST_STATE_CHANGE_READY_TO_NULL:
3030 gst_matroska_mux_set_property (GObject * object,
3031 guint prop_id, const GValue * value, GParamSpec * pspec)
3033 GstMatroskaMux *mux;
3035 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3036 mux = GST_MATROSKA_MUX (object);
3039 case ARG_WRITING_APP:
3040 if (!g_value_get_string (value)) {
3041 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3044 g_free (mux->writing_app);
3045 mux->writing_app = g_value_dup_string (value);
3047 case ARG_DOCTYPE_VERSION:
3048 mux->doctype_version = g_value_get_int (value);
3050 case ARG_MIN_INDEX_INTERVAL:
3051 mux->min_index_interval = g_value_get_int64 (value);
3053 case ARG_STREAMABLE:
3054 mux->streamable = g_value_get_boolean (value);
3057 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3063 gst_matroska_mux_get_property (GObject * object,
3064 guint prop_id, GValue * value, GParamSpec * pspec)
3066 GstMatroskaMux *mux;
3068 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3069 mux = GST_MATROSKA_MUX (object);
3072 case ARG_WRITING_APP:
3073 g_value_set_string (value, mux->writing_app);
3075 case ARG_DOCTYPE_VERSION:
3076 g_value_set_int (value, mux->doctype_version);
3078 case ARG_MIN_INDEX_INTERVAL:
3079 g_value_set_int64 (value, mux->min_index_interval);
3081 case ARG_STREAMABLE:
3082 g_value_set_boolean (value, mux->streamable);
3085 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);