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:{
664 gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
666 if (format != GST_FORMAT_TIME) {
668 gst_event_unref (event);
673 case GST_EVENT_CUSTOM_DOWNSTREAM:{
674 const GstStructure *structure;
676 structure = gst_event_get_structure (event);
677 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
678 gst_event_replace (&mux->force_key_unit_event, NULL);
679 mux->force_key_unit_event = event;
688 /* now GstCollectPads can take care of the rest, e.g. EOS */
690 ret = mux->collect_event (pad, event);
692 gst_object_unref (mux);
699 * gst_matroska_mux_video_pad_setcaps:
700 * @pad: Pad which got the caps.
703 * Setcaps function for video sink pad.
705 * Returns: #TRUE on success.
708 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
710 GstMatroskaTrackContext *context = NULL;
711 GstMatroskaTrackVideoContext *videocontext;
713 GstMatroskaPad *collect_pad;
714 GstStructure *structure;
715 const gchar *mimetype;
716 const GValue *value = NULL;
717 const GstBuffer *codec_buf = NULL;
718 gint width, height, pixel_width, pixel_height;
720 gboolean interlaced = FALSE;
722 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
725 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
726 g_assert (collect_pad);
727 context = collect_pad->track;
729 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
730 videocontext = (GstMatroskaTrackVideoContext *) context;
732 /* gst -> matroska ID'ing */
733 structure = gst_caps_get_structure (caps, 0);
735 mimetype = gst_structure_get_name (structure);
737 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
739 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
741 if (!strcmp (mimetype, "video/x-theora")) {
742 /* we'll extract the details later from the theora identification header */
746 /* get general properties */
747 /* spec says it is mandatory */
748 if (!gst_structure_get_int (structure, "width", &width) ||
749 !gst_structure_get_int (structure, "height", &height))
752 videocontext->pixel_width = width;
753 videocontext->pixel_height = height;
754 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
756 context->default_duration =
757 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
758 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
759 GST_TIME_ARGS (context->default_duration));
761 context->default_duration = 0;
763 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
764 &pixel_width, &pixel_height)) {
765 if (pixel_width > pixel_height) {
766 videocontext->display_width = width * pixel_width / pixel_height;
767 videocontext->display_height = height;
768 } else if (pixel_width < pixel_height) {
769 videocontext->display_width = width;
770 videocontext->display_height = height * pixel_height / pixel_width;
772 videocontext->display_width = 0;
773 videocontext->display_height = 0;
776 videocontext->display_width = 0;
777 videocontext->display_height = 0;
782 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
783 videocontext->fourcc = 0;
785 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
786 * data and other settings
790 /* extract codec_data, may turn out needed */
791 value = gst_structure_get_value (structure, "codec_data");
793 codec_buf = gst_value_get_buffer (value);
796 if (!strcmp (mimetype, "video/x-raw-yuv")) {
797 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
798 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
799 } else if (!strcmp (mimetype, "image/jpeg")) {
800 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
801 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
802 ||!strcmp (mimetype, "video/x-huffyuv")
803 || !strcmp (mimetype, "video/x-divx")
804 || !strcmp (mimetype, "video/x-dv")
805 || !strcmp (mimetype, "video/x-h263")
806 || !strcmp (mimetype, "video/x-msmpeg")
807 || !strcmp (mimetype, "video/x-wmv")) {
808 gst_riff_strf_vids *bih;
809 gint size = sizeof (gst_riff_strf_vids);
812 if (!strcmp (mimetype, "video/x-xvid"))
813 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
814 else if (!strcmp (mimetype, "video/x-huffyuv"))
815 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
816 else if (!strcmp (mimetype, "video/x-dv"))
817 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
818 else if (!strcmp (mimetype, "video/x-h263"))
819 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
820 else if (!strcmp (mimetype, "video/x-divx")) {
823 gst_structure_get_int (structure, "divxversion", &divxversion);
824 switch (divxversion) {
826 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
829 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
832 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
835 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
838 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
839 switch (msmpegversion) {
841 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
844 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
850 } else if (!strcmp (mimetype, "video/x-wmv")) {
853 if (gst_structure_get_fourcc (structure, "format", &format)) {
855 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
856 if (wmvversion == 2) {
857 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
858 } else if (wmvversion == 1) {
859 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
860 } else if (wmvversion == 3) {
861 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
869 bih = g_new0 (gst_riff_strf_vids, 1);
870 GST_WRITE_UINT32_LE (&bih->size, size);
871 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
872 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
873 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
874 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
875 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
876 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
877 videocontext->pixel_height * 3);
879 /* process codec private/initialization data, if any */
881 size += GST_BUFFER_SIZE (codec_buf);
882 bih = g_realloc (bih, size);
883 GST_WRITE_UINT32_LE (&bih->size, size);
884 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
885 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
888 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
889 context->codec_priv = (gpointer) bih;
890 context->codec_priv_size = size;
891 } else if (!strcmp (mimetype, "video/x-h264")) {
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
894 if (context->codec_priv != NULL) {
895 g_free (context->codec_priv);
896 context->codec_priv = NULL;
897 context->codec_priv_size = 0;
900 /* Create avcC header */
901 if (codec_buf != NULL) {
902 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
903 context->codec_priv = g_malloc0 (context->codec_priv_size);
904 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
905 context->codec_priv_size);
907 } else if (!strcmp (mimetype, "video/x-theora")) {
908 const GValue *streamheader;
910 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
912 if (context->codec_priv != NULL) {
913 g_free (context->codec_priv);
914 context->codec_priv = NULL;
915 context->codec_priv_size = 0;
918 streamheader = gst_structure_get_value (structure, "streamheader");
919 if (!theora_streamheader_to_codecdata (streamheader, context)) {
920 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
921 ("theora stream headers missing or malformed"));
924 } else if (!strcmp (mimetype, "video/x-dirac")) {
925 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
926 } else if (!strcmp (mimetype, "video/x-vp8")) {
927 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
928 } else if (!strcmp (mimetype, "video/mpeg")) {
931 gst_structure_get_int (structure, "mpegversion", &mpegversion);
932 switch (mpegversion) {
934 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
937 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
940 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
946 /* global headers may be in codec data */
947 if (codec_buf != NULL) {
948 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
949 context->codec_priv = g_malloc0 (context->codec_priv_size);
950 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
951 context->codec_priv_size);
953 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
955 /* can only make it here if preceding case verified it was version 3 */
956 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
957 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
959 const GValue *mdpr_data;
961 gst_structure_get_int (structure, "rmversion", &rmversion);
964 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
967 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
970 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
973 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
979 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
980 if (mdpr_data != NULL) {
981 guint8 *priv_data = NULL;
982 guint priv_data_size = 0;
984 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
986 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
987 priv_data = g_malloc0 (priv_data_size);
989 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
991 context->codec_priv = priv_data;
992 context->codec_priv_size = priv_data_size;
1001 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1002 GST_PAD_NAME (pad), caps);
1007 /* N > 0 to expect a particular number of headers, negative if the
1008 number of headers is variable */
1010 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1011 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1013 GstBuffer **buf = NULL;
1016 guint bufi, i, offset, priv_data_size;
1018 if (streamheader == NULL)
1019 goto no_stream_headers;
1021 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1024 bufarr = g_value_peek_pointer (streamheader);
1025 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1027 if (N > 0 && bufarr->len != N)
1030 context->xiph_headers_to_skip = bufarr->len;
1032 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1033 for (i = 0; i < bufarr->len; i++) {
1034 GValue *bufval = &g_array_index (bufarr, GValue, i);
1036 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1038 goto wrong_content_type;
1041 buf[i] = g_value_peek_pointer (bufval);
1045 if (bufarr->len > 0) {
1046 for (i = 0; i < bufarr->len - 1; i++) {
1047 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1051 for (i = 0; i < bufarr->len; ++i) {
1052 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1055 priv_data = g_malloc0 (priv_data_size);
1057 priv_data[0] = bufarr->len - 1;
1060 if (bufarr->len > 0) {
1061 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1062 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1063 priv_data[offset++] = 0xff;
1065 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1069 for (i = 0; i < bufarr->len; ++i) {
1070 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1071 GST_BUFFER_SIZE (buf[i]));
1072 offset += GST_BUFFER_SIZE (buf[i]);
1075 context->codec_priv = priv_data;
1076 context->codec_priv_size = priv_data_size;
1079 *p_buf0 = gst_buffer_ref (buf[0]);
1088 GST_WARNING ("required streamheaders missing in sink caps!");
1093 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1094 G_VALUE_TYPE_NAME (streamheader));
1099 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1104 GST_WARNING ("streamheaders array does not contain GstBuffers");
1110 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1111 GstMatroskaTrackContext * context)
1113 GstBuffer *buf0 = NULL;
1115 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1118 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1119 GST_WARNING ("First vorbis header too small, ignoring");
1121 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1122 GstMatroskaTrackAudioContext *audiocontext;
1125 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1126 audiocontext = (GstMatroskaTrackAudioContext *) context;
1127 audiocontext->channels = GST_READ_UINT8 (hdr);
1128 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1133 gst_buffer_unref (buf0);
1139 theora_streamheader_to_codecdata (const GValue * streamheader,
1140 GstMatroskaTrackContext * context)
1142 GstBuffer *buf0 = NULL;
1144 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1147 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1148 GST_WARNING ("First theora header too small, ignoring");
1149 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1150 GST_WARNING ("First header not a theora identification header, ignoring");
1152 GstMatroskaTrackVideoContext *videocontext;
1153 guint fps_num, fps_denom, par_num, par_denom;
1156 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1158 videocontext = (GstMatroskaTrackVideoContext *) context;
1159 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1160 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1161 hdr += 3 + 3 + 1 + 1;
1162 fps_num = GST_READ_UINT32_BE (hdr);
1163 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1164 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1165 fps_denom, fps_num);
1167 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1168 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1169 if (par_num > 0 && par_num > 0) {
1170 if (par_num > par_denom) {
1171 videocontext->display_width =
1172 videocontext->pixel_width * par_num / par_denom;
1173 videocontext->display_height = videocontext->pixel_height;
1174 } else if (par_num < par_denom) {
1175 videocontext->display_width = videocontext->pixel_width;
1176 videocontext->display_height =
1177 videocontext->pixel_height * par_denom / par_num;
1179 videocontext->display_width = 0;
1180 videocontext->display_height = 0;
1183 videocontext->display_width = 0;
1184 videocontext->display_height = 0;
1190 gst_buffer_unref (buf0);
1196 kate_streamheader_to_codecdata (const GValue * streamheader,
1197 GstMatroskaTrackContext * context)
1199 GstBuffer *buf0 = NULL;
1201 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1204 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1205 GST_WARNING ("First kate header too small, ignoring");
1206 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1207 GST_WARNING ("First header not a kate identification header, ignoring");
1211 gst_buffer_unref (buf0);
1217 flac_streamheader_to_codecdata (const GValue * streamheader,
1218 GstMatroskaTrackContext * context)
1225 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1226 GST_WARNING ("No or invalid streamheader field in the caps");
1230 bufarr = g_value_peek_pointer (streamheader);
1231 if (bufarr->len < 2) {
1232 GST_WARNING ("Too few headers in streamheader field");
1236 context->xiph_headers_to_skip = bufarr->len + 1;
1238 bufval = &g_array_index (bufarr, GValue, 0);
1239 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1240 GST_WARNING ("streamheaders array does not contain GstBuffers");
1244 buffer = g_value_peek_pointer (bufval);
1246 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1247 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1248 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1249 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1250 GST_WARNING ("Invalid streamheader for FLAC");
1254 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1255 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1256 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1257 GST_BUFFER_SIZE (buffer) - 9);
1259 for (i = 1; i < bufarr->len; i++) {
1260 bufval = &g_array_index (bufarr, GValue, i);
1262 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1263 g_free (context->codec_priv);
1264 context->codec_priv = NULL;
1265 context->codec_priv_size = 0;
1266 GST_WARNING ("streamheaders array does not contain GstBuffers");
1270 buffer = g_value_peek_pointer (bufval);
1272 context->codec_priv =
1273 g_realloc (context->codec_priv,
1274 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1275 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1276 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1277 context->codec_priv_size =
1278 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1285 speex_streamheader_to_codecdata (const GValue * streamheader,
1286 GstMatroskaTrackContext * context)
1292 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1293 GST_WARNING ("No or invalid streamheader field in the caps");
1297 bufarr = g_value_peek_pointer (streamheader);
1298 if (bufarr->len != 2) {
1299 GST_WARNING ("Too few headers in streamheader field");
1303 context->xiph_headers_to_skip = bufarr->len + 1;
1305 bufval = &g_array_index (bufarr, GValue, 0);
1306 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1307 GST_WARNING ("streamheaders array does not contain GstBuffers");
1311 buffer = g_value_peek_pointer (bufval);
1313 if (GST_BUFFER_SIZE (buffer) < 80
1314 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1315 GST_WARNING ("Invalid streamheader for Speex");
1319 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1320 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1321 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1322 GST_BUFFER_SIZE (buffer));
1324 bufval = &g_array_index (bufarr, GValue, 1);
1326 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1327 g_free (context->codec_priv);
1328 context->codec_priv = NULL;
1329 context->codec_priv_size = 0;
1330 GST_WARNING ("streamheaders array does not contain GstBuffers");
1334 buffer = g_value_peek_pointer (bufval);
1336 context->codec_priv =
1337 g_realloc (context->codec_priv,
1338 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1339 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1340 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1341 context->codec_priv_size =
1342 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1347 static const gchar *
1348 aac_codec_data_to_codec_id (const GstBuffer * buf)
1350 const gchar *result;
1353 /* default to MAIN */
1356 if (GST_BUFFER_SIZE (buf) >= 2) {
1357 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1375 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1384 * gst_matroska_mux_audio_pad_setcaps:
1385 * @pad: Pad which got the caps.
1388 * Setcaps function for audio sink pad.
1390 * Returns: #TRUE on success.
1393 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1395 GstMatroskaTrackContext *context = NULL;
1396 GstMatroskaTrackAudioContext *audiocontext;
1397 GstMatroskaMux *mux;
1398 GstMatroskaPad *collect_pad;
1399 const gchar *mimetype;
1400 gint samplerate = 0, channels = 0;
1401 GstStructure *structure;
1402 const GValue *codec_data = NULL;
1403 const GstBuffer *buf = NULL;
1404 const gchar *stream_format = NULL;
1406 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1409 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1410 g_assert (collect_pad);
1411 context = collect_pad->track;
1413 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1414 audiocontext = (GstMatroskaTrackAudioContext *) context;
1416 structure = gst_caps_get_structure (caps, 0);
1417 mimetype = gst_structure_get_name (structure);
1420 gst_structure_get_int (structure, "rate", &samplerate);
1421 gst_structure_get_int (structure, "channels", &channels);
1423 audiocontext->samplerate = samplerate;
1424 audiocontext->channels = channels;
1425 audiocontext->bitdepth = 0;
1426 context->default_duration = 0;
1428 codec_data = gst_structure_get_value (structure, "codec_data");
1430 buf = gst_value_get_buffer (codec_data);
1432 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1433 * data and other settings
1437 if (!strcmp (mimetype, "audio/mpeg")) {
1438 gint mpegversion = 0;
1440 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1441 switch (mpegversion) {
1447 gst_structure_get_int (structure, "layer", &layer);
1449 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1450 GST_WARNING_OBJECT (mux,
1451 "Unable to determine MPEG audio version, assuming 1");
1457 else if (layer == 2)
1459 else if (version == 2)
1464 context->default_duration =
1465 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1469 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1472 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1475 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1484 stream_format = gst_structure_get_string (structure, "stream-format");
1485 /* check this is raw aac */
1486 if (stream_format) {
1487 if (strcmp (stream_format, "raw") != 0) {
1488 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1492 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1497 if (mpegversion == 2)
1499 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1500 aac_codec_data_to_codec_id (buf));
1501 else if (mpegversion == 4)
1503 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1504 aac_codec_data_to_codec_id (buf));
1506 g_assert_not_reached ();
1508 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1515 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1517 gint endianness = G_LITTLE_ENDIAN;
1518 gboolean signedness = TRUE;
1520 if (!gst_structure_get_int (structure, "width", &width) ||
1521 !gst_structure_get_int (structure, "depth", &depth) ||
1522 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1523 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1528 !gst_structure_get_int (structure, "endianness", &endianness)) {
1529 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1533 if (width != depth) {
1534 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1538 /* FIXME: where is this spec'ed out? (tpm) */
1539 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1540 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1544 audiocontext->bitdepth = depth;
1545 if (endianness == G_BIG_ENDIAN)
1546 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1548 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1550 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1553 if (!gst_structure_get_int (structure, "width", &width)) {
1554 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1558 audiocontext->bitdepth = width;
1559 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1561 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1562 const GValue *streamheader;
1564 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1566 if (context->codec_priv != NULL) {
1567 g_free (context->codec_priv);
1568 context->codec_priv = NULL;
1569 context->codec_priv_size = 0;
1572 streamheader = gst_structure_get_value (structure, "streamheader");
1573 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1574 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1575 ("vorbis stream headers missing or malformed"));
1578 } else if (!strcmp (mimetype, "audio/x-flac")) {
1579 const GValue *streamheader;
1581 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1582 if (context->codec_priv != NULL) {
1583 g_free (context->codec_priv);
1584 context->codec_priv = NULL;
1585 context->codec_priv_size = 0;
1588 streamheader = gst_structure_get_value (structure, "streamheader");
1589 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1590 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1591 ("flac stream headers missing or malformed"));
1594 } else if (!strcmp (mimetype, "audio/x-speex")) {
1595 const GValue *streamheader;
1597 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1598 if (context->codec_priv != NULL) {
1599 g_free (context->codec_priv);
1600 context->codec_priv = NULL;
1601 context->codec_priv_size = 0;
1604 streamheader = gst_structure_get_value (structure, "streamheader");
1605 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1606 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1607 ("speex stream headers missing or malformed"));
1610 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1611 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1612 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1613 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1614 } else if (!strcmp (mimetype, "audio/x-dts")) {
1615 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1616 } else if (!strcmp (mimetype, "audio/x-tta")) {
1619 /* TTA frame duration */
1620 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1622 gst_structure_get_int (structure, "width", &width);
1623 audiocontext->bitdepth = width;
1624 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1626 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1628 const GValue *mdpr_data;
1630 gst_structure_get_int (structure, "raversion", &raversion);
1631 switch (raversion) {
1633 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1636 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1639 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1645 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1646 if (mdpr_data != NULL) {
1647 guint8 *priv_data = NULL;
1648 guint priv_data_size = 0;
1650 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1652 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1653 priv_data = g_malloc0 (priv_data_size);
1655 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1657 context->codec_priv = priv_data;
1658 context->codec_priv_size = priv_data_size;
1661 } else if (!strcmp (mimetype, "audio/x-wma")
1662 || !strcmp (mimetype, "audio/x-alaw")
1663 || !strcmp (mimetype, "audio/x-mulaw")) {
1665 guint codec_priv_size;
1670 if (samplerate == 0 || channels == 0) {
1671 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1675 if (!strcmp (mimetype, "audio/x-wma")) {
1679 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1680 || !gst_structure_get_int (structure, "block_align", &block_align)
1681 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1682 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1687 switch (wmaversion) {
1689 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1692 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1695 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1698 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1702 if (gst_structure_get_int (structure, "depth", &depth))
1703 audiocontext->bitdepth = depth;
1704 } else if (!strcmp (mimetype, "audio/x-alaw")
1705 || !strcmp (mimetype, "audio/x-mulaw")) {
1706 audiocontext->bitdepth = 8;
1707 if (!strcmp (mimetype, "audio/x-alaw"))
1708 format = GST_RIFF_WAVE_FORMAT_ALAW;
1710 format = GST_RIFF_WAVE_FORMAT_MULAW;
1712 block_align = channels;
1713 bitrate = block_align * samplerate;
1715 g_assert (format != 0);
1717 codec_priv_size = WAVEFORMATEX_SIZE;
1719 codec_priv_size += GST_BUFFER_SIZE (buf);
1721 /* serialize waveformatex structure */
1722 codec_priv = g_malloc0 (codec_priv_size);
1723 GST_WRITE_UINT16_LE (codec_priv, format);
1724 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1725 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1726 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1727 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1728 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1730 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1732 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1734 /* process codec private/initialization data, if any */
1736 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1737 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1740 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1741 context->codec_priv = (gpointer) codec_priv;
1742 context->codec_priv_size = codec_priv_size;
1750 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1751 GST_PAD_NAME (pad), caps);
1758 * gst_matroska_mux_subtitle_pad_setcaps:
1759 * @pad: Pad which got the caps.
1762 * Setcaps function for subtitle sink pad.
1764 * Returns: #TRUE on success.
1767 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1770 * Consider this as boilerplate code for now. There is
1771 * no single subtitle creation element in GStreamer,
1772 * neither do I know how subtitling works at all. */
1774 /* There is now (at least) one such alement (kateenc), and I'm going
1775 to handle it here and claim it works when it can be piped back
1776 through GStreamer and VLC */
1778 GstMatroskaTrackContext *context = NULL;
1779 GstMatroskaTrackSubtitleContext *scontext;
1780 GstMatroskaMux *mux;
1781 GstMatroskaPad *collect_pad;
1782 const gchar *mimetype;
1783 GstStructure *structure;
1785 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1788 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1789 g_assert (collect_pad);
1790 context = collect_pad->track;
1792 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1793 scontext = (GstMatroskaTrackSubtitleContext *) context;
1795 structure = gst_caps_get_structure (caps, 0);
1796 mimetype = gst_structure_get_name (structure);
1799 scontext->check_utf8 = 1;
1800 scontext->invalid_utf8 = 0;
1801 context->default_duration = 0;
1803 /* TODO: - other format than Kate */
1805 if (!strcmp (mimetype, "subtitle/x-kate")) {
1806 const GValue *streamheader;
1808 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1810 if (context->codec_priv != NULL) {
1811 g_free (context->codec_priv);
1812 context->codec_priv = NULL;
1813 context->codec_priv_size = 0;
1816 streamheader = gst_structure_get_value (structure, "streamheader");
1817 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1818 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1819 ("kate stream headers missing or malformed"));
1830 * gst_matroska_mux_request_new_pad:
1831 * @element: #GstMatroskaMux.
1832 * @templ: #GstPadTemplate.
1833 * @pad_name: New pad name.
1835 * Request pad function for sink templates.
1837 * Returns: New #GstPad.
1840 gst_matroska_mux_request_new_pad (GstElement * element,
1841 GstPadTemplate * templ, const gchar * req_name)
1843 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1844 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1845 GstMatroskaPad *collect_pad;
1846 GstPad *newpad = NULL;
1848 const gchar *pad_name = NULL;
1849 GstPadSetCapsFunction setcapsfunc = NULL;
1850 GstMatroskaTrackContext *context = NULL;
1853 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1854 /* don't mix named and unnamed pads, if the pad already exists we fail when
1855 * trying to add it */
1856 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1857 pad_name = req_name;
1859 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1862 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1863 context = (GstMatroskaTrackContext *)
1864 g_new0 (GstMatroskaTrackAudioContext, 1);
1865 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1866 context->name = g_strdup ("Audio");
1867 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1868 /* don't mix named and unnamed pads, if the pad already exists we fail when
1869 * trying to add it */
1870 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1871 pad_name = req_name;
1873 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1876 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1877 context = (GstMatroskaTrackContext *)
1878 g_new0 (GstMatroskaTrackVideoContext, 1);
1879 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1880 context->name = g_strdup ("Video");
1881 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1882 /* don't mix named and unnamed pads, if the pad already exists we fail when
1883 * trying to add it */
1884 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1885 pad_name = req_name;
1887 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1890 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1891 context = (GstMatroskaTrackContext *)
1892 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1893 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1894 context->name = g_strdup ("Subtitle");
1896 GST_WARNING_OBJECT (mux, "This is not our template!");
1900 newpad = gst_pad_new_from_template (templ, pad_name);
1902 collect_pad = (GstMatroskaPad *)
1903 gst_collect_pads_add_pad_full (mux->collect, newpad,
1904 sizeof (GstMatroskaPad),
1905 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1907 collect_pad->track = context;
1908 gst_matroska_pad_reset (collect_pad, FALSE);
1910 /* FIXME: hacked way to override/extend the event function of
1911 * GstCollectPads; because it sets its own event function giving the
1912 * element no access to events.
1913 * TODO GstCollectPads should really give its 'users' a clean chance to
1914 * properly handle events that are not meant for collectpads itself.
1915 * Perhaps a callback or so, though rejected (?) in #340060.
1916 * This would allow (clean) transcoding of info from demuxer/streams
1917 * to another muxer */
1918 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1919 gst_pad_set_event_function (newpad,
1920 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1922 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1923 gst_pad_set_active (newpad, TRUE);
1924 if (!gst_element_add_pad (element, newpad))
1925 goto pad_add_failed;
1929 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1936 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1937 gst_object_unref (newpad);
1943 * gst_matroska_mux_release_pad:
1944 * @element: #GstMatroskaMux.
1945 * @pad: Pad to release.
1947 * Release a previously requested pad.
1950 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1952 GstMatroskaMux *mux;
1955 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1957 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1958 GstCollectData *cdata = (GstCollectData *) walk->data;
1959 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1961 if (cdata->pad == pad) {
1962 GstClockTime min_dur; /* observed minimum duration */
1964 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1965 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1966 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1967 if (collect_pad->duration < min_dur)
1968 collect_pad->duration = min_dur;
1971 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1972 mux->duration < collect_pad->duration)
1973 mux->duration = collect_pad->duration;
1979 gst_collect_pads_remove_pad (mux->collect, pad);
1980 if (gst_element_remove_pad (element, pad))
1986 * gst_matroska_mux_track_header:
1987 * @mux: #GstMatroskaMux
1988 * @context: Tack context.
1990 * Write a track header.
1993 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1994 GstMatroskaTrackContext * context)
1996 GstEbmlWrite *ebml = mux->ebml_write;
1999 /* TODO: check if everything necessary is written and check default values */
2001 /* track type goes before the type-specific stuff */
2002 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2003 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2005 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2006 gst_matroska_mux_create_uid ());
2007 if (context->default_duration) {
2008 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2009 context->default_duration);
2011 if (context->language) {
2012 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2016 /* type-specific stuff */
2017 switch (context->type) {
2018 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2019 GstMatroskaTrackVideoContext *videocontext =
2020 (GstMatroskaTrackVideoContext *) context;
2022 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2023 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2024 videocontext->pixel_width);
2025 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2026 videocontext->pixel_height);
2027 if (videocontext->display_width && videocontext->display_height) {
2028 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2029 videocontext->display_width);
2030 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2031 videocontext->display_height);
2033 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2034 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2035 if (videocontext->fourcc) {
2036 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2038 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2039 (gpointer) & fcc_le, 4);
2041 gst_ebml_write_master_finish (ebml, master);
2046 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2047 GstMatroskaTrackAudioContext *audiocontext =
2048 (GstMatroskaTrackAudioContext *) context;
2050 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2051 if (audiocontext->samplerate != 8000)
2052 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2053 audiocontext->samplerate);
2054 if (audiocontext->channels != 1)
2055 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2056 audiocontext->channels);
2057 if (audiocontext->bitdepth) {
2058 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2059 audiocontext->bitdepth);
2061 gst_ebml_write_master_finish (ebml, master);
2067 /* doesn't need type-specific data */
2071 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2072 if (context->codec_priv)
2073 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2074 context->codec_priv, context->codec_priv_size);
2075 /* FIXME: until we have a nice way of getting the codecname
2076 * out of the caps, I'm not going to enable this. Too much
2077 * (useless, double, boring) work... */
2078 /* TODO: Use value from tags if any */
2079 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2080 context->codec_name); */
2081 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2086 * gst_matroska_mux_start:
2087 * @mux: #GstMatroskaMux
2089 * Start a new matroska file (write headers etc...)
2092 gst_matroska_mux_start (GstMatroskaMux * mux)
2094 GstEbmlWrite *ebml = mux->ebml_write;
2095 const gchar *doctype;
2096 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2097 GST_MATROSKA_ID_TRACKS,
2098 GST_MATROSKA_ID_CUES,
2099 GST_MATROSKA_ID_TAGS,
2102 guint64 master, child;
2106 GstClockTime duration = 0;
2107 guint32 segment_uid[4];
2108 GTimeVal time = { 0, 0 };
2110 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2111 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2113 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2115 /* we start with a EBML header */
2116 doctype = mux->doctype;
2117 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2118 doctype, mux->doctype_version);
2119 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2121 /* the rest of the header is cached */
2122 gst_ebml_write_set_cache (ebml, 0x1000);
2124 /* start a segment */
2126 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2127 mux->segment_master = ebml->pos;
2129 if (!mux->streamable) {
2130 /* seekhead (table of contents) - we set the positions later */
2131 mux->seekhead_pos = ebml->pos;
2132 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2133 for (i = 0; seekhead_id[i] != 0; i++) {
2134 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2135 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2136 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2137 gst_ebml_write_master_finish (ebml, child);
2139 gst_ebml_write_master_finish (ebml, master);
2142 if (mux->streamable) {
2143 const GstTagList *tags;
2146 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2148 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2149 guint64 master_tags, master_tag;
2151 GST_DEBUG ("Writing tags");
2153 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2154 mux->tags_pos = ebml->pos;
2155 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2156 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2157 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2158 gst_ebml_write_master_finish (ebml, master_tag);
2159 gst_ebml_write_master_finish (ebml, master_tags);
2164 mux->info_pos = ebml->pos;
2165 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2166 for (i = 0; i < 4; i++) {
2167 segment_uid[i] = g_random_int ();
2169 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2170 (guint8 *) segment_uid, 16);
2171 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2172 mux->duration_pos = ebml->pos;
2174 if (!mux->streamable) {
2175 for (collected = mux->collect->data; collected;
2176 collected = g_slist_next (collected)) {
2177 GstMatroskaPad *collect_pad;
2178 GstFormat format = GST_FORMAT_TIME;
2180 gint64 trackduration;
2182 collect_pad = (GstMatroskaPad *) collected->data;
2183 thepad = collect_pad->collect.pad;
2185 /* Query the total length of the track. */
2186 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2187 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2188 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2189 GST_TIME_ARGS (trackduration));
2190 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2191 duration = (GstClockTime) trackduration;
2195 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2196 gst_guint64_to_gdouble (duration) /
2197 gst_guint64_to_gdouble (mux->time_scale));
2199 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2200 "GStreamer plugin version " PACKAGE_VERSION);
2201 if (mux->writing_app && mux->writing_app[0]) {
2202 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2204 g_get_current_time (&time);
2205 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2206 gst_ebml_write_master_finish (ebml, master);
2209 mux->tracks_pos = ebml->pos;
2210 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2212 for (collected = mux->collect->data; collected;
2213 collected = g_slist_next (collected)) {
2214 GstMatroskaPad *collect_pad;
2217 collect_pad = (GstMatroskaPad *) collected->data;
2218 thepad = collect_pad->collect.pad;
2220 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2221 collect_pad->track->codec_id != 0) {
2222 collect_pad->track->num = tracknum++;
2223 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2224 gst_matroska_mux_track_header (mux, collect_pad->track);
2225 gst_ebml_write_master_finish (ebml, child);
2226 /* some remaing pad/track setup */
2227 collect_pad->default_duration_scaled =
2228 gst_util_uint64_scale (collect_pad->track->default_duration,
2229 1, mux->time_scale);
2232 gst_ebml_write_master_finish (ebml, master);
2234 /* lastly, flush the cache */
2235 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2239 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2242 /* TODO: more sensible tag mappings */
2245 const gchar *matroska_tagname;
2246 const gchar *gstreamer_tagname;
2250 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2251 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2252 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2253 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2254 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2255 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2256 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2257 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2258 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2259 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2260 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2261 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2262 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2263 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2264 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2266 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2268 guint64 simpletag_master;
2270 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2271 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2272 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2274 if (strcmp (tagname_gst, tag) == 0) {
2275 GValue src = { 0, };
2278 if (!gst_tag_list_copy_value (&src, list, tag))
2280 if ((dest = gst_value_serialize (&src))) {
2282 simpletag_master = gst_ebml_write_master_start (ebml,
2283 GST_MATROSKA_ID_SIMPLETAG);
2284 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2285 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2286 gst_ebml_write_master_finish (ebml, simpletag_master);
2289 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2291 g_value_unset (&src);
2299 * gst_matroska_mux_finish:
2300 * @mux: #GstMatroskaMux
2302 * Finish a new matroska file (write index etc...)
2305 gst_matroska_mux_finish (GstMatroskaMux * mux)
2307 GstEbmlWrite *ebml = mux->ebml_write;
2309 guint64 duration = 0;
2311 const GstTagList *tags;
2313 /* finish last cluster */
2315 gst_ebml_write_master_finish (ebml, mux->cluster);
2319 if (mux->index != NULL) {
2321 guint64 master, pointentry_master, trackpos_master;
2323 mux->cues_pos = ebml->pos;
2324 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2325 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2327 for (n = 0; n < mux->num_indexes; n++) {
2328 GstMatroskaIndex *idx = &mux->index[n];
2330 pointentry_master = gst_ebml_write_master_start (ebml,
2331 GST_MATROSKA_ID_POINTENTRY);
2332 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2333 idx->time / mux->time_scale);
2334 trackpos_master = gst_ebml_write_master_start (ebml,
2335 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2336 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2337 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2338 idx->pos - mux->segment_master);
2339 gst_ebml_write_master_finish (ebml, trackpos_master);
2340 gst_ebml_write_master_finish (ebml, pointentry_master);
2343 gst_ebml_write_master_finish (ebml, master);
2344 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2348 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2350 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2351 guint64 master_tags, master_tag;
2353 GST_DEBUG ("Writing tags");
2355 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2356 mux->tags_pos = ebml->pos;
2357 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2358 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2359 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2360 gst_ebml_write_master_finish (ebml, master_tag);
2361 gst_ebml_write_master_finish (ebml, master_tags);
2364 /* update seekhead. We know that:
2365 * - a seekhead contains 4 entries.
2366 * - order of entries is as above.
2367 * - a seekhead has a 4-byte header + 8-byte length
2368 * - each entry is 2-byte master, 2-byte ID pointer,
2369 * 2-byte length pointer, all 8/1-byte length, 4-
2370 * byte ID and 8-byte length pointer, where the
2371 * length pointer starts at 20.
2372 * - all entries are local to the segment (so pos - segment_master).
2373 * - so each entry is at 12 + 20 + num * 28. */
2374 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2375 mux->info_pos - mux->segment_master);
2376 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2377 mux->tracks_pos - mux->segment_master);
2378 if (mux->index != NULL) {
2379 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2380 mux->cues_pos - mux->segment_master);
2383 guint64 my_pos = ebml->pos;
2385 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2386 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2387 gst_ebml_write_seek (ebml, my_pos);
2390 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2391 mux->tags_pos - mux->segment_master);
2394 guint64 my_pos = ebml->pos;
2396 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2397 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2398 gst_ebml_write_seek (ebml, my_pos);
2401 /* update duration */
2402 /* first get the overall duration */
2403 /* a released track may have left a duration in here */
2404 duration = mux->duration;
2405 for (collected = mux->collect->data; collected;
2406 collected = g_slist_next (collected)) {
2407 GstMatroskaPad *collect_pad;
2408 GstClockTime min_duration; /* observed minimum duration */
2410 collect_pad = (GstMatroskaPad *) collected->data;
2412 GST_DEBUG_OBJECT (mux,
2413 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2414 " end ts %" GST_TIME_FORMAT, collect_pad,
2415 GST_TIME_ARGS (collect_pad->start_ts),
2416 GST_TIME_ARGS (collect_pad->end_ts));
2418 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2419 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2421 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2422 if (collect_pad->duration < min_duration)
2423 collect_pad->duration = min_duration;
2424 GST_DEBUG_OBJECT (collect_pad,
2425 "final track duration: %" GST_TIME_FORMAT,
2426 GST_TIME_ARGS (collect_pad->duration));
2429 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2430 duration < collect_pad->duration)
2431 duration = collect_pad->duration;
2433 if (duration != 0) {
2434 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2435 GST_TIME_ARGS (duration));
2436 pos = mux->ebml_write->pos;
2437 gst_ebml_write_seek (ebml, mux->duration_pos);
2438 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2439 gst_guint64_to_gdouble (duration) /
2440 gst_guint64_to_gdouble (mux->time_scale));
2441 gst_ebml_write_seek (ebml, pos);
2444 guint64 my_pos = ebml->pos;
2446 gst_ebml_write_seek (ebml, mux->duration_pos);
2447 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2448 gst_ebml_write_seek (ebml, my_pos);
2450 GST_DEBUG_OBJECT (mux, "finishing segment");
2451 /* finish segment - this also writes element length */
2452 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2457 * gst_matroska_mux_best_pad:
2458 * @mux: #GstMatroskaMux
2459 * @popped: True if at least one buffer was popped from #GstCollectPads
2461 * Find a pad with the oldest data
2462 * (data from this pad should be written first).
2464 * Returns: Selected pad.
2466 static GstMatroskaPad *
2467 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2470 GstMatroskaPad *best = NULL;
2473 for (collected = mux->collect->data; collected;
2474 collected = g_slist_next (collected)) {
2475 GstMatroskaPad *collect_pad;
2477 collect_pad = (GstMatroskaPad *) collected->data;
2478 /* fetch a new buffer if needed */
2479 if (collect_pad->buffer == NULL) {
2480 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2481 (GstCollectData *) collect_pad);
2483 if (collect_pad->buffer != NULL) {
2487 /* convert to running time */
2488 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2489 /* invalid should pass */
2490 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2491 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2492 GST_FORMAT_TIME, time);
2493 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2494 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2495 GST_PAD_NAME (collect_pad->collect.pad));
2496 gst_buffer_unref (collect_pad->buffer);
2497 collect_pad->buffer = NULL;
2500 GST_LOG_OBJECT (mux, "buffer ts %" GST_TIME_FORMAT " -> %"
2501 GST_TIME_FORMAT " running time",
2502 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (collect_pad->buffer)),
2503 GST_TIME_ARGS (time));
2504 collect_pad->buffer =
2505 gst_buffer_make_metadata_writable (collect_pad->buffer);
2506 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2512 /* if we have a buffer check if it is better then the current best one */
2513 if (collect_pad->buffer != NULL) {
2514 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2515 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2516 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2517 GST_BUFFER_TIMESTAMP (best->buffer))) {
2527 * gst_matroska_mux_buffer_header:
2528 * @track: Track context.
2529 * @relative_timestamp: relative timestamp of the buffer
2530 * @flags: Buffer flags.
2532 * Create a buffer containing buffer header.
2534 * Returns: New buffer.
2537 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2538 gint16 relative_timestamp, int flags)
2542 hdr = gst_buffer_new_and_alloc (4);
2543 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2544 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2545 /* time relative to clustertime */
2546 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2549 GST_BUFFER_DATA (hdr)[3] = flags;
2554 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2555 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2556 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2559 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2560 GstMatroskaPad * collect_pad, GstBuffer * buf)
2562 GstMatroskaTrackVideoContext *ctx =
2563 (GstMatroskaTrackVideoContext *) collect_pad->track;
2564 const guint8 *data = GST_BUFFER_DATA (buf);
2565 guint size = GST_BUFFER_SIZE (buf);
2567 guint32 next_parse_offset;
2568 GstBuffer *ret = NULL;
2569 gboolean is_muxing_unit = FALSE;
2571 if (GST_BUFFER_SIZE (buf) < 13) {
2572 gst_buffer_unref (buf);
2576 /* Check if this buffer contains a picture or end-of-sequence packet */
2577 while (size >= 13) {
2578 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2579 gst_buffer_unref (buf);
2583 parse_code = GST_READ_UINT8 (data + 4);
2584 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2585 if (ctx->dirac_unit) {
2586 gst_buffer_unref (ctx->dirac_unit);
2587 ctx->dirac_unit = NULL;
2589 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2590 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2591 is_muxing_unit = TRUE;
2595 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2597 if (G_UNLIKELY (next_parse_offset == 0))
2600 data += next_parse_offset;
2601 size -= next_parse_offset;
2604 if (ctx->dirac_unit)
2605 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2607 ctx->dirac_unit = gst_buffer_ref (buf);
2609 if (is_muxing_unit) {
2610 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2611 ctx->dirac_unit = NULL;
2612 gst_buffer_copy_metadata (ret, buf,
2613 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2614 GST_BUFFER_COPY_CAPS);
2615 gst_buffer_unref (buf);
2617 gst_buffer_unref (buf);
2625 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2629 GValue streamheader = { 0 };
2630 GValue bufval = { 0 };
2631 GstBuffer *streamheader_buffer;
2632 GstEbmlWrite *ebml = mux->ebml_write;
2634 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2635 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2636 caps = gst_caps_new_simple ("video/webm", NULL);
2638 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2640 s = gst_caps_get_structure (caps, 0);
2641 g_value_init (&streamheader, GST_TYPE_ARRAY);
2642 g_value_init (&bufval, GST_TYPE_BUFFER);
2643 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2644 gst_value_set_buffer (&bufval, streamheader_buffer);
2645 gst_value_array_append_value (&streamheader, &bufval);
2646 g_value_unset (&bufval);
2647 gst_structure_set_value (s, "streamheader", &streamheader);
2648 g_value_unset (&streamheader);
2649 gst_caps_replace (&ebml->caps, caps);
2650 gst_buffer_unref (streamheader_buffer);
2651 gst_caps_unref (caps);
2655 * gst_matroska_mux_write_data:
2656 * @mux: #GstMatroskaMux
2657 * @collect_pad: #GstMatroskaPad with the data
2659 * Write collected data (called from gst_matroska_mux_collected).
2661 * Returns: Result of the gst_pad_push issued to write the data.
2663 static GstFlowReturn
2664 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2666 GstEbmlWrite *ebml = mux->ebml_write;
2667 GstBuffer *buf, *hdr;
2669 gboolean write_duration;
2670 gint16 relative_timestamp;
2671 gint64 relative_timestamp64;
2672 guint64 block_duration;
2673 gboolean is_video_keyframe = FALSE;
2676 buf = collect_pad->buffer;
2677 collect_pad->buffer = NULL;
2679 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2680 if (collect_pad->track->xiph_headers_to_skip > 0) {
2681 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2682 gst_buffer_unref (buf);
2683 --collect_pad->track->xiph_headers_to_skip;
2687 /* for dirac we have to queue up everything up to a picture unit */
2688 if (collect_pad->track->codec_id != NULL &&
2689 strcmp (collect_pad->track->codec_id,
2690 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2691 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2696 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2697 * this would wreak havoc with time stored in matroska file */
2698 /* TODO: maybe calculate a timestamp by using the previous timestamp
2699 * and default duration */
2700 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2701 GST_WARNING_OBJECT (collect_pad->collect.pad,
2702 "Invalid buffer timestamp; dropping buffer");
2703 gst_buffer_unref (buf);
2707 /* set the timestamp for outgoing buffers */
2708 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2710 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2711 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2712 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2713 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2714 is_video_keyframe = TRUE;
2718 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2719 * or when we may be reaching the limit of the relative timestamp */
2720 if (mux->cluster_time +
2721 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2722 || is_video_keyframe || mux->force_key_unit_event) {
2723 if (!mux->streamable)
2724 gst_ebml_write_master_finish (ebml, mux->cluster);
2726 /* Forward the GstForceKeyUnit event after finishing the cluster */
2727 if (mux->force_key_unit_event) {
2728 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2729 mux->force_key_unit_event = NULL;
2732 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2733 mux->cluster_pos = ebml->pos;
2734 gst_ebml_write_set_cache (ebml, 0x20);
2736 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2737 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2738 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2740 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2741 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2743 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2744 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2745 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2746 mux->prev_cluster_size);
2751 mux->cluster_pos = ebml->pos;
2752 gst_ebml_write_set_cache (ebml, 0x20);
2753 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2754 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2755 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2756 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2757 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2760 /* update duration of this track */
2761 if (GST_BUFFER_DURATION_IS_VALID (buf))
2762 collect_pad->duration += GST_BUFFER_DURATION (buf);
2764 /* We currently write index entries for all video tracks or for the audio
2765 * track in a single-track audio file. This could be improved by keeping the
2766 * index only for the *first* video track. */
2768 /* TODO: index is useful for every track, should contain the number of
2769 * the block in the cluster which contains the timestamp, should also work
2770 * for files with multiple audio tracks.
2772 if (!mux->streamable &&
2773 (is_video_keyframe ||
2774 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2775 (mux->num_streams == 1)))) {
2778 if (mux->min_index_interval != 0) {
2779 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2780 if (mux->index[last_idx].track == collect_pad->track->num)
2785 if (last_idx < 0 || mux->min_index_interval == 0 ||
2786 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2787 >= mux->min_index_interval)) {
2788 GstMatroskaIndex *idx;
2790 if (mux->num_indexes % 32 == 0) {
2791 mux->index = g_renew (GstMatroskaIndex, mux->index,
2792 mux->num_indexes + 32);
2794 idx = &mux->index[mux->num_indexes++];
2796 idx->pos = mux->cluster_pos;
2797 idx->time = GST_BUFFER_TIMESTAMP (buf);
2798 idx->track = collect_pad->track->num;
2802 /* Check if the duration differs from the default duration. */
2803 write_duration = FALSE;
2805 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2806 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
2807 1, mux->time_scale);
2809 /* small difference should be ok. */
2810 if (block_duration > collect_pad->default_duration_scaled + 1 ||
2811 block_duration < collect_pad->default_duration_scaled - 1) {
2812 write_duration = TRUE;
2816 /* write the block, for doctype v2 use SimpleBlock if possible
2817 * one slice (*breath*).
2818 * FIXME: Need to do correct lacing! */
2819 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2820 if (relative_timestamp64 >= 0) {
2821 /* round the timestamp */
2822 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2824 /* round the timestamp */
2825 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2827 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2829 if (mux->doctype_version > 1 && !write_duration) {
2831 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2834 gst_matroska_mux_create_buffer_header (collect_pad->track,
2835 relative_timestamp, flags);
2836 gst_ebml_write_set_cache (ebml, 0x40);
2837 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2838 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2839 gst_ebml_write_buffer (ebml, hdr);
2840 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2841 gst_ebml_write_buffer (ebml, buf);
2843 return gst_ebml_last_write_result (ebml);
2845 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2846 /* write and call order slightly unnatural,
2847 * but avoids seek and minizes pushing */
2848 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2850 gst_matroska_mux_create_buffer_header (collect_pad->track,
2851 relative_timestamp, 0);
2853 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
2854 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2855 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2856 gst_ebml_write_buffer (ebml, hdr);
2857 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2858 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2859 gst_ebml_write_buffer (ebml, buf);
2861 return gst_ebml_last_write_result (ebml);
2867 * gst_matroska_mux_collected:
2868 * @pads: #GstCollectPads
2869 * @uuser_data: #GstMatroskaMux
2871 * Collectpads callback.
2873 * Returns: #GstFlowReturn
2875 static GstFlowReturn
2876 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2878 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2879 GstEbmlWrite *ebml = mux->ebml_write;
2880 GstMatroskaPad *best;
2882 GstFlowReturn ret = GST_FLOW_OK;
2884 GST_DEBUG_OBJECT (mux, "Collected pads");
2886 /* start with a header */
2887 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2888 if (mux->collect->data == NULL) {
2889 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2890 ("No input streams configured"));
2891 return GST_FLOW_ERROR;
2893 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2894 gst_ebml_start_streamheader (ebml);
2895 gst_matroska_mux_start (mux);
2896 gst_matroska_mux_stop_streamheader (mux);
2897 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2901 /* which stream to write from? */
2902 best = gst_matroska_mux_best_pad (mux, &popped);
2904 /* if there is no best pad, we have reached EOS */
2906 /* buffer popped, but none returned means it was clipped */
2909 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2910 if (!mux->streamable) {
2911 gst_matroska_mux_finish (mux);
2913 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2915 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2916 ret = GST_FLOW_UNEXPECTED;
2919 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2920 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2921 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2922 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2924 /* make note of first and last encountered timestamps, so we can calculate
2925 * the actual duration later when we send an updated header on eos */
2926 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2927 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2928 GstClockTime end_ts = start_ts;
2930 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2931 end_ts += GST_BUFFER_DURATION (best->buffer);
2932 else if (best->track->default_duration)
2933 end_ts += best->track->default_duration;
2935 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2936 best->end_ts = end_ts;
2938 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2939 start_ts < best->start_ts))
2940 best->start_ts = start_ts;
2943 /* write one buffer */
2944 ret = gst_matroska_mux_write_data (mux, best);
2945 } while (ret == GST_FLOW_OK && !popped);
2952 * gst_matroska_mux_change_state:
2953 * @element: #GstMatroskaMux
2954 * @transition: State change transition.
2956 * Change the muxer state.
2958 * Returns: #GstStateChangeReturn
2960 static GstStateChangeReturn
2961 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2963 GstStateChangeReturn ret;
2964 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2966 switch (transition) {
2967 case GST_STATE_CHANGE_NULL_TO_READY:
2969 case GST_STATE_CHANGE_READY_TO_PAUSED:
2970 gst_collect_pads_start (mux->collect);
2972 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2974 case GST_STATE_CHANGE_PAUSED_TO_READY:
2975 gst_collect_pads_stop (mux->collect);
2981 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2983 switch (transition) {
2984 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2986 case GST_STATE_CHANGE_PAUSED_TO_READY:
2987 gst_matroska_mux_reset (GST_ELEMENT (mux));
2989 case GST_STATE_CHANGE_READY_TO_NULL:
2999 gst_matroska_mux_set_property (GObject * object,
3000 guint prop_id, const GValue * value, GParamSpec * pspec)
3002 GstMatroskaMux *mux;
3004 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3005 mux = GST_MATROSKA_MUX (object);
3008 case ARG_WRITING_APP:
3009 if (!g_value_get_string (value)) {
3010 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3013 g_free (mux->writing_app);
3014 mux->writing_app = g_value_dup_string (value);
3016 case ARG_DOCTYPE_VERSION:
3017 mux->doctype_version = g_value_get_int (value);
3019 case ARG_MIN_INDEX_INTERVAL:
3020 mux->min_index_interval = g_value_get_int64 (value);
3022 case ARG_STREAMABLE:
3023 mux->streamable = g_value_get_boolean (value);
3026 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3032 gst_matroska_mux_get_property (GObject * object,
3033 guint prop_id, GValue * value, GParamSpec * pspec)
3035 GstMatroskaMux *mux;
3037 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3038 mux = GST_MATROSKA_MUX (object);
3041 case ARG_WRITING_APP:
3042 g_value_set_string (value, mux->writing_app);
3044 case ARG_DOCTYPE_VERSION:
3045 g_value_set_int (value, mux->doctype_version);
3047 case ARG_MIN_INDEX_INTERVAL:
3048 g_value_set_int64 (value, mux->min_index_interval);
3050 case ARG_STREAMABLE:
3051 g_value_set_boolean (value, mux->streamable);
3054 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);