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_%u",
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_%u",
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_%u",
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, "video/x-xvid") /* MS/VfW compatibility cases */
800 ||!strcmp (mimetype, "video/x-huffyuv")
801 || !strcmp (mimetype, "video/x-divx")
802 || !strcmp (mimetype, "video/x-dv")
803 || !strcmp (mimetype, "video/x-h263")
804 || !strcmp (mimetype, "video/x-msmpeg")
805 || !strcmp (mimetype, "video/x-wmv")
806 || !strcmp (mimetype, "image/jpeg")) {
807 gst_riff_strf_vids *bih;
808 gint size = sizeof (gst_riff_strf_vids);
811 if (!strcmp (mimetype, "video/x-xvid"))
812 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
813 else if (!strcmp (mimetype, "video/x-huffyuv"))
814 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
815 else if (!strcmp (mimetype, "video/x-dv"))
816 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
817 else if (!strcmp (mimetype, "video/x-h263"))
818 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
819 else if (!strcmp (mimetype, "video/x-divx")) {
822 gst_structure_get_int (structure, "divxversion", &divxversion);
823 switch (divxversion) {
825 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
828 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
831 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
834 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
837 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
838 switch (msmpegversion) {
840 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
843 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
849 } else if (!strcmp (mimetype, "video/x-wmv")) {
852 if (gst_structure_get_fourcc (structure, "format", &format)) {
854 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
855 if (wmvversion == 2) {
856 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
857 } else if (wmvversion == 1) {
858 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
859 } else if (wmvversion == 3) {
860 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
863 } else if (!strcmp (mimetype, "image/jpeg")) {
864 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
870 bih = g_new0 (gst_riff_strf_vids, 1);
871 GST_WRITE_UINT32_LE (&bih->size, size);
872 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
873 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
874 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
875 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
876 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
877 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
878 videocontext->pixel_height * 3);
880 /* process codec private/initialization data, if any */
882 size += GST_BUFFER_SIZE (codec_buf);
883 bih = g_realloc (bih, size);
884 GST_WRITE_UINT32_LE (&bih->size, size);
885 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
886 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
889 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
890 context->codec_priv = (gpointer) bih;
891 context->codec_priv_size = size;
892 } else if (!strcmp (mimetype, "video/x-h264")) {
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
895 if (context->codec_priv != NULL) {
896 g_free (context->codec_priv);
897 context->codec_priv = NULL;
898 context->codec_priv_size = 0;
901 /* Create avcC header */
902 if (codec_buf != NULL) {
903 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
904 context->codec_priv = g_malloc0 (context->codec_priv_size);
905 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
906 context->codec_priv_size);
908 } else if (!strcmp (mimetype, "video/x-theora")) {
909 const GValue *streamheader;
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
913 if (context->codec_priv != NULL) {
914 g_free (context->codec_priv);
915 context->codec_priv = NULL;
916 context->codec_priv_size = 0;
919 streamheader = gst_structure_get_value (structure, "streamheader");
920 if (!theora_streamheader_to_codecdata (streamheader, context)) {
921 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
922 ("theora stream headers missing or malformed"));
925 } else if (!strcmp (mimetype, "video/x-dirac")) {
926 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
927 } else if (!strcmp (mimetype, "video/x-vp8")) {
928 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
929 } else if (!strcmp (mimetype, "video/mpeg")) {
932 gst_structure_get_int (structure, "mpegversion", &mpegversion);
933 switch (mpegversion) {
935 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
938 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
941 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
947 /* global headers may be in codec data */
948 if (codec_buf != NULL) {
949 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
950 context->codec_priv = g_malloc0 (context->codec_priv_size);
951 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
952 context->codec_priv_size);
954 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
956 /* can only make it here if preceding case verified it was version 3 */
957 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
958 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
960 const GValue *mdpr_data;
962 gst_structure_get_int (structure, "rmversion", &rmversion);
965 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
968 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
971 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
974 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
980 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
981 if (mdpr_data != NULL) {
982 guint8 *priv_data = NULL;
983 guint priv_data_size = 0;
985 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
987 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
988 priv_data = g_malloc0 (priv_data_size);
990 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
992 context->codec_priv = priv_data;
993 context->codec_priv_size = priv_data_size;
1002 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1003 GST_PAD_NAME (pad), caps);
1008 /* N > 0 to expect a particular number of headers, negative if the
1009 number of headers is variable */
1011 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1012 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1014 GstBuffer **buf = NULL;
1017 guint bufi, i, offset, priv_data_size;
1019 if (streamheader == NULL)
1020 goto no_stream_headers;
1022 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1025 bufarr = g_value_peek_pointer (streamheader);
1026 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1028 if (N > 0 && bufarr->len != N)
1031 context->xiph_headers_to_skip = bufarr->len;
1033 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1034 for (i = 0; i < bufarr->len; i++) {
1035 GValue *bufval = &g_array_index (bufarr, GValue, i);
1037 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1039 goto wrong_content_type;
1042 buf[i] = g_value_peek_pointer (bufval);
1046 if (bufarr->len > 0) {
1047 for (i = 0; i < bufarr->len - 1; i++) {
1048 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1052 for (i = 0; i < bufarr->len; ++i) {
1053 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1056 priv_data = g_malloc0 (priv_data_size);
1058 priv_data[0] = bufarr->len - 1;
1061 if (bufarr->len > 0) {
1062 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1063 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1064 priv_data[offset++] = 0xff;
1066 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1070 for (i = 0; i < bufarr->len; ++i) {
1071 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1072 GST_BUFFER_SIZE (buf[i]));
1073 offset += GST_BUFFER_SIZE (buf[i]);
1076 context->codec_priv = priv_data;
1077 context->codec_priv_size = priv_data_size;
1080 *p_buf0 = gst_buffer_ref (buf[0]);
1089 GST_WARNING ("required streamheaders missing in sink caps!");
1094 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1095 G_VALUE_TYPE_NAME (streamheader));
1100 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1105 GST_WARNING ("streamheaders array does not contain GstBuffers");
1111 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1112 GstMatroskaTrackContext * context)
1114 GstBuffer *buf0 = NULL;
1116 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1119 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1120 GST_WARNING ("First vorbis header too small, ignoring");
1122 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1123 GstMatroskaTrackAudioContext *audiocontext;
1126 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1127 audiocontext = (GstMatroskaTrackAudioContext *) context;
1128 audiocontext->channels = GST_READ_UINT8 (hdr);
1129 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1134 gst_buffer_unref (buf0);
1140 theora_streamheader_to_codecdata (const GValue * streamheader,
1141 GstMatroskaTrackContext * context)
1143 GstBuffer *buf0 = NULL;
1145 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1148 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1149 GST_WARNING ("First theora header too small, ignoring");
1150 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1151 GST_WARNING ("First header not a theora identification header, ignoring");
1153 GstMatroskaTrackVideoContext *videocontext;
1154 guint fps_num, fps_denom, par_num, par_denom;
1157 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1159 videocontext = (GstMatroskaTrackVideoContext *) context;
1160 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1161 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1162 hdr += 3 + 3 + 1 + 1;
1163 fps_num = GST_READ_UINT32_BE (hdr);
1164 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1165 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1166 fps_denom, fps_num);
1168 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1169 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1170 if (par_num > 0 && par_num > 0) {
1171 if (par_num > par_denom) {
1172 videocontext->display_width =
1173 videocontext->pixel_width * par_num / par_denom;
1174 videocontext->display_height = videocontext->pixel_height;
1175 } else if (par_num < par_denom) {
1176 videocontext->display_width = videocontext->pixel_width;
1177 videocontext->display_height =
1178 videocontext->pixel_height * par_denom / par_num;
1180 videocontext->display_width = 0;
1181 videocontext->display_height = 0;
1184 videocontext->display_width = 0;
1185 videocontext->display_height = 0;
1191 gst_buffer_unref (buf0);
1197 kate_streamheader_to_codecdata (const GValue * streamheader,
1198 GstMatroskaTrackContext * context)
1200 GstBuffer *buf0 = NULL;
1202 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1205 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1206 GST_WARNING ("First kate header too small, ignoring");
1207 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1208 GST_WARNING ("First header not a kate identification header, ignoring");
1212 gst_buffer_unref (buf0);
1218 flac_streamheader_to_codecdata (const GValue * streamheader,
1219 GstMatroskaTrackContext * context)
1226 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1227 GST_WARNING ("No or invalid streamheader field in the caps");
1231 bufarr = g_value_peek_pointer (streamheader);
1232 if (bufarr->len < 2) {
1233 GST_WARNING ("Too few headers in streamheader field");
1237 context->xiph_headers_to_skip = bufarr->len + 1;
1239 bufval = &g_array_index (bufarr, GValue, 0);
1240 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1241 GST_WARNING ("streamheaders array does not contain GstBuffers");
1245 buffer = g_value_peek_pointer (bufval);
1247 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1248 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1249 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1250 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1251 GST_WARNING ("Invalid streamheader for FLAC");
1255 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1256 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1257 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1258 GST_BUFFER_SIZE (buffer) - 9);
1260 for (i = 1; i < bufarr->len; i++) {
1261 bufval = &g_array_index (bufarr, GValue, i);
1263 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1264 g_free (context->codec_priv);
1265 context->codec_priv = NULL;
1266 context->codec_priv_size = 0;
1267 GST_WARNING ("streamheaders array does not contain GstBuffers");
1271 buffer = g_value_peek_pointer (bufval);
1273 context->codec_priv =
1274 g_realloc (context->codec_priv,
1275 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1276 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1277 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1278 context->codec_priv_size =
1279 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1286 speex_streamheader_to_codecdata (const GValue * streamheader,
1287 GstMatroskaTrackContext * context)
1293 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1294 GST_WARNING ("No or invalid streamheader field in the caps");
1298 bufarr = g_value_peek_pointer (streamheader);
1299 if (bufarr->len != 2) {
1300 GST_WARNING ("Too few headers in streamheader field");
1304 context->xiph_headers_to_skip = bufarr->len + 1;
1306 bufval = &g_array_index (bufarr, GValue, 0);
1307 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1308 GST_WARNING ("streamheaders array does not contain GstBuffers");
1312 buffer = g_value_peek_pointer (bufval);
1314 if (GST_BUFFER_SIZE (buffer) < 80
1315 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1316 GST_WARNING ("Invalid streamheader for Speex");
1320 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1321 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1322 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1323 GST_BUFFER_SIZE (buffer));
1325 bufval = &g_array_index (bufarr, GValue, 1);
1327 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1328 g_free (context->codec_priv);
1329 context->codec_priv = NULL;
1330 context->codec_priv_size = 0;
1331 GST_WARNING ("streamheaders array does not contain GstBuffers");
1335 buffer = g_value_peek_pointer (bufval);
1337 context->codec_priv =
1338 g_realloc (context->codec_priv,
1339 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1340 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1341 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1342 context->codec_priv_size =
1343 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1348 static const gchar *
1349 aac_codec_data_to_codec_id (const GstBuffer * buf)
1351 const gchar *result;
1354 /* default to MAIN */
1357 if (GST_BUFFER_SIZE (buf) >= 2) {
1358 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1376 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1385 * gst_matroska_mux_audio_pad_setcaps:
1386 * @pad: Pad which got the caps.
1389 * Setcaps function for audio sink pad.
1391 * Returns: #TRUE on success.
1394 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1396 GstMatroskaTrackContext *context = NULL;
1397 GstMatroskaTrackAudioContext *audiocontext;
1398 GstMatroskaMux *mux;
1399 GstMatroskaPad *collect_pad;
1400 const gchar *mimetype;
1401 gint samplerate = 0, channels = 0;
1402 GstStructure *structure;
1403 const GValue *codec_data = NULL;
1404 const GstBuffer *buf = NULL;
1405 const gchar *stream_format = NULL;
1407 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1410 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1411 g_assert (collect_pad);
1412 context = collect_pad->track;
1414 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1415 audiocontext = (GstMatroskaTrackAudioContext *) context;
1417 structure = gst_caps_get_structure (caps, 0);
1418 mimetype = gst_structure_get_name (structure);
1421 gst_structure_get_int (structure, "rate", &samplerate);
1422 gst_structure_get_int (structure, "channels", &channels);
1424 audiocontext->samplerate = samplerate;
1425 audiocontext->channels = channels;
1426 audiocontext->bitdepth = 0;
1427 context->default_duration = 0;
1429 codec_data = gst_structure_get_value (structure, "codec_data");
1431 buf = gst_value_get_buffer (codec_data);
1433 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1434 * data and other settings
1438 if (!strcmp (mimetype, "audio/mpeg")) {
1439 gint mpegversion = 0;
1441 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1442 switch (mpegversion) {
1448 gst_structure_get_int (structure, "layer", &layer);
1450 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1451 GST_WARNING_OBJECT (mux,
1452 "Unable to determine MPEG audio version, assuming 1");
1458 else if (layer == 2)
1460 else if (version == 2)
1465 context->default_duration =
1466 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1470 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1473 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1476 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1485 stream_format = gst_structure_get_string (structure, "stream-format");
1486 /* check this is raw aac */
1487 if (stream_format) {
1488 if (strcmp (stream_format, "raw") != 0) {
1489 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1493 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1498 if (mpegversion == 2)
1500 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1501 aac_codec_data_to_codec_id (buf));
1502 else if (mpegversion == 4)
1504 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1505 aac_codec_data_to_codec_id (buf));
1507 g_assert_not_reached ();
1509 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1516 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1518 gint endianness = G_LITTLE_ENDIAN;
1519 gboolean signedness = TRUE;
1521 if (!gst_structure_get_int (structure, "width", &width) ||
1522 !gst_structure_get_int (structure, "depth", &depth) ||
1523 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1524 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1529 !gst_structure_get_int (structure, "endianness", &endianness)) {
1530 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1534 if (width != depth) {
1535 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1539 /* FIXME: where is this spec'ed out? (tpm) */
1540 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1541 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1545 audiocontext->bitdepth = depth;
1546 if (endianness == G_BIG_ENDIAN)
1547 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1549 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1551 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1554 if (!gst_structure_get_int (structure, "width", &width)) {
1555 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1559 audiocontext->bitdepth = width;
1560 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1562 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1563 const GValue *streamheader;
1565 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1567 if (context->codec_priv != NULL) {
1568 g_free (context->codec_priv);
1569 context->codec_priv = NULL;
1570 context->codec_priv_size = 0;
1573 streamheader = gst_structure_get_value (structure, "streamheader");
1574 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1575 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1576 ("vorbis stream headers missing or malformed"));
1579 } else if (!strcmp (mimetype, "audio/x-flac")) {
1580 const GValue *streamheader;
1582 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1583 if (context->codec_priv != NULL) {
1584 g_free (context->codec_priv);
1585 context->codec_priv = NULL;
1586 context->codec_priv_size = 0;
1589 streamheader = gst_structure_get_value (structure, "streamheader");
1590 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1591 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1592 ("flac stream headers missing or malformed"));
1595 } else if (!strcmp (mimetype, "audio/x-speex")) {
1596 const GValue *streamheader;
1598 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1599 if (context->codec_priv != NULL) {
1600 g_free (context->codec_priv);
1601 context->codec_priv = NULL;
1602 context->codec_priv_size = 0;
1605 streamheader = gst_structure_get_value (structure, "streamheader");
1606 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1607 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1608 ("speex stream headers missing or malformed"));
1611 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1612 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1613 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1614 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1615 } else if (!strcmp (mimetype, "audio/x-dts")) {
1616 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1617 } else if (!strcmp (mimetype, "audio/x-tta")) {
1620 /* TTA frame duration */
1621 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1623 gst_structure_get_int (structure, "width", &width);
1624 audiocontext->bitdepth = width;
1625 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1627 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1629 const GValue *mdpr_data;
1631 gst_structure_get_int (structure, "raversion", &raversion);
1632 switch (raversion) {
1634 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1637 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1640 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1646 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1647 if (mdpr_data != NULL) {
1648 guint8 *priv_data = NULL;
1649 guint priv_data_size = 0;
1651 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1653 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1654 priv_data = g_malloc0 (priv_data_size);
1656 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1658 context->codec_priv = priv_data;
1659 context->codec_priv_size = priv_data_size;
1662 } else if (!strcmp (mimetype, "audio/x-wma")
1663 || !strcmp (mimetype, "audio/x-alaw")
1664 || !strcmp (mimetype, "audio/x-mulaw")) {
1666 guint codec_priv_size;
1671 if (samplerate == 0 || channels == 0) {
1672 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1676 if (!strcmp (mimetype, "audio/x-wma")) {
1680 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1681 || !gst_structure_get_int (structure, "block_align", &block_align)
1682 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1683 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1688 switch (wmaversion) {
1690 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1693 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1696 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1699 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1703 if (gst_structure_get_int (structure, "depth", &depth))
1704 audiocontext->bitdepth = depth;
1705 } else if (!strcmp (mimetype, "audio/x-alaw")
1706 || !strcmp (mimetype, "audio/x-mulaw")) {
1707 audiocontext->bitdepth = 8;
1708 if (!strcmp (mimetype, "audio/x-alaw"))
1709 format = GST_RIFF_WAVE_FORMAT_ALAW;
1711 format = GST_RIFF_WAVE_FORMAT_MULAW;
1713 block_align = channels;
1714 bitrate = block_align * samplerate;
1716 g_assert (format != 0);
1718 codec_priv_size = WAVEFORMATEX_SIZE;
1720 codec_priv_size += GST_BUFFER_SIZE (buf);
1722 /* serialize waveformatex structure */
1723 codec_priv = g_malloc0 (codec_priv_size);
1724 GST_WRITE_UINT16_LE (codec_priv, format);
1725 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1726 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1727 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1728 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1729 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1731 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1733 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1735 /* process codec private/initialization data, if any */
1737 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1738 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1741 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1742 context->codec_priv = (gpointer) codec_priv;
1743 context->codec_priv_size = codec_priv_size;
1751 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1752 GST_PAD_NAME (pad), caps);
1759 * gst_matroska_mux_subtitle_pad_setcaps:
1760 * @pad: Pad which got the caps.
1763 * Setcaps function for subtitle sink pad.
1765 * Returns: #TRUE on success.
1768 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1771 * Consider this as boilerplate code for now. There is
1772 * no single subtitle creation element in GStreamer,
1773 * neither do I know how subtitling works at all. */
1775 /* There is now (at least) one such alement (kateenc), and I'm going
1776 to handle it here and claim it works when it can be piped back
1777 through GStreamer and VLC */
1779 GstMatroskaTrackContext *context = NULL;
1780 GstMatroskaTrackSubtitleContext *scontext;
1781 GstMatroskaMux *mux;
1782 GstMatroskaPad *collect_pad;
1783 const gchar *mimetype;
1784 GstStructure *structure;
1786 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1789 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1790 g_assert (collect_pad);
1791 context = collect_pad->track;
1793 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1794 scontext = (GstMatroskaTrackSubtitleContext *) context;
1796 structure = gst_caps_get_structure (caps, 0);
1797 mimetype = gst_structure_get_name (structure);
1800 scontext->check_utf8 = 1;
1801 scontext->invalid_utf8 = 0;
1802 context->default_duration = 0;
1804 /* TODO: - other format than Kate */
1806 if (!strcmp (mimetype, "subtitle/x-kate")) {
1807 const GValue *streamheader;
1809 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1811 if (context->codec_priv != NULL) {
1812 g_free (context->codec_priv);
1813 context->codec_priv = NULL;
1814 context->codec_priv_size = 0;
1817 streamheader = gst_structure_get_value (structure, "streamheader");
1818 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1819 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1820 ("kate stream headers missing or malformed"));
1831 * gst_matroska_mux_request_new_pad:
1832 * @element: #GstMatroskaMux.
1833 * @templ: #GstPadTemplate.
1834 * @pad_name: New pad name.
1836 * Request pad function for sink templates.
1838 * Returns: New #GstPad.
1841 gst_matroska_mux_request_new_pad (GstElement * element,
1842 GstPadTemplate * templ, const gchar * req_name)
1844 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1845 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1846 GstMatroskaPad *collect_pad;
1847 GstPad *newpad = NULL;
1849 const gchar *pad_name = NULL;
1850 GstPadSetCapsFunction setcapsfunc = NULL;
1851 GstMatroskaTrackContext *context = NULL;
1854 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
1855 /* don't mix named and unnamed pads, if the pad already exists we fail when
1856 * trying to add it */
1857 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
1858 pad_name = req_name;
1860 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
1863 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1864 context = (GstMatroskaTrackContext *)
1865 g_new0 (GstMatroskaTrackAudioContext, 1);
1866 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1867 context->name = g_strdup ("Audio");
1868 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
1869 /* don't mix named and unnamed pads, if the pad already exists we fail when
1870 * trying to add it */
1871 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
1872 pad_name = req_name;
1874 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
1877 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1878 context = (GstMatroskaTrackContext *)
1879 g_new0 (GstMatroskaTrackVideoContext, 1);
1880 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1881 context->name = g_strdup ("Video");
1882 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
1883 /* don't mix named and unnamed pads, if the pad already exists we fail when
1884 * trying to add it */
1885 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
1886 pad_name = req_name;
1888 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
1891 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1892 context = (GstMatroskaTrackContext *)
1893 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1894 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1895 context->name = g_strdup ("Subtitle");
1897 GST_WARNING_OBJECT (mux, "This is not our template!");
1901 newpad = gst_pad_new_from_template (templ, pad_name);
1903 collect_pad = (GstMatroskaPad *)
1904 gst_collect_pads_add_pad (mux->collect, newpad,
1905 sizeof (GstMatroskaPad),
1906 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1908 collect_pad->track = context;
1909 gst_matroska_pad_reset (collect_pad, FALSE);
1911 /* FIXME: hacked way to override/extend the event function of
1912 * GstCollectPads; because it sets its own event function giving the
1913 * element no access to events.
1914 * TODO GstCollectPads should really give its 'users' a clean chance to
1915 * properly handle events that are not meant for collectpads itself.
1916 * Perhaps a callback or so, though rejected (?) in #340060.
1917 * This would allow (clean) transcoding of info from demuxer/streams
1918 * to another muxer */
1919 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1920 gst_pad_set_event_function (newpad,
1921 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1923 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1924 gst_pad_set_active (newpad, TRUE);
1925 if (!gst_element_add_pad (element, newpad))
1926 goto pad_add_failed;
1930 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1937 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1938 gst_object_unref (newpad);
1944 * gst_matroska_mux_release_pad:
1945 * @element: #GstMatroskaMux.
1946 * @pad: Pad to release.
1948 * Release a previously requested pad.
1951 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1953 GstMatroskaMux *mux;
1956 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1958 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1959 GstCollectData *cdata = (GstCollectData *) walk->data;
1960 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1962 if (cdata->pad == pad) {
1963 GstClockTime min_dur; /* observed minimum duration */
1965 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1966 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1967 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1968 if (collect_pad->duration < min_dur)
1969 collect_pad->duration = min_dur;
1972 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1973 mux->duration < collect_pad->duration)
1974 mux->duration = collect_pad->duration;
1980 gst_collect_pads_remove_pad (mux->collect, pad);
1981 if (gst_element_remove_pad (element, pad))
1987 * gst_matroska_mux_track_header:
1988 * @mux: #GstMatroskaMux
1989 * @context: Tack context.
1991 * Write a track header.
1994 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1995 GstMatroskaTrackContext * context)
1997 GstEbmlWrite *ebml = mux->ebml_write;
2000 /* TODO: check if everything necessary is written and check default values */
2002 /* track type goes before the type-specific stuff */
2003 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2004 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2006 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2007 gst_matroska_mux_create_uid ());
2008 if (context->default_duration) {
2009 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2010 context->default_duration);
2012 if (context->language) {
2013 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2017 /* type-specific stuff */
2018 switch (context->type) {
2019 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2020 GstMatroskaTrackVideoContext *videocontext =
2021 (GstMatroskaTrackVideoContext *) context;
2023 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2024 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2025 videocontext->pixel_width);
2026 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2027 videocontext->pixel_height);
2028 if (videocontext->display_width && videocontext->display_height) {
2029 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2030 videocontext->display_width);
2031 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2032 videocontext->display_height);
2034 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2035 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2036 if (videocontext->fourcc) {
2037 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2039 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2040 (gpointer) & fcc_le, 4);
2042 gst_ebml_write_master_finish (ebml, master);
2047 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2048 GstMatroskaTrackAudioContext *audiocontext =
2049 (GstMatroskaTrackAudioContext *) context;
2051 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2052 if (audiocontext->samplerate != 8000)
2053 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2054 audiocontext->samplerate);
2055 if (audiocontext->channels != 1)
2056 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2057 audiocontext->channels);
2058 if (audiocontext->bitdepth) {
2059 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2060 audiocontext->bitdepth);
2062 gst_ebml_write_master_finish (ebml, master);
2068 /* doesn't need type-specific data */
2072 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2073 if (context->codec_priv)
2074 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2075 context->codec_priv, context->codec_priv_size);
2076 /* FIXME: until we have a nice way of getting the codecname
2077 * out of the caps, I'm not going to enable this. Too much
2078 * (useless, double, boring) work... */
2079 /* TODO: Use value from tags if any */
2080 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2081 context->codec_name); */
2082 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2087 * gst_matroska_mux_start:
2088 * @mux: #GstMatroskaMux
2090 * Start a new matroska file (write headers etc...)
2093 gst_matroska_mux_start (GstMatroskaMux * mux)
2095 GstEbmlWrite *ebml = mux->ebml_write;
2096 const gchar *doctype;
2097 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2098 GST_MATROSKA_ID_TRACKS,
2099 GST_MATROSKA_ID_CUES,
2100 GST_MATROSKA_ID_TAGS,
2103 guint64 master, child;
2107 GstClockTime duration = 0;
2108 guint32 segment_uid[4];
2109 GTimeVal time = { 0, 0 };
2111 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2112 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2114 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2116 /* we start with a EBML header */
2117 doctype = mux->doctype;
2118 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2119 doctype, mux->doctype_version);
2120 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2122 /* the rest of the header is cached */
2123 gst_ebml_write_set_cache (ebml, 0x1000);
2125 /* start a segment */
2127 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2128 mux->segment_master = ebml->pos;
2130 if (!mux->streamable) {
2131 /* seekhead (table of contents) - we set the positions later */
2132 mux->seekhead_pos = ebml->pos;
2133 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2134 for (i = 0; seekhead_id[i] != 0; i++) {
2135 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2136 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2137 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2138 gst_ebml_write_master_finish (ebml, child);
2140 gst_ebml_write_master_finish (ebml, master);
2143 if (mux->streamable) {
2144 const GstTagList *tags;
2147 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2149 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2150 guint64 master_tags, master_tag;
2152 GST_DEBUG ("Writing tags");
2154 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2155 mux->tags_pos = ebml->pos;
2156 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2157 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2158 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2159 gst_ebml_write_master_finish (ebml, master_tag);
2160 gst_ebml_write_master_finish (ebml, master_tags);
2165 mux->info_pos = ebml->pos;
2166 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2167 for (i = 0; i < 4; i++) {
2168 segment_uid[i] = g_random_int ();
2170 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2171 (guint8 *) segment_uid, 16);
2172 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2173 mux->duration_pos = ebml->pos;
2175 if (!mux->streamable) {
2176 for (collected = mux->collect->data; collected;
2177 collected = g_slist_next (collected)) {
2178 GstMatroskaPad *collect_pad;
2179 GstFormat format = GST_FORMAT_TIME;
2181 gint64 trackduration;
2183 collect_pad = (GstMatroskaPad *) collected->data;
2184 thepad = collect_pad->collect.pad;
2186 /* Query the total length of the track. */
2187 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2188 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2189 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2190 GST_TIME_ARGS (trackduration));
2191 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2192 duration = (GstClockTime) trackduration;
2196 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2197 gst_guint64_to_gdouble (duration) /
2198 gst_guint64_to_gdouble (mux->time_scale));
2200 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2201 "GStreamer plugin version " PACKAGE_VERSION);
2202 if (mux->writing_app && mux->writing_app[0]) {
2203 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2205 g_get_current_time (&time);
2206 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2207 gst_ebml_write_master_finish (ebml, master);
2210 mux->tracks_pos = ebml->pos;
2211 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2213 for (collected = mux->collect->data; collected;
2214 collected = g_slist_next (collected)) {
2215 GstMatroskaPad *collect_pad;
2218 collect_pad = (GstMatroskaPad *) collected->data;
2219 thepad = collect_pad->collect.pad;
2221 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2222 collect_pad->track->codec_id != 0) {
2223 collect_pad->track->num = tracknum++;
2224 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2225 gst_matroska_mux_track_header (mux, collect_pad->track);
2226 gst_ebml_write_master_finish (ebml, child);
2227 /* some remaing pad/track setup */
2228 collect_pad->default_duration_scaled =
2229 gst_util_uint64_scale (collect_pad->track->default_duration,
2230 1, mux->time_scale);
2233 gst_ebml_write_master_finish (ebml, master);
2235 /* lastly, flush the cache */
2236 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2240 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2243 /* TODO: more sensible tag mappings */
2246 const gchar *matroska_tagname;
2247 const gchar *gstreamer_tagname;
2251 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2252 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2253 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2254 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2255 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2256 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2257 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2258 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2259 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2260 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2261 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2262 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2263 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2264 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2265 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2267 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2269 guint64 simpletag_master;
2271 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2272 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2273 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2275 if (strcmp (tagname_gst, tag) == 0) {
2276 GValue src = { 0, };
2279 if (!gst_tag_list_copy_value (&src, list, tag))
2281 if ((dest = gst_value_serialize (&src))) {
2283 simpletag_master = gst_ebml_write_master_start (ebml,
2284 GST_MATROSKA_ID_SIMPLETAG);
2285 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2286 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2287 gst_ebml_write_master_finish (ebml, simpletag_master);
2290 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2292 g_value_unset (&src);
2300 * gst_matroska_mux_finish:
2301 * @mux: #GstMatroskaMux
2303 * Finish a new matroska file (write index etc...)
2306 gst_matroska_mux_finish (GstMatroskaMux * mux)
2308 GstEbmlWrite *ebml = mux->ebml_write;
2310 guint64 duration = 0;
2312 const GstTagList *tags;
2314 /* finish last cluster */
2316 gst_ebml_write_master_finish (ebml, mux->cluster);
2320 if (mux->index != NULL) {
2322 guint64 master, pointentry_master, trackpos_master;
2324 mux->cues_pos = ebml->pos;
2325 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2326 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2328 for (n = 0; n < mux->num_indexes; n++) {
2329 GstMatroskaIndex *idx = &mux->index[n];
2331 pointentry_master = gst_ebml_write_master_start (ebml,
2332 GST_MATROSKA_ID_POINTENTRY);
2333 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2334 idx->time / mux->time_scale);
2335 trackpos_master = gst_ebml_write_master_start (ebml,
2336 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2337 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2338 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2339 idx->pos - mux->segment_master);
2340 gst_ebml_write_master_finish (ebml, trackpos_master);
2341 gst_ebml_write_master_finish (ebml, pointentry_master);
2344 gst_ebml_write_master_finish (ebml, master);
2345 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2349 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2351 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2352 guint64 master_tags, master_tag;
2354 GST_DEBUG ("Writing tags");
2356 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2357 mux->tags_pos = ebml->pos;
2358 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2359 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2360 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2361 gst_ebml_write_master_finish (ebml, master_tag);
2362 gst_ebml_write_master_finish (ebml, master_tags);
2365 /* update seekhead. We know that:
2366 * - a seekhead contains 4 entries.
2367 * - order of entries is as above.
2368 * - a seekhead has a 4-byte header + 8-byte length
2369 * - each entry is 2-byte master, 2-byte ID pointer,
2370 * 2-byte length pointer, all 8/1-byte length, 4-
2371 * byte ID and 8-byte length pointer, where the
2372 * length pointer starts at 20.
2373 * - all entries are local to the segment (so pos - segment_master).
2374 * - so each entry is at 12 + 20 + num * 28. */
2375 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2376 mux->info_pos - mux->segment_master);
2377 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2378 mux->tracks_pos - mux->segment_master);
2379 if (mux->index != NULL) {
2380 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2381 mux->cues_pos - mux->segment_master);
2384 guint64 my_pos = ebml->pos;
2386 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2387 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2388 gst_ebml_write_seek (ebml, my_pos);
2391 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2392 mux->tags_pos - mux->segment_master);
2395 guint64 my_pos = ebml->pos;
2397 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2398 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2399 gst_ebml_write_seek (ebml, my_pos);
2402 /* update duration */
2403 /* first get the overall duration */
2404 /* a released track may have left a duration in here */
2405 duration = mux->duration;
2406 for (collected = mux->collect->data; collected;
2407 collected = g_slist_next (collected)) {
2408 GstMatroskaPad *collect_pad;
2409 GstClockTime min_duration; /* observed minimum duration */
2411 collect_pad = (GstMatroskaPad *) collected->data;
2413 GST_DEBUG_OBJECT (mux,
2414 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2415 " end ts %" GST_TIME_FORMAT, collect_pad,
2416 GST_TIME_ARGS (collect_pad->start_ts),
2417 GST_TIME_ARGS (collect_pad->end_ts));
2419 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2420 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2422 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2423 if (collect_pad->duration < min_duration)
2424 collect_pad->duration = min_duration;
2425 GST_DEBUG_OBJECT (collect_pad,
2426 "final track duration: %" GST_TIME_FORMAT,
2427 GST_TIME_ARGS (collect_pad->duration));
2430 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2431 duration < collect_pad->duration)
2432 duration = collect_pad->duration;
2434 if (duration != 0) {
2435 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2436 GST_TIME_ARGS (duration));
2437 pos = mux->ebml_write->pos;
2438 gst_ebml_write_seek (ebml, mux->duration_pos);
2439 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2440 gst_guint64_to_gdouble (duration) /
2441 gst_guint64_to_gdouble (mux->time_scale));
2442 gst_ebml_write_seek (ebml, pos);
2445 guint64 my_pos = ebml->pos;
2447 gst_ebml_write_seek (ebml, mux->duration_pos);
2448 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2449 gst_ebml_write_seek (ebml, my_pos);
2451 GST_DEBUG_OBJECT (mux, "finishing segment");
2452 /* finish segment - this also writes element length */
2453 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2458 * gst_matroska_mux_best_pad:
2459 * @mux: #GstMatroskaMux
2460 * @popped: True if at least one buffer was popped from #GstCollectPads
2462 * Find a pad with the oldest data
2463 * (data from this pad should be written first).
2465 * Returns: Selected pad.
2467 static GstMatroskaPad *
2468 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2471 GstMatroskaPad *best = NULL;
2474 for (collected = mux->collect->data; collected;
2475 collected = g_slist_next (collected)) {
2476 GstMatroskaPad *collect_pad;
2478 collect_pad = (GstMatroskaPad *) collected->data;
2479 /* fetch a new buffer if needed */
2480 if (collect_pad->buffer == NULL) {
2481 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2482 (GstCollectData *) collect_pad);
2484 if (collect_pad->buffer != NULL) {
2488 /* convert to running time */
2489 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2490 /* invalid should pass */
2491 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2492 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2493 GST_FORMAT_TIME, time);
2494 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2495 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2496 GST_PAD_NAME (collect_pad->collect.pad));
2497 gst_buffer_unref (collect_pad->buffer);
2498 collect_pad->buffer = NULL;
2501 GST_LOG_OBJECT (mux, "buffer ts %" GST_TIME_FORMAT " -> %"
2502 GST_TIME_FORMAT " running time",
2503 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (collect_pad->buffer)),
2504 GST_TIME_ARGS (time));
2505 collect_pad->buffer =
2506 gst_buffer_make_metadata_writable (collect_pad->buffer);
2507 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2513 /* if we have a buffer check if it is better then the current best one */
2514 if (collect_pad->buffer != NULL) {
2515 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2516 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2517 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2518 GST_BUFFER_TIMESTAMP (best->buffer))) {
2528 * gst_matroska_mux_buffer_header:
2529 * @track: Track context.
2530 * @relative_timestamp: relative timestamp of the buffer
2531 * @flags: Buffer flags.
2533 * Create a buffer containing buffer header.
2535 * Returns: New buffer.
2538 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2539 gint16 relative_timestamp, int flags)
2543 hdr = gst_buffer_new_and_alloc (4);
2544 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2545 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2546 /* time relative to clustertime */
2547 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2550 GST_BUFFER_DATA (hdr)[3] = flags;
2555 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2556 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2557 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2560 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2561 GstMatroskaPad * collect_pad, GstBuffer * buf)
2563 GstMatroskaTrackVideoContext *ctx =
2564 (GstMatroskaTrackVideoContext *) collect_pad->track;
2565 const guint8 *data = GST_BUFFER_DATA (buf);
2566 guint size = GST_BUFFER_SIZE (buf);
2568 guint32 next_parse_offset;
2569 GstBuffer *ret = NULL;
2570 gboolean is_muxing_unit = FALSE;
2572 if (GST_BUFFER_SIZE (buf) < 13) {
2573 gst_buffer_unref (buf);
2577 /* Check if this buffer contains a picture or end-of-sequence packet */
2578 while (size >= 13) {
2579 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2580 gst_buffer_unref (buf);
2584 parse_code = GST_READ_UINT8 (data + 4);
2585 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2586 if (ctx->dirac_unit) {
2587 gst_buffer_unref (ctx->dirac_unit);
2588 ctx->dirac_unit = NULL;
2590 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2591 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2592 is_muxing_unit = TRUE;
2596 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2598 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2601 data += next_parse_offset;
2602 size -= next_parse_offset;
2605 if (ctx->dirac_unit)
2606 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2608 ctx->dirac_unit = gst_buffer_ref (buf);
2610 if (is_muxing_unit) {
2611 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2612 ctx->dirac_unit = NULL;
2613 gst_buffer_copy_metadata (ret, buf,
2614 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2615 GST_BUFFER_COPY_CAPS);
2616 gst_buffer_unref (buf);
2618 gst_buffer_unref (buf);
2626 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2630 GValue streamheader = { 0 };
2631 GValue bufval = { 0 };
2632 GstBuffer *streamheader_buffer;
2633 GstEbmlWrite *ebml = mux->ebml_write;
2635 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2636 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2637 caps = gst_caps_new_simple ("video/webm", NULL);
2639 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2641 s = gst_caps_get_structure (caps, 0);
2642 g_value_init (&streamheader, GST_TYPE_ARRAY);
2643 g_value_init (&bufval, GST_TYPE_BUFFER);
2644 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2645 gst_value_set_buffer (&bufval, streamheader_buffer);
2646 gst_value_array_append_value (&streamheader, &bufval);
2647 g_value_unset (&bufval);
2648 gst_structure_set_value (s, "streamheader", &streamheader);
2649 g_value_unset (&streamheader);
2650 gst_caps_replace (&ebml->caps, caps);
2651 gst_buffer_unref (streamheader_buffer);
2652 gst_caps_unref (caps);
2656 * gst_matroska_mux_write_data:
2657 * @mux: #GstMatroskaMux
2658 * @collect_pad: #GstMatroskaPad with the data
2660 * Write collected data (called from gst_matroska_mux_collected).
2662 * Returns: Result of the gst_pad_push issued to write the data.
2664 static GstFlowReturn
2665 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2667 GstEbmlWrite *ebml = mux->ebml_write;
2668 GstBuffer *buf, *hdr;
2670 gboolean write_duration;
2671 gint16 relative_timestamp;
2672 gint64 relative_timestamp64;
2673 guint64 block_duration;
2674 gboolean is_video_keyframe = FALSE;
2677 buf = collect_pad->buffer;
2678 collect_pad->buffer = NULL;
2680 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2681 if (collect_pad->track->xiph_headers_to_skip > 0) {
2682 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2683 gst_buffer_unref (buf);
2684 --collect_pad->track->xiph_headers_to_skip;
2688 /* for dirac we have to queue up everything up to a picture unit */
2689 if (collect_pad->track->codec_id != NULL &&
2690 strcmp (collect_pad->track->codec_id,
2691 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2692 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2697 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2698 * this would wreak havoc with time stored in matroska file */
2699 /* TODO: maybe calculate a timestamp by using the previous timestamp
2700 * and default duration */
2701 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2702 GST_WARNING_OBJECT (collect_pad->collect.pad,
2703 "Invalid buffer timestamp; dropping buffer");
2704 gst_buffer_unref (buf);
2708 /* set the timestamp for outgoing buffers */
2709 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2711 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2712 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2713 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2714 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2715 is_video_keyframe = TRUE;
2719 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2720 * or when we may be reaching the limit of the relative timestamp */
2721 if (mux->cluster_time +
2722 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2723 || is_video_keyframe || mux->force_key_unit_event) {
2724 if (!mux->streamable)
2725 gst_ebml_write_master_finish (ebml, mux->cluster);
2727 /* Forward the GstForceKeyUnit event after finishing the cluster */
2728 if (mux->force_key_unit_event) {
2729 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2730 mux->force_key_unit_event = NULL;
2733 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2734 mux->cluster_pos = ebml->pos;
2735 gst_ebml_write_set_cache (ebml, 0x20);
2737 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2738 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2739 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2741 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2742 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2744 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2745 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2746 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2747 mux->prev_cluster_size);
2752 mux->cluster_pos = ebml->pos;
2753 gst_ebml_write_set_cache (ebml, 0x20);
2754 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2755 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2756 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2757 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2758 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2761 /* update duration of this track */
2762 if (GST_BUFFER_DURATION_IS_VALID (buf))
2763 collect_pad->duration += GST_BUFFER_DURATION (buf);
2765 /* We currently write index entries for all video tracks or for the audio
2766 * track in a single-track audio file. This could be improved by keeping the
2767 * index only for the *first* video track. */
2769 /* TODO: index is useful for every track, should contain the number of
2770 * the block in the cluster which contains the timestamp, should also work
2771 * for files with multiple audio tracks.
2773 if (!mux->streamable &&
2774 (is_video_keyframe ||
2775 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2776 (mux->num_streams == 1)))) {
2779 if (mux->min_index_interval != 0) {
2780 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2781 if (mux->index[last_idx].track == collect_pad->track->num)
2786 if (last_idx < 0 || mux->min_index_interval == 0 ||
2787 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2788 >= mux->min_index_interval)) {
2789 GstMatroskaIndex *idx;
2791 if (mux->num_indexes % 32 == 0) {
2792 mux->index = g_renew (GstMatroskaIndex, mux->index,
2793 mux->num_indexes + 32);
2795 idx = &mux->index[mux->num_indexes++];
2797 idx->pos = mux->cluster_pos;
2798 idx->time = GST_BUFFER_TIMESTAMP (buf);
2799 idx->track = collect_pad->track->num;
2803 /* Check if the duration differs from the default duration. */
2804 write_duration = FALSE;
2806 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2807 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
2808 1, mux->time_scale);
2810 /* small difference should be ok. */
2811 if (block_duration > collect_pad->default_duration_scaled + 1 ||
2812 block_duration < collect_pad->default_duration_scaled - 1) {
2813 write_duration = TRUE;
2817 /* write the block, for doctype v2 use SimpleBlock if possible
2818 * one slice (*breath*).
2819 * FIXME: Need to do correct lacing! */
2820 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2821 if (relative_timestamp64 >= 0) {
2822 /* round the timestamp */
2823 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2825 /* round the timestamp */
2826 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2828 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2830 if (mux->doctype_version > 1 && !write_duration) {
2832 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2835 gst_matroska_mux_create_buffer_header (collect_pad->track,
2836 relative_timestamp, flags);
2837 gst_ebml_write_set_cache (ebml, 0x40);
2838 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2839 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2840 gst_ebml_write_buffer (ebml, hdr);
2841 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2842 gst_ebml_write_buffer (ebml, buf);
2844 return gst_ebml_last_write_result (ebml);
2846 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2847 /* write and call order slightly unnatural,
2848 * but avoids seek and minizes pushing */
2849 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2851 gst_matroska_mux_create_buffer_header (collect_pad->track,
2852 relative_timestamp, 0);
2854 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
2855 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2856 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2857 gst_ebml_write_buffer (ebml, hdr);
2858 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2859 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2860 gst_ebml_write_buffer (ebml, buf);
2862 return gst_ebml_last_write_result (ebml);
2868 * gst_matroska_mux_collected:
2869 * @pads: #GstCollectPads
2870 * @uuser_data: #GstMatroskaMux
2872 * Collectpads callback.
2874 * Returns: #GstFlowReturn
2876 static GstFlowReturn
2877 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2879 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2880 GstEbmlWrite *ebml = mux->ebml_write;
2881 GstMatroskaPad *best;
2883 GstFlowReturn ret = GST_FLOW_OK;
2885 GST_DEBUG_OBJECT (mux, "Collected pads");
2887 /* start with a header */
2888 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2889 if (mux->collect->data == NULL) {
2890 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2891 ("No input streams configured"));
2892 return GST_FLOW_ERROR;
2894 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2895 gst_ebml_start_streamheader (ebml);
2896 gst_matroska_mux_start (mux);
2897 gst_matroska_mux_stop_streamheader (mux);
2898 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2902 /* which stream to write from? */
2903 best = gst_matroska_mux_best_pad (mux, &popped);
2905 /* if there is no best pad, we have reached EOS */
2907 /* buffer popped, but none returned means it was clipped */
2910 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2911 if (!mux->streamable) {
2912 gst_matroska_mux_finish (mux);
2914 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2916 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2917 ret = GST_FLOW_UNEXPECTED;
2920 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2921 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2922 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2923 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2925 /* make note of first and last encountered timestamps, so we can calculate
2926 * the actual duration later when we send an updated header on eos */
2927 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2928 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2929 GstClockTime end_ts = start_ts;
2931 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2932 end_ts += GST_BUFFER_DURATION (best->buffer);
2933 else if (best->track->default_duration)
2934 end_ts += best->track->default_duration;
2936 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2937 best->end_ts = end_ts;
2939 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2940 start_ts < best->start_ts))
2941 best->start_ts = start_ts;
2944 /* write one buffer */
2945 ret = gst_matroska_mux_write_data (mux, best);
2946 } while (ret == GST_FLOW_OK && !popped);
2953 * gst_matroska_mux_change_state:
2954 * @element: #GstMatroskaMux
2955 * @transition: State change transition.
2957 * Change the muxer state.
2959 * Returns: #GstStateChangeReturn
2961 static GstStateChangeReturn
2962 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2964 GstStateChangeReturn ret;
2965 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2967 switch (transition) {
2968 case GST_STATE_CHANGE_NULL_TO_READY:
2970 case GST_STATE_CHANGE_READY_TO_PAUSED:
2971 gst_collect_pads_start (mux->collect);
2973 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2975 case GST_STATE_CHANGE_PAUSED_TO_READY:
2976 gst_collect_pads_stop (mux->collect);
2982 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2984 switch (transition) {
2985 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2987 case GST_STATE_CHANGE_PAUSED_TO_READY:
2988 gst_matroska_mux_reset (GST_ELEMENT (mux));
2990 case GST_STATE_CHANGE_READY_TO_NULL:
3000 gst_matroska_mux_set_property (GObject * object,
3001 guint prop_id, const GValue * value, GParamSpec * pspec)
3003 GstMatroskaMux *mux;
3005 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3006 mux = GST_MATROSKA_MUX (object);
3009 case ARG_WRITING_APP:
3010 if (!g_value_get_string (value)) {
3011 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3014 g_free (mux->writing_app);
3015 mux->writing_app = g_value_dup_string (value);
3017 case ARG_DOCTYPE_VERSION:
3018 mux->doctype_version = g_value_get_int (value);
3020 case ARG_MIN_INDEX_INTERVAL:
3021 mux->min_index_interval = g_value_get_int64 (value);
3023 case ARG_STREAMABLE:
3024 mux->streamable = g_value_get_boolean (value);
3027 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3033 gst_matroska_mux_get_property (GObject * object,
3034 guint prop_id, GValue * value, GParamSpec * pspec)
3036 GstMatroskaMux *mux;
3038 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3039 mux = GST_MATROSKA_MUX (object);
3042 case ARG_WRITING_APP:
3043 g_value_set_string (value, mux->writing_app);
3045 case ARG_DOCTYPE_VERSION:
3046 g_value_set_int (value, mux->doctype_version);
3048 case ARG_MIN_INDEX_INTERVAL:
3049 g_value_set_int64 (value, mux->min_index_interval);
3051 case ARG_STREAMABLE:
3052 g_value_set_boolean (value, mux->streamable);
3055 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);