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_object_unref (mux->collect);
395 gst_object_unref (mux->ebml_write);
396 if (mux->writing_app)
397 g_free (mux->writing_app);
399 G_OBJECT_CLASS (parent_class)->finalize (object);
404 * gst_matroska_mux_create_uid:
406 * Generate new unused track UID.
408 * Returns: New track UID.
411 gst_matroska_mux_create_uid (void)
418 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
423 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
424 for (i = 0; i < used_uids->len; i++) {
425 if (g_array_index (used_uids, guint64, i) == uid) {
430 g_array_append_val (used_uids, uid);
433 G_UNLOCK (used_uids);
439 * gst_matroska_pad_reset:
440 * @collect_pad: the #GstMatroskaPad
442 * Reset and/or release resources of a matroska collect pad.
445 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
448 GstMatroskaTrackType type = 0;
450 /* free track information */
451 if (collect_pad->track != NULL) {
452 /* retrieve for optional later use */
453 name = collect_pad->track->name;
454 type = collect_pad->track->type;
455 /* extra for video */
456 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
457 GstMatroskaTrackVideoContext *ctx =
458 (GstMatroskaTrackVideoContext *) collect_pad->track;
460 if (ctx->dirac_unit) {
461 gst_buffer_unref (ctx->dirac_unit);
462 ctx->dirac_unit = NULL;
465 g_free (collect_pad->track->codec_id);
466 g_free (collect_pad->track->codec_name);
468 g_free (collect_pad->track->name);
469 g_free (collect_pad->track->language);
470 g_free (collect_pad->track->codec_priv);
471 g_free (collect_pad->track);
472 collect_pad->track = NULL;
475 /* free cached buffer */
476 if (collect_pad->buffer != NULL) {
477 gst_buffer_unref (collect_pad->buffer);
478 collect_pad->buffer = NULL;
481 if (!full && type != 0) {
482 GstMatroskaTrackContext *context;
484 /* create a fresh context */
486 case GST_MATROSKA_TRACK_TYPE_VIDEO:
487 context = (GstMatroskaTrackContext *)
488 g_new0 (GstMatroskaTrackVideoContext, 1);
490 case GST_MATROSKA_TRACK_TYPE_AUDIO:
491 context = (GstMatroskaTrackContext *)
492 g_new0 (GstMatroskaTrackAudioContext, 1);
494 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
495 context = (GstMatroskaTrackContext *)
496 g_new0 (GstMatroskaTrackSubtitleContext, 1);
499 g_assert_not_reached ();
503 context->type = type;
504 context->name = name;
505 /* TODO: check default values for the context */
506 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
507 collect_pad->track = context;
508 collect_pad->buffer = NULL;
509 collect_pad->duration = 0;
510 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
511 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
516 * gst_matroska_pad_free:
517 * @collect_pad: the #GstMatroskaPad
519 * Release resources of a matroska collect pad.
522 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
524 gst_matroska_pad_reset (collect_pad, TRUE);
529 * gst_matroska_mux_reset:
530 * @element: #GstMatroskaMux that should be reseted.
532 * Reset matroska muxer back to initial state.
535 gst_matroska_mux_reset (GstElement * element)
537 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
540 /* reset EBML write */
541 gst_ebml_write_reset (mux->ebml_write);
544 mux->state = GST_MATROSKA_MUX_STATE_START;
546 /* clean up existing streams */
548 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
549 GstMatroskaPad *collect_pad;
551 collect_pad = (GstMatroskaPad *) walk->data;
553 /* reset collect pad to pristine state */
554 gst_matroska_pad_reset (collect_pad, FALSE);
558 mux->num_indexes = 0;
563 mux->time_scale = GST_MSECOND;
564 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
569 mux->cluster_time = 0;
570 mux->cluster_pos = 0;
571 mux->prev_cluster_size = 0;
574 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
578 * gst_matroska_mux_handle_src_event:
579 * @pad: Pad which received the event.
580 * @event: Received event.
582 * handle events - copied from oggmux without understanding
584 * Returns: #TRUE on success.
587 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
591 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
595 /* disable seeking for now */
601 return gst_pad_event_default (pad, event);
605 * gst_matroska_mux_handle_sink_event:
606 * @pad: Pad which received the event.
607 * @event: Received event.
609 * handle events - informational ones like tags
611 * Returns: #TRUE on success.
614 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
616 GstMatroskaTrackContext *context;
617 GstMatroskaPad *collect_pad;
622 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
624 switch (GST_EVENT_TYPE (event)) {
628 GST_DEBUG_OBJECT (mux, "received tag event");
629 gst_event_parse_tag (event, &list);
631 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
632 g_assert (collect_pad);
633 context = collect_pad->track;
636 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
637 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
638 const gchar *lang_code;
640 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
642 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
643 context->language = g_strdup (lang_code);
645 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
650 /* FIXME: what about stream-specific tags? */
651 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
652 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
654 gst_event_unref (event);
655 /* handled this, don't want collectpads to forward it downstream */
659 case GST_EVENT_NEWSEGMENT:
660 /* We don't support NEWSEGMENT events */
662 gst_event_unref (event);
669 /* now GstCollectPads can take care of the rest, e.g. EOS */
671 ret = mux->collect_event (pad, event);
673 gst_object_unref (mux);
680 * gst_matroska_mux_video_pad_setcaps:
681 * @pad: Pad which got the caps.
684 * Setcaps function for video sink pad.
686 * Returns: #TRUE on success.
689 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
691 GstMatroskaTrackContext *context = NULL;
692 GstMatroskaTrackVideoContext *videocontext;
694 GstMatroskaPad *collect_pad;
695 GstStructure *structure;
696 const gchar *mimetype;
697 const GValue *value = NULL;
698 const GstBuffer *codec_buf = NULL;
699 gint width, height, pixel_width, pixel_height;
701 gboolean interlaced = FALSE;
703 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
706 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
707 g_assert (collect_pad);
708 context = collect_pad->track;
710 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
711 videocontext = (GstMatroskaTrackVideoContext *) context;
713 /* gst -> matroska ID'ing */
714 structure = gst_caps_get_structure (caps, 0);
716 mimetype = gst_structure_get_name (structure);
718 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
720 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
722 if (!strcmp (mimetype, "video/x-theora")) {
723 /* we'll extract the details later from the theora identification header */
727 /* get general properties */
728 /* spec says it is mandatory */
729 if (!gst_structure_get_int (structure, "width", &width) ||
730 !gst_structure_get_int (structure, "height", &height))
733 videocontext->pixel_width = width;
734 videocontext->pixel_height = height;
735 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
737 context->default_duration =
738 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
739 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
740 GST_TIME_ARGS (context->default_duration));
742 context->default_duration = 0;
744 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
745 &pixel_width, &pixel_height)) {
746 if (pixel_width > pixel_height) {
747 videocontext->display_width = width * pixel_width / pixel_height;
748 videocontext->display_height = height;
749 } else if (pixel_width < pixel_height) {
750 videocontext->display_width = width;
751 videocontext->display_height = height * pixel_height / pixel_width;
753 videocontext->display_width = 0;
754 videocontext->display_height = 0;
757 videocontext->display_width = 0;
758 videocontext->display_height = 0;
763 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
764 videocontext->fourcc = 0;
766 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
767 * data and other settings
771 /* extract codec_data, may turn out needed */
772 value = gst_structure_get_value (structure, "codec_data");
774 codec_buf = gst_value_get_buffer (value);
777 if (!strcmp (mimetype, "video/x-raw-yuv")) {
778 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
779 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
780 } else if (!strcmp (mimetype, "image/jpeg")) {
781 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
782 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
783 ||!strcmp (mimetype, "video/x-huffyuv")
784 || !strcmp (mimetype, "video/x-divx")
785 || !strcmp (mimetype, "video/x-dv")
786 || !strcmp (mimetype, "video/x-h263")
787 || !strcmp (mimetype, "video/x-msmpeg")
788 || !strcmp (mimetype, "video/x-wmv")) {
789 gst_riff_strf_vids *bih;
790 gint size = sizeof (gst_riff_strf_vids);
793 if (!strcmp (mimetype, "video/x-xvid"))
794 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
795 else if (!strcmp (mimetype, "video/x-huffyuv"))
796 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
797 else if (!strcmp (mimetype, "video/x-dv"))
798 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
799 else if (!strcmp (mimetype, "video/x-h263"))
800 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
801 else if (!strcmp (mimetype, "video/x-divx")) {
804 gst_structure_get_int (structure, "divxversion", &divxversion);
805 switch (divxversion) {
807 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
810 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
813 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
816 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
819 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
820 switch (msmpegversion) {
822 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
825 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
831 } else if (!strcmp (mimetype, "video/x-wmv")) {
834 if (gst_structure_get_fourcc (structure, "format", &format)) {
836 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
837 if (wmvversion == 2) {
838 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
839 } else if (wmvversion == 1) {
840 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
841 } else if (wmvversion == 3) {
842 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
850 bih = g_new0 (gst_riff_strf_vids, 1);
851 GST_WRITE_UINT32_LE (&bih->size, size);
852 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
853 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
854 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
855 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
856 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
857 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
858 videocontext->pixel_height * 3);
860 /* process codec private/initialization data, if any */
862 size += GST_BUFFER_SIZE (codec_buf);
863 bih = g_realloc (bih, size);
864 GST_WRITE_UINT32_LE (&bih->size, size);
865 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
866 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
869 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
870 context->codec_priv = (gpointer) bih;
871 context->codec_priv_size = size;
872 } else if (!strcmp (mimetype, "video/x-h264")) {
873 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
875 if (context->codec_priv != NULL) {
876 g_free (context->codec_priv);
877 context->codec_priv = NULL;
878 context->codec_priv_size = 0;
881 /* Create avcC header */
882 if (codec_buf != NULL) {
883 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
884 context->codec_priv = g_malloc0 (context->codec_priv_size);
885 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
886 context->codec_priv_size);
888 } else if (!strcmp (mimetype, "video/x-theora")) {
889 const GValue *streamheader;
891 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
893 if (context->codec_priv != NULL) {
894 g_free (context->codec_priv);
895 context->codec_priv = NULL;
896 context->codec_priv_size = 0;
899 streamheader = gst_structure_get_value (structure, "streamheader");
900 if (!theora_streamheader_to_codecdata (streamheader, context)) {
901 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
902 ("theora stream headers missing or malformed"));
905 } else if (!strcmp (mimetype, "video/x-dirac")) {
906 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
907 } else if (!strcmp (mimetype, "video/x-vp8")) {
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
909 } else if (!strcmp (mimetype, "video/mpeg")) {
912 gst_structure_get_int (structure, "mpegversion", &mpegversion);
913 switch (mpegversion) {
915 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
918 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
921 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
927 /* global headers may be in codec data */
928 if (codec_buf != NULL) {
929 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
930 context->codec_priv = g_malloc0 (context->codec_priv_size);
931 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
932 context->codec_priv_size);
934 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
936 /* can only make it here if preceding case verified it was version 3 */
937 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
938 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
940 const GValue *mdpr_data;
942 gst_structure_get_int (structure, "rmversion", &rmversion);
945 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
948 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
951 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
954 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
960 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
961 if (mdpr_data != NULL) {
962 guint8 *priv_data = NULL;
963 guint priv_data_size = 0;
965 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
967 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
968 priv_data = g_malloc0 (priv_data_size);
970 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
972 context->codec_priv = priv_data;
973 context->codec_priv_size = priv_data_size;
982 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
983 GST_PAD_NAME (pad), caps);
988 /* N > 0 to expect a particular number of headers, negative if the
989 number of headers is variable */
991 xiphN_streamheader_to_codecdata (const GValue * streamheader,
992 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
994 GstBuffer **buf = NULL;
997 guint bufi, i, offset, priv_data_size;
999 if (streamheader == NULL)
1000 goto no_stream_headers;
1002 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1005 bufarr = g_value_peek_pointer (streamheader);
1006 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1008 if (N > 0 && bufarr->len != N)
1011 context->xiph_headers_to_skip = bufarr->len;
1013 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1014 for (i = 0; i < bufarr->len; i++) {
1015 GValue *bufval = &g_array_index (bufarr, GValue, i);
1017 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1019 goto wrong_content_type;
1022 buf[i] = g_value_peek_pointer (bufval);
1026 if (bufarr->len > 0) {
1027 for (i = 0; i < bufarr->len - 1; i++) {
1028 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1032 for (i = 0; i < bufarr->len; ++i) {
1033 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1036 priv_data = g_malloc0 (priv_data_size);
1038 priv_data[0] = bufarr->len - 1;
1041 if (bufarr->len > 0) {
1042 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1043 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1044 priv_data[offset++] = 0xff;
1046 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1050 for (i = 0; i < bufarr->len; ++i) {
1051 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1052 GST_BUFFER_SIZE (buf[i]));
1053 offset += GST_BUFFER_SIZE (buf[i]);
1056 context->codec_priv = priv_data;
1057 context->codec_priv_size = priv_data_size;
1060 *p_buf0 = gst_buffer_ref (buf[0]);
1069 GST_WARNING ("required streamheaders missing in sink caps!");
1074 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1075 G_VALUE_TYPE_NAME (streamheader));
1080 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1085 GST_WARNING ("streamheaders array does not contain GstBuffers");
1091 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1092 GstMatroskaTrackContext * context)
1094 GstBuffer *buf0 = NULL;
1096 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1099 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1100 GST_WARNING ("First vorbis header too small, ignoring");
1102 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1103 GstMatroskaTrackAudioContext *audiocontext;
1106 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1107 audiocontext = (GstMatroskaTrackAudioContext *) context;
1108 audiocontext->channels = GST_READ_UINT8 (hdr);
1109 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1114 gst_buffer_unref (buf0);
1120 theora_streamheader_to_codecdata (const GValue * streamheader,
1121 GstMatroskaTrackContext * context)
1123 GstBuffer *buf0 = NULL;
1125 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1128 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1129 GST_WARNING ("First theora header too small, ignoring");
1130 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1131 GST_WARNING ("First header not a theora identification header, ignoring");
1133 GstMatroskaTrackVideoContext *videocontext;
1134 guint fps_num, fps_denom, par_num, par_denom;
1137 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1139 videocontext = (GstMatroskaTrackVideoContext *) context;
1140 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1141 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1142 hdr += 3 + 3 + 1 + 1;
1143 fps_num = GST_READ_UINT32_BE (hdr);
1144 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1145 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1146 fps_denom, fps_num);
1148 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1149 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1150 if (par_num > 0 && par_num > 0) {
1151 if (par_num > par_denom) {
1152 videocontext->display_width =
1153 videocontext->pixel_width * par_num / par_denom;
1154 videocontext->display_height = videocontext->pixel_height;
1155 } else if (par_num < par_denom) {
1156 videocontext->display_width = videocontext->pixel_width;
1157 videocontext->display_height =
1158 videocontext->pixel_height * par_denom / par_num;
1160 videocontext->display_width = 0;
1161 videocontext->display_height = 0;
1164 videocontext->display_width = 0;
1165 videocontext->display_height = 0;
1171 gst_buffer_unref (buf0);
1177 kate_streamheader_to_codecdata (const GValue * streamheader,
1178 GstMatroskaTrackContext * context)
1180 GstBuffer *buf0 = NULL;
1182 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1185 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1186 GST_WARNING ("First kate header too small, ignoring");
1187 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1188 GST_WARNING ("First header not a kate identification header, ignoring");
1192 gst_buffer_unref (buf0);
1198 flac_streamheader_to_codecdata (const GValue * streamheader,
1199 GstMatroskaTrackContext * context)
1206 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1207 GST_WARNING ("No or invalid streamheader field in the caps");
1211 bufarr = g_value_peek_pointer (streamheader);
1212 if (bufarr->len < 2) {
1213 GST_WARNING ("Too few headers in streamheader field");
1217 context->xiph_headers_to_skip = bufarr->len + 1;
1219 bufval = &g_array_index (bufarr, GValue, 0);
1220 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1221 GST_WARNING ("streamheaders array does not contain GstBuffers");
1225 buffer = g_value_peek_pointer (bufval);
1227 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1228 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1229 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1230 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1231 GST_WARNING ("Invalid streamheader for FLAC");
1235 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1236 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1237 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1238 GST_BUFFER_SIZE (buffer) - 9);
1240 for (i = 1; i < bufarr->len; i++) {
1241 bufval = &g_array_index (bufarr, GValue, i);
1243 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1244 g_free (context->codec_priv);
1245 context->codec_priv = NULL;
1246 context->codec_priv_size = 0;
1247 GST_WARNING ("streamheaders array does not contain GstBuffers");
1251 buffer = g_value_peek_pointer (bufval);
1253 context->codec_priv =
1254 g_realloc (context->codec_priv,
1255 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1256 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1257 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1258 context->codec_priv_size =
1259 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1266 speex_streamheader_to_codecdata (const GValue * streamheader,
1267 GstMatroskaTrackContext * context)
1273 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1274 GST_WARNING ("No or invalid streamheader field in the caps");
1278 bufarr = g_value_peek_pointer (streamheader);
1279 if (bufarr->len != 2) {
1280 GST_WARNING ("Too few headers in streamheader field");
1284 context->xiph_headers_to_skip = bufarr->len + 1;
1286 bufval = &g_array_index (bufarr, GValue, 0);
1287 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1288 GST_WARNING ("streamheaders array does not contain GstBuffers");
1292 buffer = g_value_peek_pointer (bufval);
1294 if (GST_BUFFER_SIZE (buffer) < 80
1295 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1296 GST_WARNING ("Invalid streamheader for Speex");
1300 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1301 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1302 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1303 GST_BUFFER_SIZE (buffer));
1305 bufval = &g_array_index (bufarr, GValue, 1);
1307 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1308 g_free (context->codec_priv);
1309 context->codec_priv = NULL;
1310 context->codec_priv_size = 0;
1311 GST_WARNING ("streamheaders array does not contain GstBuffers");
1315 buffer = g_value_peek_pointer (bufval);
1317 context->codec_priv =
1318 g_realloc (context->codec_priv,
1319 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1320 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1321 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1322 context->codec_priv_size =
1323 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1328 static const gchar *
1329 aac_codec_data_to_codec_id (const GstBuffer * buf)
1331 const gchar *result;
1334 /* default to MAIN */
1337 if (GST_BUFFER_SIZE (buf) >= 2) {
1338 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1356 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1365 * gst_matroska_mux_audio_pad_setcaps:
1366 * @pad: Pad which got the caps.
1369 * Setcaps function for audio sink pad.
1371 * Returns: #TRUE on success.
1374 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1376 GstMatroskaTrackContext *context = NULL;
1377 GstMatroskaTrackAudioContext *audiocontext;
1378 GstMatroskaMux *mux;
1379 GstMatroskaPad *collect_pad;
1380 const gchar *mimetype;
1381 gint samplerate = 0, channels = 0;
1382 GstStructure *structure;
1383 const GValue *codec_data = NULL;
1384 const GstBuffer *buf = NULL;
1385 const gchar *stream_format = NULL;
1387 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1390 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1391 g_assert (collect_pad);
1392 context = collect_pad->track;
1394 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1395 audiocontext = (GstMatroskaTrackAudioContext *) context;
1397 structure = gst_caps_get_structure (caps, 0);
1398 mimetype = gst_structure_get_name (structure);
1401 gst_structure_get_int (structure, "rate", &samplerate);
1402 gst_structure_get_int (structure, "channels", &channels);
1404 audiocontext->samplerate = samplerate;
1405 audiocontext->channels = channels;
1406 audiocontext->bitdepth = 0;
1407 context->default_duration = 0;
1409 codec_data = gst_structure_get_value (structure, "codec_data");
1411 buf = gst_value_get_buffer (codec_data);
1413 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1414 * data and other settings
1418 if (!strcmp (mimetype, "audio/mpeg")) {
1419 gint mpegversion = 0;
1421 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1422 switch (mpegversion) {
1428 gst_structure_get_int (structure, "layer", &layer);
1430 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1431 GST_WARNING_OBJECT (mux,
1432 "Unable to determine MPEG audio version, assuming 1");
1438 else if (layer == 2)
1440 else if (version == 2)
1445 context->default_duration =
1446 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1450 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1453 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1456 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1465 stream_format = gst_structure_get_string (structure, "stream-format");
1466 /* check this is raw aac */
1467 if (stream_format) {
1468 if (strcmp (stream_format, "raw") != 0) {
1469 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1473 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1478 if (mpegversion == 2)
1480 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1481 aac_codec_data_to_codec_id (buf));
1482 else if (mpegversion == 4)
1484 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1485 aac_codec_data_to_codec_id (buf));
1487 g_assert_not_reached ();
1489 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1496 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1498 gint endianness = G_LITTLE_ENDIAN;
1499 gboolean signedness = TRUE;
1501 if (!gst_structure_get_int (structure, "width", &width) ||
1502 !gst_structure_get_int (structure, "depth", &depth) ||
1503 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1504 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1509 !gst_structure_get_int (structure, "endianness", &endianness)) {
1510 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1514 if (width != depth) {
1515 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1519 /* FIXME: where is this spec'ed out? (tpm) */
1520 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1521 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1525 audiocontext->bitdepth = depth;
1526 if (endianness == G_BIG_ENDIAN)
1527 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1529 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1531 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1534 if (!gst_structure_get_int (structure, "width", &width)) {
1535 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1539 audiocontext->bitdepth = width;
1540 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1542 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1543 const GValue *streamheader;
1545 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1547 if (context->codec_priv != NULL) {
1548 g_free (context->codec_priv);
1549 context->codec_priv = NULL;
1550 context->codec_priv_size = 0;
1553 streamheader = gst_structure_get_value (structure, "streamheader");
1554 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1555 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1556 ("vorbis stream headers missing or malformed"));
1559 } else if (!strcmp (mimetype, "audio/x-flac")) {
1560 const GValue *streamheader;
1562 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1563 if (context->codec_priv != NULL) {
1564 g_free (context->codec_priv);
1565 context->codec_priv = NULL;
1566 context->codec_priv_size = 0;
1569 streamheader = gst_structure_get_value (structure, "streamheader");
1570 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1571 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1572 ("flac stream headers missing or malformed"));
1575 } else if (!strcmp (mimetype, "audio/x-speex")) {
1576 const GValue *streamheader;
1578 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1579 if (context->codec_priv != NULL) {
1580 g_free (context->codec_priv);
1581 context->codec_priv = NULL;
1582 context->codec_priv_size = 0;
1585 streamheader = gst_structure_get_value (structure, "streamheader");
1586 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1587 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1588 ("speex stream headers missing or malformed"));
1591 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1592 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1593 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1594 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1595 } else if (!strcmp (mimetype, "audio/x-dts")) {
1596 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1597 } else if (!strcmp (mimetype, "audio/x-tta")) {
1600 /* TTA frame duration */
1601 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1603 gst_structure_get_int (structure, "width", &width);
1604 audiocontext->bitdepth = width;
1605 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1607 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1609 const GValue *mdpr_data;
1611 gst_structure_get_int (structure, "raversion", &raversion);
1612 switch (raversion) {
1614 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1617 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1620 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1626 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1627 if (mdpr_data != NULL) {
1628 guint8 *priv_data = NULL;
1629 guint priv_data_size = 0;
1631 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1633 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1634 priv_data = g_malloc0 (priv_data_size);
1636 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1638 context->codec_priv = priv_data;
1639 context->codec_priv_size = priv_data_size;
1642 } else if (!strcmp (mimetype, "audio/x-wma")
1643 || !strcmp (mimetype, "audio/x-alaw")
1644 || !strcmp (mimetype, "audio/x-mulaw")) {
1646 guint codec_priv_size;
1651 if (samplerate == 0 || channels == 0) {
1652 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1656 if (!strcmp (mimetype, "audio/x-wma")) {
1660 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1661 || !gst_structure_get_int (structure, "block_align", &block_align)
1662 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1663 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1668 switch (wmaversion) {
1670 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1673 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1676 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1679 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1683 if (gst_structure_get_int (structure, "depth", &depth))
1684 audiocontext->bitdepth = depth;
1685 } else if (!strcmp (mimetype, "audio/x-alaw")
1686 || !strcmp (mimetype, "audio/x-mulaw")) {
1687 audiocontext->bitdepth = 8;
1688 if (!strcmp (mimetype, "audio/x-alaw"))
1689 format = GST_RIFF_WAVE_FORMAT_ALAW;
1691 format = GST_RIFF_WAVE_FORMAT_MULAW;
1693 block_align = channels;
1694 bitrate = block_align * samplerate;
1696 g_assert (format != 0);
1698 codec_priv_size = WAVEFORMATEX_SIZE;
1700 codec_priv_size += GST_BUFFER_SIZE (buf);
1702 /* serialize waveformatex structure */
1703 codec_priv = g_malloc0 (codec_priv_size);
1704 GST_WRITE_UINT16_LE (codec_priv, format);
1705 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1706 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1707 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1708 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1709 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1711 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1713 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1715 /* process codec private/initialization data, if any */
1717 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1718 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1721 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1722 context->codec_priv = (gpointer) codec_priv;
1723 context->codec_priv_size = codec_priv_size;
1731 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1732 GST_PAD_NAME (pad), caps);
1739 * gst_matroska_mux_subtitle_pad_setcaps:
1740 * @pad: Pad which got the caps.
1743 * Setcaps function for subtitle sink pad.
1745 * Returns: #TRUE on success.
1748 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1751 * Consider this as boilerplate code for now. There is
1752 * no single subtitle creation element in GStreamer,
1753 * neither do I know how subtitling works at all. */
1755 /* There is now (at least) one such alement (kateenc), and I'm going
1756 to handle it here and claim it works when it can be piped back
1757 through GStreamer and VLC */
1759 GstMatroskaTrackContext *context = NULL;
1760 GstMatroskaTrackSubtitleContext *scontext;
1761 GstMatroskaMux *mux;
1762 GstMatroskaPad *collect_pad;
1763 const gchar *mimetype;
1764 GstStructure *structure;
1766 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1769 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1770 g_assert (collect_pad);
1771 context = collect_pad->track;
1773 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1774 scontext = (GstMatroskaTrackSubtitleContext *) context;
1776 structure = gst_caps_get_structure (caps, 0);
1777 mimetype = gst_structure_get_name (structure);
1780 scontext->check_utf8 = 1;
1781 scontext->invalid_utf8 = 0;
1782 context->default_duration = 0;
1784 /* TODO: - other format than Kate */
1786 if (!strcmp (mimetype, "subtitle/x-kate")) {
1787 const GValue *streamheader;
1789 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1791 if (context->codec_priv != NULL) {
1792 g_free (context->codec_priv);
1793 context->codec_priv = NULL;
1794 context->codec_priv_size = 0;
1797 streamheader = gst_structure_get_value (structure, "streamheader");
1798 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1799 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1800 ("kate stream headers missing or malformed"));
1811 * gst_matroska_mux_request_new_pad:
1812 * @element: #GstMatroskaMux.
1813 * @templ: #GstPadTemplate.
1814 * @pad_name: New pad name.
1816 * Request pad function for sink templates.
1818 * Returns: New #GstPad.
1821 gst_matroska_mux_request_new_pad (GstElement * element,
1822 GstPadTemplate * templ, const gchar * req_name)
1824 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1825 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1826 GstMatroskaPad *collect_pad;
1827 GstPad *newpad = NULL;
1829 const gchar *pad_name = NULL;
1830 GstPadSetCapsFunction setcapsfunc = NULL;
1831 GstMatroskaTrackContext *context = NULL;
1834 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1835 /* don't mix named and unnamed pads, if the pad already exists we fail when
1836 * trying to add it */
1837 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1838 pad_name = req_name;
1840 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1843 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1844 context = (GstMatroskaTrackContext *)
1845 g_new0 (GstMatroskaTrackAudioContext, 1);
1846 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1847 context->name = g_strdup ("Audio");
1848 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1849 /* don't mix named and unnamed pads, if the pad already exists we fail when
1850 * trying to add it */
1851 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1852 pad_name = req_name;
1854 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1857 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1858 context = (GstMatroskaTrackContext *)
1859 g_new0 (GstMatroskaTrackVideoContext, 1);
1860 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1861 context->name = g_strdup ("Video");
1862 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1863 /* don't mix named and unnamed pads, if the pad already exists we fail when
1864 * trying to add it */
1865 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1866 pad_name = req_name;
1868 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1871 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1872 context = (GstMatroskaTrackContext *)
1873 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1874 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1875 context->name = g_strdup ("Subtitle");
1877 GST_WARNING_OBJECT (mux, "This is not our template!");
1881 newpad = gst_pad_new_from_template (templ, pad_name);
1883 collect_pad = (GstMatroskaPad *)
1884 gst_collect_pads_add_pad_full (mux->collect, newpad,
1885 sizeof (GstMatroskaPad),
1886 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1888 collect_pad->track = context;
1889 gst_matroska_pad_reset (collect_pad, FALSE);
1891 /* FIXME: hacked way to override/extend the event function of
1892 * GstCollectPads; because it sets its own event function giving the
1893 * element no access to events.
1894 * TODO GstCollectPads should really give its 'users' a clean chance to
1895 * properly handle events that are not meant for collectpads itself.
1896 * Perhaps a callback or so, though rejected (?) in #340060.
1897 * This would allow (clean) transcoding of info from demuxer/streams
1898 * to another muxer */
1899 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1900 gst_pad_set_event_function (newpad,
1901 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1903 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1904 gst_pad_set_active (newpad, TRUE);
1905 if (!gst_element_add_pad (element, newpad))
1906 goto pad_add_failed;
1910 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1917 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1918 gst_object_unref (newpad);
1924 * gst_matroska_mux_release_pad:
1925 * @element: #GstMatroskaMux.
1926 * @pad: Pad to release.
1928 * Release a previously requested pad.
1931 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1933 GstMatroskaMux *mux;
1936 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1938 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1939 GstCollectData *cdata = (GstCollectData *) walk->data;
1940 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1942 if (cdata->pad == pad) {
1943 GstClockTime min_dur; /* observed minimum duration */
1945 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1946 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1947 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1948 if (collect_pad->duration < min_dur)
1949 collect_pad->duration = min_dur;
1952 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1953 mux->duration < collect_pad->duration)
1954 mux->duration = collect_pad->duration;
1960 gst_collect_pads_remove_pad (mux->collect, pad);
1961 if (gst_element_remove_pad (element, pad))
1967 * gst_matroska_mux_track_header:
1968 * @mux: #GstMatroskaMux
1969 * @context: Tack context.
1971 * Write a track header.
1974 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1975 GstMatroskaTrackContext * context)
1977 GstEbmlWrite *ebml = mux->ebml_write;
1980 /* TODO: check if everything necessary is written and check default values */
1982 /* track type goes before the type-specific stuff */
1983 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1986 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1987 gst_matroska_mux_create_uid ());
1988 if (context->default_duration) {
1989 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1990 context->default_duration);
1992 if (context->language) {
1993 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1997 /* type-specific stuff */
1998 switch (context->type) {
1999 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2000 GstMatroskaTrackVideoContext *videocontext =
2001 (GstMatroskaTrackVideoContext *) context;
2003 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2004 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2005 videocontext->pixel_width);
2006 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2007 videocontext->pixel_height);
2008 if (videocontext->display_width && videocontext->display_height) {
2009 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2010 videocontext->display_width);
2011 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2012 videocontext->display_height);
2014 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2015 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2016 if (videocontext->fourcc) {
2017 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2019 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2020 (gpointer) & fcc_le, 4);
2022 gst_ebml_write_master_finish (ebml, master);
2027 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2028 GstMatroskaTrackAudioContext *audiocontext =
2029 (GstMatroskaTrackAudioContext *) context;
2031 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2032 if (audiocontext->samplerate != 8000)
2033 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2034 audiocontext->samplerate);
2035 if (audiocontext->channels != 1)
2036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2037 audiocontext->channels);
2038 if (audiocontext->bitdepth) {
2039 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2040 audiocontext->bitdepth);
2042 gst_ebml_write_master_finish (ebml, master);
2048 /* doesn't need type-specific data */
2052 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2053 if (context->codec_priv)
2054 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2055 context->codec_priv, context->codec_priv_size);
2056 /* FIXME: until we have a nice way of getting the codecname
2057 * out of the caps, I'm not going to enable this. Too much
2058 * (useless, double, boring) work... */
2059 /* TODO: Use value from tags if any */
2060 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2061 context->codec_name); */
2062 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2067 * gst_matroska_mux_start:
2068 * @mux: #GstMatroskaMux
2070 * Start a new matroska file (write headers etc...)
2073 gst_matroska_mux_start (GstMatroskaMux * mux)
2075 GstEbmlWrite *ebml = mux->ebml_write;
2076 const gchar *doctype;
2077 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2078 GST_MATROSKA_ID_TRACKS,
2079 GST_MATROSKA_ID_CUES,
2080 GST_MATROSKA_ID_TAGS,
2083 guint64 master, child;
2087 GstClockTime duration = 0;
2088 guint32 segment_uid[4];
2089 GTimeVal time = { 0, 0 };
2091 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2092 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2094 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2096 /* we start with a EBML header */
2097 doctype = mux->doctype;
2098 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2099 doctype, mux->doctype_version);
2100 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2102 /* the rest of the header is cached */
2103 gst_ebml_write_set_cache (ebml, 0x1000);
2105 /* start a segment */
2107 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2108 mux->segment_master = ebml->pos;
2110 if (!mux->streamable) {
2111 /* seekhead (table of contents) - we set the positions later */
2112 mux->seekhead_pos = ebml->pos;
2113 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2114 for (i = 0; seekhead_id[i] != 0; i++) {
2115 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2116 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2117 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2118 gst_ebml_write_master_finish (ebml, child);
2120 gst_ebml_write_master_finish (ebml, master);
2123 if (mux->streamable) {
2124 const GstTagList *tags;
2127 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2129 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2130 guint64 master_tags, master_tag;
2132 GST_DEBUG ("Writing tags");
2134 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2135 mux->tags_pos = ebml->pos;
2136 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2137 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2138 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2139 gst_ebml_write_master_finish (ebml, master_tag);
2140 gst_ebml_write_master_finish (ebml, master_tags);
2145 mux->info_pos = ebml->pos;
2146 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2147 for (i = 0; i < 4; i++) {
2148 segment_uid[i] = g_random_int ();
2150 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2151 (guint8 *) segment_uid, 16);
2152 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2153 mux->duration_pos = ebml->pos;
2155 if (!mux->streamable) {
2156 for (collected = mux->collect->data; collected;
2157 collected = g_slist_next (collected)) {
2158 GstMatroskaPad *collect_pad;
2159 GstFormat format = GST_FORMAT_TIME;
2161 gint64 trackduration;
2163 collect_pad = (GstMatroskaPad *) collected->data;
2164 thepad = collect_pad->collect.pad;
2166 /* Query the total length of the track. */
2167 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2168 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2169 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2170 GST_TIME_ARGS (trackduration));
2171 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2172 duration = (GstClockTime) trackduration;
2176 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2177 gst_guint64_to_gdouble (duration) /
2178 gst_guint64_to_gdouble (mux->time_scale));
2180 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2181 "GStreamer plugin version " PACKAGE_VERSION);
2182 if (mux->writing_app && mux->writing_app[0]) {
2183 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2185 g_get_current_time (&time);
2186 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2187 gst_ebml_write_master_finish (ebml, master);
2190 mux->tracks_pos = ebml->pos;
2191 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2193 for (collected = mux->collect->data; collected;
2194 collected = g_slist_next (collected)) {
2195 GstMatroskaPad *collect_pad;
2198 collect_pad = (GstMatroskaPad *) collected->data;
2199 thepad = collect_pad->collect.pad;
2201 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2202 collect_pad->track->codec_id != 0) {
2203 collect_pad->track->num = tracknum++;
2204 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2205 gst_matroska_mux_track_header (mux, collect_pad->track);
2206 gst_ebml_write_master_finish (ebml, child);
2207 /* some remaing pad/track setup */
2208 collect_pad->default_duration_scaled =
2209 gst_util_uint64_scale (collect_pad->track->default_duration,
2210 1, mux->time_scale);
2213 gst_ebml_write_master_finish (ebml, master);
2215 /* lastly, flush the cache */
2216 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2220 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2223 /* TODO: more sensible tag mappings */
2226 const gchar *matroska_tagname;
2227 const gchar *gstreamer_tagname;
2231 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2232 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2233 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2234 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2235 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2236 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2237 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2238 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2239 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2240 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2241 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2242 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2243 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2244 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2245 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2247 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2249 guint64 simpletag_master;
2251 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2252 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2253 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2255 if (strcmp (tagname_gst, tag) == 0) {
2256 GValue src = { 0, };
2259 if (!gst_tag_list_copy_value (&src, list, tag))
2261 if ((dest = gst_value_serialize (&src))) {
2263 simpletag_master = gst_ebml_write_master_start (ebml,
2264 GST_MATROSKA_ID_SIMPLETAG);
2265 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2266 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2267 gst_ebml_write_master_finish (ebml, simpletag_master);
2270 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2272 g_value_unset (&src);
2280 * gst_matroska_mux_finish:
2281 * @mux: #GstMatroskaMux
2283 * Finish a new matroska file (write index etc...)
2286 gst_matroska_mux_finish (GstMatroskaMux * mux)
2288 GstEbmlWrite *ebml = mux->ebml_write;
2290 guint64 duration = 0;
2292 const GstTagList *tags;
2294 /* finish last cluster */
2296 gst_ebml_write_master_finish (ebml, mux->cluster);
2300 if (mux->index != NULL) {
2302 guint64 master, pointentry_master, trackpos_master;
2304 mux->cues_pos = ebml->pos;
2305 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2306 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2308 for (n = 0; n < mux->num_indexes; n++) {
2309 GstMatroskaIndex *idx = &mux->index[n];
2311 pointentry_master = gst_ebml_write_master_start (ebml,
2312 GST_MATROSKA_ID_POINTENTRY);
2313 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2314 idx->time / mux->time_scale);
2315 trackpos_master = gst_ebml_write_master_start (ebml,
2316 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2317 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2318 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2319 idx->pos - mux->segment_master);
2320 gst_ebml_write_master_finish (ebml, trackpos_master);
2321 gst_ebml_write_master_finish (ebml, pointentry_master);
2324 gst_ebml_write_master_finish (ebml, master);
2325 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2329 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2331 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2332 guint64 master_tags, master_tag;
2334 GST_DEBUG ("Writing tags");
2336 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2337 mux->tags_pos = ebml->pos;
2338 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2339 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2340 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2341 gst_ebml_write_master_finish (ebml, master_tag);
2342 gst_ebml_write_master_finish (ebml, master_tags);
2345 /* update seekhead. We know that:
2346 * - a seekhead contains 4 entries.
2347 * - order of entries is as above.
2348 * - a seekhead has a 4-byte header + 8-byte length
2349 * - each entry is 2-byte master, 2-byte ID pointer,
2350 * 2-byte length pointer, all 8/1-byte length, 4-
2351 * byte ID and 8-byte length pointer, where the
2352 * length pointer starts at 20.
2353 * - all entries are local to the segment (so pos - segment_master).
2354 * - so each entry is at 12 + 20 + num * 28. */
2355 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2356 mux->info_pos - mux->segment_master);
2357 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2358 mux->tracks_pos - mux->segment_master);
2359 if (mux->index != NULL) {
2360 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2361 mux->cues_pos - mux->segment_master);
2364 guint64 my_pos = ebml->pos;
2366 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2367 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2368 gst_ebml_write_seek (ebml, my_pos);
2371 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2372 mux->tags_pos - mux->segment_master);
2375 guint64 my_pos = ebml->pos;
2377 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2378 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2379 gst_ebml_write_seek (ebml, my_pos);
2382 /* update duration */
2383 /* first get the overall duration */
2384 /* a released track may have left a duration in here */
2385 duration = mux->duration;
2386 for (collected = mux->collect->data; collected;
2387 collected = g_slist_next (collected)) {
2388 GstMatroskaPad *collect_pad;
2389 GstClockTime min_duration; /* observed minimum duration */
2391 collect_pad = (GstMatroskaPad *) collected->data;
2393 GST_DEBUG_OBJECT (mux,
2394 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2395 " end ts %" GST_TIME_FORMAT, collect_pad,
2396 GST_TIME_ARGS (collect_pad->start_ts),
2397 GST_TIME_ARGS (collect_pad->end_ts));
2399 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2400 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2402 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2403 if (collect_pad->duration < min_duration)
2404 collect_pad->duration = min_duration;
2405 GST_DEBUG_OBJECT (collect_pad,
2406 "final track duration: %" GST_TIME_FORMAT,
2407 GST_TIME_ARGS (collect_pad->duration));
2410 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2411 duration < collect_pad->duration)
2412 duration = collect_pad->duration;
2414 if (duration != 0) {
2415 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2416 GST_TIME_ARGS (duration));
2417 pos = mux->ebml_write->pos;
2418 gst_ebml_write_seek (ebml, mux->duration_pos);
2419 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2420 gst_guint64_to_gdouble (duration) /
2421 gst_guint64_to_gdouble (mux->time_scale));
2422 gst_ebml_write_seek (ebml, pos);
2425 guint64 my_pos = ebml->pos;
2427 gst_ebml_write_seek (ebml, mux->duration_pos);
2428 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2429 gst_ebml_write_seek (ebml, my_pos);
2431 GST_DEBUG_OBJECT (mux, "finishing segment");
2432 /* finish segment - this also writes element length */
2433 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2438 * gst_matroska_mux_best_pad:
2439 * @mux: #GstMatroskaMux
2440 * @popped: True if at least one buffer was popped from #GstCollectPads
2442 * Find a pad with the oldest data
2443 * (data from this pad should be written first).
2445 * Returns: Selected pad.
2447 static GstMatroskaPad *
2448 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2451 GstMatroskaPad *best = NULL;
2454 for (collected = mux->collect->data; collected;
2455 collected = g_slist_next (collected)) {
2456 GstMatroskaPad *collect_pad;
2458 collect_pad = (GstMatroskaPad *) collected->data;
2459 /* fetch a new buffer if needed */
2460 if (collect_pad->buffer == NULL) {
2461 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2462 (GstCollectData *) collect_pad);
2464 if (collect_pad->buffer != NULL) {
2468 /* convert to running time */
2469 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2470 /* invalid should pass */
2471 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2472 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2473 GST_FORMAT_TIME, time);
2474 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2475 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2476 GST_PAD_NAME (collect_pad->collect.pad));
2477 gst_buffer_unref (collect_pad->buffer);
2478 collect_pad->buffer = NULL;
2481 collect_pad->buffer =
2482 gst_buffer_make_metadata_writable (collect_pad->buffer);
2483 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2489 /* if we have a buffer check if it is better then the current best one */
2490 if (collect_pad->buffer != NULL) {
2491 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2492 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2493 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2494 GST_BUFFER_TIMESTAMP (best->buffer))) {
2504 * gst_matroska_mux_buffer_header:
2505 * @track: Track context.
2506 * @relative_timestamp: relative timestamp of the buffer
2507 * @flags: Buffer flags.
2509 * Create a buffer containing buffer header.
2511 * Returns: New buffer.
2514 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2515 gint16 relative_timestamp, int flags)
2519 hdr = gst_buffer_new_and_alloc (4);
2520 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2521 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2522 /* time relative to clustertime */
2523 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2526 GST_BUFFER_DATA (hdr)[3] = flags;
2531 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2532 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2533 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2536 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2537 GstMatroskaPad * collect_pad, GstBuffer * buf)
2539 GstMatroskaTrackVideoContext *ctx =
2540 (GstMatroskaTrackVideoContext *) collect_pad->track;
2541 const guint8 *data = GST_BUFFER_DATA (buf);
2542 guint size = GST_BUFFER_SIZE (buf);
2544 guint32 next_parse_offset;
2545 GstBuffer *ret = NULL;
2546 gboolean is_muxing_unit = FALSE;
2548 if (GST_BUFFER_SIZE (buf) < 13) {
2549 gst_buffer_unref (buf);
2553 /* Check if this buffer contains a picture or end-of-sequence packet */
2554 while (size >= 13) {
2555 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2556 gst_buffer_unref (buf);
2560 parse_code = GST_READ_UINT8 (data + 4);
2561 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2562 if (ctx->dirac_unit) {
2563 gst_buffer_unref (ctx->dirac_unit);
2564 ctx->dirac_unit = NULL;
2566 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2567 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2568 is_muxing_unit = TRUE;
2572 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2574 if (G_UNLIKELY (next_parse_offset == 0))
2577 data += next_parse_offset;
2578 size -= next_parse_offset;
2581 if (ctx->dirac_unit)
2582 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2584 ctx->dirac_unit = gst_buffer_ref (buf);
2586 if (is_muxing_unit) {
2587 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2588 ctx->dirac_unit = NULL;
2589 gst_buffer_copy_metadata (ret, buf,
2590 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2591 GST_BUFFER_COPY_CAPS);
2592 gst_buffer_unref (buf);
2594 gst_buffer_unref (buf);
2602 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2606 GValue streamheader = { 0 };
2607 GValue bufval = { 0 };
2608 GstBuffer *streamheader_buffer;
2609 GstEbmlWrite *ebml = mux->ebml_write;
2611 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2612 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2613 caps = gst_caps_new_simple ("video/webm", NULL);
2615 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2617 s = gst_caps_get_structure (caps, 0);
2618 g_value_init (&streamheader, GST_TYPE_ARRAY);
2619 g_value_init (&bufval, GST_TYPE_BUFFER);
2620 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2621 gst_value_set_buffer (&bufval, streamheader_buffer);
2622 gst_value_array_append_value (&streamheader, &bufval);
2623 g_value_unset (&bufval);
2624 gst_structure_set_value (s, "streamheader", &streamheader);
2625 g_value_unset (&streamheader);
2626 gst_caps_replace (&ebml->caps, caps);
2627 gst_buffer_unref (streamheader_buffer);
2628 gst_caps_unref (caps);
2632 * gst_matroska_mux_write_data:
2633 * @mux: #GstMatroskaMux
2634 * @collect_pad: #GstMatroskaPad with the data
2636 * Write collected data (called from gst_matroska_mux_collected).
2638 * Returns: Result of the gst_pad_push issued to write the data.
2640 static GstFlowReturn
2641 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2643 GstEbmlWrite *ebml = mux->ebml_write;
2644 GstBuffer *buf, *hdr;
2646 gboolean write_duration;
2647 gint16 relative_timestamp;
2648 gint64 relative_timestamp64;
2649 guint64 block_duration;
2650 gboolean is_video_keyframe = FALSE;
2653 buf = collect_pad->buffer;
2654 collect_pad->buffer = NULL;
2656 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2657 if (collect_pad->track->xiph_headers_to_skip > 0) {
2658 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2659 gst_buffer_unref (buf);
2660 --collect_pad->track->xiph_headers_to_skip;
2664 /* for dirac we have to queue up everything up to a picture unit */
2665 if (collect_pad->track->codec_id != NULL &&
2666 strcmp (collect_pad->track->codec_id,
2667 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2668 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2673 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2674 * this would wreak havoc with time stored in matroska file */
2675 /* TODO: maybe calculate a timestamp by using the previous timestamp
2676 * and default duration */
2677 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2678 GST_WARNING_OBJECT (collect_pad->collect.pad,
2679 "Invalid buffer timestamp; dropping buffer");
2680 gst_buffer_unref (buf);
2684 /* set the timestamp for outgoing buffers */
2685 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2687 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2688 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2689 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2690 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2691 is_video_keyframe = TRUE;
2695 /* start a new cluster at every keyframe or when we may be reaching the
2696 * limit of the relative timestamp */
2697 if (mux->cluster_time +
2698 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2699 || is_video_keyframe) {
2700 if (!mux->streamable)
2701 gst_ebml_write_master_finish (ebml, mux->cluster);
2702 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2703 mux->cluster_pos = ebml->pos;
2704 gst_ebml_write_set_cache (ebml, 0x20);
2706 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2707 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2708 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2710 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2711 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2713 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2714 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2715 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2716 mux->prev_cluster_size);
2721 mux->cluster_pos = ebml->pos;
2722 gst_ebml_write_set_cache (ebml, 0x20);
2723 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2724 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2725 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2726 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2727 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2730 /* update duration of this track */
2731 if (GST_BUFFER_DURATION_IS_VALID (buf))
2732 collect_pad->duration += GST_BUFFER_DURATION (buf);
2734 /* We currently write index entries for all video tracks or for the audio
2735 * track in a single-track audio file. This could be improved by keeping the
2736 * index only for the *first* video track. */
2738 /* TODO: index is useful for every track, should contain the number of
2739 * the block in the cluster which contains the timestamp, should also work
2740 * for files with multiple audio tracks.
2742 if (!mux->streamable &&
2743 (is_video_keyframe ||
2744 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2745 (mux->num_streams == 1)))) {
2748 if (mux->min_index_interval != 0) {
2749 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2750 if (mux->index[last_idx].track == collect_pad->track->num)
2755 if (last_idx < 0 || mux->min_index_interval == 0 ||
2756 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2757 >= mux->min_index_interval)) {
2758 GstMatroskaIndex *idx;
2760 if (mux->num_indexes % 32 == 0) {
2761 mux->index = g_renew (GstMatroskaIndex, mux->index,
2762 mux->num_indexes + 32);
2764 idx = &mux->index[mux->num_indexes++];
2766 idx->pos = mux->cluster_pos;
2767 idx->time = GST_BUFFER_TIMESTAMP (buf);
2768 idx->track = collect_pad->track->num;
2772 /* Check if the duration differs from the default duration. */
2773 write_duration = FALSE;
2775 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2776 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
2777 1, mux->time_scale);
2779 /* small difference should be ok. */
2780 if (block_duration > collect_pad->default_duration_scaled + 1 ||
2781 block_duration < collect_pad->default_duration_scaled - 1) {
2782 write_duration = TRUE;
2786 /* write the block, for doctype v2 use SimpleBlock if possible
2787 * one slice (*breath*).
2788 * FIXME: Need to do correct lacing! */
2789 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2790 if (relative_timestamp64 >= 0) {
2791 /* round the timestamp */
2792 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2794 /* round the timestamp */
2795 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2797 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2799 if (mux->doctype_version > 1 && !write_duration) {
2801 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2804 gst_matroska_mux_create_buffer_header (collect_pad->track,
2805 relative_timestamp, flags);
2806 gst_ebml_write_set_cache (ebml, 0x40);
2807 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2808 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2809 gst_ebml_write_buffer (ebml, hdr);
2810 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2811 gst_ebml_write_buffer (ebml, buf);
2813 return gst_ebml_last_write_result (ebml);
2815 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2816 /* write and call order slightly unnatural,
2817 * but avoids seek and minizes pushing */
2818 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2820 gst_matroska_mux_create_buffer_header (collect_pad->track,
2821 relative_timestamp, 0);
2823 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
2824 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2825 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2826 gst_ebml_write_buffer (ebml, hdr);
2827 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2828 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2829 gst_ebml_write_buffer (ebml, buf);
2831 return gst_ebml_last_write_result (ebml);
2837 * gst_matroska_mux_collected:
2838 * @pads: #GstCollectPads
2839 * @uuser_data: #GstMatroskaMux
2841 * Collectpads callback.
2843 * Returns: #GstFlowReturn
2845 static GstFlowReturn
2846 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2848 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2849 GstEbmlWrite *ebml = mux->ebml_write;
2850 GstMatroskaPad *best;
2852 GstFlowReturn ret = GST_FLOW_OK;
2854 GST_DEBUG_OBJECT (mux, "Collected pads");
2856 /* start with a header */
2857 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2858 if (mux->collect->data == NULL) {
2859 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2860 ("No input streams configured"));
2861 return GST_FLOW_ERROR;
2863 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2864 gst_ebml_start_streamheader (ebml);
2865 gst_matroska_mux_start (mux);
2866 gst_matroska_mux_stop_streamheader (mux);
2867 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2871 /* which stream to write from? */
2872 best = gst_matroska_mux_best_pad (mux, &popped);
2874 /* if there is no best pad, we have reached EOS */
2876 /* buffer popped, but none returned means it was clipped */
2879 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2880 if (!mux->streamable) {
2881 gst_matroska_mux_finish (mux);
2883 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2885 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2886 ret = GST_FLOW_UNEXPECTED;
2889 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2890 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2891 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2892 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2894 /* make note of first and last encountered timestamps, so we can calculate
2895 * the actual duration later when we send an updated header on eos */
2896 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2897 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2898 GstClockTime end_ts = start_ts;
2900 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2901 end_ts += GST_BUFFER_DURATION (best->buffer);
2902 else if (best->track->default_duration)
2903 end_ts += best->track->default_duration;
2905 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2906 best->end_ts = end_ts;
2908 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2909 start_ts < best->start_ts))
2910 best->start_ts = start_ts;
2913 /* write one buffer */
2914 ret = gst_matroska_mux_write_data (mux, best);
2915 } while (ret == GST_FLOW_OK && !popped);
2922 * gst_matroska_mux_change_state:
2923 * @element: #GstMatroskaMux
2924 * @transition: State change transition.
2926 * Change the muxer state.
2928 * Returns: #GstStateChangeReturn
2930 static GstStateChangeReturn
2931 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2933 GstStateChangeReturn ret;
2934 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2936 switch (transition) {
2937 case GST_STATE_CHANGE_NULL_TO_READY:
2939 case GST_STATE_CHANGE_READY_TO_PAUSED:
2940 gst_collect_pads_start (mux->collect);
2942 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2944 case GST_STATE_CHANGE_PAUSED_TO_READY:
2945 gst_collect_pads_stop (mux->collect);
2951 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2953 switch (transition) {
2954 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2956 case GST_STATE_CHANGE_PAUSED_TO_READY:
2957 gst_matroska_mux_reset (GST_ELEMENT (mux));
2959 case GST_STATE_CHANGE_READY_TO_NULL:
2969 gst_matroska_mux_set_property (GObject * object,
2970 guint prop_id, const GValue * value, GParamSpec * pspec)
2972 GstMatroskaMux *mux;
2974 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2975 mux = GST_MATROSKA_MUX (object);
2978 case ARG_WRITING_APP:
2979 if (!g_value_get_string (value)) {
2980 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2983 g_free (mux->writing_app);
2984 mux->writing_app = g_value_dup_string (value);
2986 case ARG_DOCTYPE_VERSION:
2987 mux->doctype_version = g_value_get_int (value);
2989 case ARG_MIN_INDEX_INTERVAL:
2990 mux->min_index_interval = g_value_get_int64 (value);
2992 case ARG_STREAMABLE:
2993 mux->streamable = g_value_get_boolean (value);
2996 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3002 gst_matroska_mux_get_property (GObject * object,
3003 guint prop_id, GValue * value, GParamSpec * pspec)
3005 GstMatroskaMux *mux;
3007 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3008 mux = GST_MATROSKA_MUX (object);
3011 case ARG_WRITING_APP:
3012 g_value_set_string (value, mux->writing_app);
3014 case ARG_DOCTYPE_VERSION:
3015 g_value_set_int (value, mux->doctype_version);
3017 case ARG_MIN_INDEX_INTERVAL:
3018 g_value_set_int64 (value, mux->min_index_interval);
3020 case ARG_STREAMABLE:
3021 g_value_set_boolean (value, mux->streamable);
3024 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);