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 "; "
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 ], "
203 static GstStaticPadTemplate subtitlesink_templ =
204 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
207 GST_STATIC_CAPS ("subtitle/x-kate"));
209 static GArray *used_uids;
210 G_LOCK_DEFINE_STATIC (used_uids);
212 static void gst_matroska_mux_add_interfaces (GType type);
214 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
215 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
217 /* Matroska muxer destructor */
218 static void gst_matroska_mux_finalize (GObject * object);
220 /* Pads collected callback */
222 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
225 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
227 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
228 GstPadTemplate * templ, const gchar * name);
229 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
231 /* gst internal change state handler */
232 static GstStateChangeReturn
233 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
235 /* gobject bla bla */
236 static void gst_matroska_mux_set_property (GObject * object,
237 guint prop_id, const GValue * value, GParamSpec * pspec);
238 static void gst_matroska_mux_get_property (GObject * object,
239 guint prop_id, GValue * value, GParamSpec * pspec);
242 static void gst_matroska_mux_reset (GstElement * element);
245 static guint64 gst_matroska_mux_create_uid ();
247 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
251 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
253 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
254 GstMatroskaTrackContext * context);
255 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
256 GstMatroskaTrackContext * context);
259 gst_matroska_mux_add_interfaces (GType type)
261 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
263 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
267 gst_matroska_mux_base_init (gpointer g_class)
272 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
274 GObjectClass *gobject_class;
275 GstElementClass *gstelement_class;
277 gobject_class = (GObjectClass *) klass;
278 gstelement_class = (GstElementClass *) klass;
280 gst_element_class_add_pad_template (gstelement_class,
281 gst_static_pad_template_get (&videosink_templ));
282 gst_element_class_add_pad_template (gstelement_class,
283 gst_static_pad_template_get (&audiosink_templ));
284 gst_element_class_add_pad_template (gstelement_class,
285 gst_static_pad_template_get (&subtitlesink_templ));
286 gst_element_class_add_pad_template (gstelement_class,
287 gst_static_pad_template_get (&src_templ));
288 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
290 "Muxes video/audio/subtitle streams into a matroska stream",
291 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
293 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
296 gobject_class->finalize = gst_matroska_mux_finalize;
298 gobject_class->get_property = gst_matroska_mux_get_property;
299 gobject_class->set_property = gst_matroska_mux_set_property;
301 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
302 g_param_spec_string ("writing-app", "Writing application.",
303 "The name the application that creates the matroska file.",
304 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
306 g_param_spec_int ("version", "DocType version",
307 "This parameter determines what Matroska features can be used.",
308 1, 2, DEFAULT_DOCTYPE_VERSION,
309 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
311 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
312 "entries", "An index entry is created every so many nanoseconds.",
313 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
314 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
316 g_param_spec_boolean ("streamable", "Determines whether output should "
317 "be streamable", "If set to true, the output should be as if it is "
318 "to be streamed and hence no indexes written or duration written.",
320 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
322 gstelement_class->change_state =
323 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
324 gstelement_class->request_new_pad =
325 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
326 gstelement_class->release_pad =
327 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
332 * gst_matroska_mux_init:
333 * @mux: #GstMatroskaMux that should be initialized.
334 * @g_class: Class of the muxer.
336 * Matroska muxer constructor.
339 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
341 GstPadTemplate *templ;
344 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
345 mux->srcpad = gst_pad_new_from_template (templ, "src");
347 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
348 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
350 mux->collect = gst_collect_pads_new ();
351 gst_collect_pads_set_function (mux->collect,
352 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
355 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
356 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
358 /* property defaults */
359 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
360 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
361 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
362 mux->streamable = DEFAULT_STREAMABLE;
364 /* initialize internal variables */
366 mux->num_streams = 0;
367 mux->num_a_streams = 0;
368 mux->num_t_streams = 0;
369 mux->num_v_streams = 0;
371 /* initialize remaining variables */
372 gst_matroska_mux_reset (GST_ELEMENT (mux));
377 * gst_matroska_mux_finalize:
378 * @object: #GstMatroskaMux that should be finalized.
380 * Finalize matroska muxer.
383 gst_matroska_mux_finalize (GObject * object)
385 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
387 gst_object_unref (mux->collect);
388 gst_object_unref (mux->ebml_write);
389 if (mux->writing_app)
390 g_free (mux->writing_app);
392 G_OBJECT_CLASS (parent_class)->finalize (object);
397 * gst_matroska_mux_create_uid:
399 * Generate new unused track UID.
401 * Returns: New track UID.
404 gst_matroska_mux_create_uid (void)
411 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
416 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
417 for (i = 0; i < used_uids->len; i++) {
418 if (g_array_index (used_uids, guint64, i) == uid) {
423 g_array_append_val (used_uids, uid);
426 G_UNLOCK (used_uids);
432 * gst_matroska_pad_reset:
433 * @collect_pad: the #GstMatroskaPad
435 * Reset and/or release resources of a matroska collect pad.
438 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
441 GstMatroskaTrackType type = 0;
443 /* free track information */
444 if (collect_pad->track != NULL) {
445 /* retrieve for optional later use */
446 name = collect_pad->track->name;
447 type = collect_pad->track->type;
448 /* extra for video */
449 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
450 GstMatroskaTrackVideoContext *ctx =
451 (GstMatroskaTrackVideoContext *) collect_pad->track;
453 if (ctx->dirac_unit) {
454 gst_buffer_unref (ctx->dirac_unit);
455 ctx->dirac_unit = NULL;
458 g_free (collect_pad->track->codec_id);
459 g_free (collect_pad->track->codec_name);
461 g_free (collect_pad->track->name);
462 g_free (collect_pad->track->language);
463 g_free (collect_pad->track->codec_priv);
464 g_free (collect_pad->track);
465 collect_pad->track = NULL;
468 /* free cached buffer */
469 if (collect_pad->buffer != NULL) {
470 gst_buffer_unref (collect_pad->buffer);
471 collect_pad->buffer = NULL;
474 if (!full && type != 0) {
475 GstMatroskaTrackContext *context;
477 /* create a fresh context */
479 case GST_MATROSKA_TRACK_TYPE_VIDEO:
480 context = (GstMatroskaTrackContext *)
481 g_new0 (GstMatroskaTrackVideoContext, 1);
483 case GST_MATROSKA_TRACK_TYPE_AUDIO:
484 context = (GstMatroskaTrackContext *)
485 g_new0 (GstMatroskaTrackAudioContext, 1);
487 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
488 context = (GstMatroskaTrackContext *)
489 g_new0 (GstMatroskaTrackSubtitleContext, 1);
492 g_assert_not_reached ();
496 context->type = type;
497 context->name = name;
498 /* TODO: check default values for the context */
499 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
500 collect_pad->track = context;
501 collect_pad->buffer = NULL;
502 collect_pad->duration = 0;
503 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
504 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
509 * gst_matroska_pad_free:
510 * @collect_pad: the #GstMatroskaPad
512 * Release resources of a matroska collect pad.
515 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
517 gst_matroska_pad_reset (collect_pad, TRUE);
522 * gst_matroska_mux_reset:
523 * @element: #GstMatroskaMux that should be reseted.
525 * Reset matroska muxer back to initial state.
528 gst_matroska_mux_reset (GstElement * element)
530 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
533 /* reset EBML write */
534 gst_ebml_write_reset (mux->ebml_write);
537 mux->state = GST_MATROSKA_MUX_STATE_START;
539 /* clean up existing streams */
541 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
542 GstMatroskaPad *collect_pad;
544 collect_pad = (GstMatroskaPad *) walk->data;
546 /* reset collect pad to pristine state */
547 gst_matroska_pad_reset (collect_pad, FALSE);
551 mux->num_indexes = 0;
556 mux->time_scale = GST_MSECOND;
557 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
562 mux->cluster_time = 0;
563 mux->cluster_pos = 0;
564 mux->prev_cluster_size = 0;
567 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
571 * gst_matroska_mux_handle_src_event:
572 * @pad: Pad which received the event.
573 * @event: Received event.
575 * handle events - copied from oggmux without understanding
577 * Returns: #TRUE on success.
580 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
584 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
588 /* disable seeking for now */
594 return gst_pad_event_default (pad, event);
598 * gst_matroska_mux_handle_sink_event:
599 * @pad: Pad which received the event.
600 * @event: Received event.
602 * handle events - informational ones like tags
604 * Returns: #TRUE on success.
607 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
609 GstMatroskaTrackContext *context;
610 GstMatroskaPad *collect_pad;
615 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
617 switch (GST_EVENT_TYPE (event)) {
621 GST_DEBUG_OBJECT (mux, "received tag event");
622 gst_event_parse_tag (event, &list);
624 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
625 g_assert (collect_pad);
626 context = collect_pad->track;
629 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
630 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
631 const gchar *lang_code;
633 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
635 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
636 context->language = g_strdup (lang_code);
638 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
643 /* FIXME: what about stream-specific tags? */
644 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
645 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
647 gst_event_unref (event);
648 /* handled this, don't want collectpads to forward it downstream */
652 case GST_EVENT_NEWSEGMENT:
653 /* We don't support NEWSEGMENT events */
655 gst_event_unref (event);
662 /* now GstCollectPads can take care of the rest, e.g. EOS */
664 ret = mux->collect_event (pad, event);
666 gst_object_unref (mux);
673 * gst_matroska_mux_video_pad_setcaps:
674 * @pad: Pad which got the caps.
677 * Setcaps function for video sink pad.
679 * Returns: #TRUE on success.
682 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
684 GstMatroskaTrackContext *context = NULL;
685 GstMatroskaTrackVideoContext *videocontext;
687 GstMatroskaPad *collect_pad;
688 GstStructure *structure;
689 const gchar *mimetype;
690 const GValue *value = NULL;
691 const GstBuffer *codec_buf = NULL;
692 gint width, height, pixel_width, pixel_height;
694 gboolean interlaced = FALSE;
696 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
699 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
700 g_assert (collect_pad);
701 context = collect_pad->track;
703 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
704 videocontext = (GstMatroskaTrackVideoContext *) context;
706 /* gst -> matroska ID'ing */
707 structure = gst_caps_get_structure (caps, 0);
709 mimetype = gst_structure_get_name (structure);
711 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
713 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
715 if (!strcmp (mimetype, "video/x-theora")) {
716 /* we'll extract the details later from the theora identification header */
720 /* get general properties */
721 /* spec says it is mandatory */
722 if (!gst_structure_get_int (structure, "width", &width) ||
723 !gst_structure_get_int (structure, "height", &height))
726 videocontext->pixel_width = width;
727 videocontext->pixel_height = height;
728 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
730 context->default_duration =
731 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
732 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
733 GST_TIME_ARGS (context->default_duration));
735 context->default_duration = 0;
737 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
738 &pixel_width, &pixel_height)) {
739 if (pixel_width > pixel_height) {
740 videocontext->display_width = width * pixel_width / pixel_height;
741 videocontext->display_height = height;
742 } else if (pixel_width < pixel_height) {
743 videocontext->display_width = width;
744 videocontext->display_height = height * pixel_height / pixel_width;
746 videocontext->display_width = 0;
747 videocontext->display_height = 0;
750 videocontext->display_width = 0;
751 videocontext->display_height = 0;
756 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
757 videocontext->fourcc = 0;
759 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
760 * data and other settings
764 /* extract codec_data, may turn out needed */
765 value = gst_structure_get_value (structure, "codec_data");
767 codec_buf = gst_value_get_buffer (value);
770 if (!strcmp (mimetype, "video/x-raw-yuv")) {
771 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
772 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
773 } else if (!strcmp (mimetype, "image/jpeg")) {
774 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
775 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
776 ||!strcmp (mimetype, "video/x-huffyuv")
777 || !strcmp (mimetype, "video/x-divx")
778 || !strcmp (mimetype, "video/x-dv")
779 || !strcmp (mimetype, "video/x-h263")
780 || !strcmp (mimetype, "video/x-msmpeg")
781 || !strcmp (mimetype, "video/x-wmv")) {
782 gst_riff_strf_vids *bih;
783 gint size = sizeof (gst_riff_strf_vids);
786 if (!strcmp (mimetype, "video/x-xvid"))
787 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
788 else if (!strcmp (mimetype, "video/x-huffyuv"))
789 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
790 else if (!strcmp (mimetype, "video/x-dv"))
791 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
792 else if (!strcmp (mimetype, "video/x-h263"))
793 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
794 else if (!strcmp (mimetype, "video/x-divx")) {
797 gst_structure_get_int (structure, "divxversion", &divxversion);
798 switch (divxversion) {
800 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
803 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
806 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
809 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
812 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
813 switch (msmpegversion) {
815 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
818 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
824 } else if (!strcmp (mimetype, "video/x-wmv")) {
827 if (gst_structure_get_fourcc (structure, "format", &format)) {
829 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
830 if (wmvversion == 2) {
831 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
832 } else if (wmvversion == 1) {
833 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
834 } else if (wmvversion == 3) {
835 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
843 bih = g_new0 (gst_riff_strf_vids, 1);
844 GST_WRITE_UINT32_LE (&bih->size, size);
845 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
846 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
847 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
848 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
849 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
850 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
851 videocontext->pixel_height * 3);
853 /* process codec private/initialization data, if any */
855 size += GST_BUFFER_SIZE (codec_buf);
856 bih = g_realloc (bih, size);
857 GST_WRITE_UINT32_LE (&bih->size, size);
858 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
859 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
862 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
863 context->codec_priv = (gpointer) bih;
864 context->codec_priv_size = size;
865 } else if (!strcmp (mimetype, "video/x-h264")) {
866 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
868 if (context->codec_priv != NULL) {
869 g_free (context->codec_priv);
870 context->codec_priv = NULL;
871 context->codec_priv_size = 0;
874 /* Create avcC header */
875 if (codec_buf != NULL) {
876 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
877 context->codec_priv = g_malloc0 (context->codec_priv_size);
878 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
879 context->codec_priv_size);
881 } else if (!strcmp (mimetype, "video/x-theora")) {
882 const GValue *streamheader;
884 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
886 if (context->codec_priv != NULL) {
887 g_free (context->codec_priv);
888 context->codec_priv = NULL;
889 context->codec_priv_size = 0;
892 streamheader = gst_structure_get_value (structure, "streamheader");
893 if (!theora_streamheader_to_codecdata (streamheader, context)) {
894 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
895 ("theora stream headers missing or malformed"));
898 } else if (!strcmp (mimetype, "video/x-dirac")) {
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
900 } else if (!strcmp (mimetype, "video/x-vp8")) {
901 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
902 } else if (!strcmp (mimetype, "video/mpeg")) {
905 gst_structure_get_int (structure, "mpegversion", &mpegversion);
906 switch (mpegversion) {
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
914 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
920 /* global headers may be in codec data */
921 if (codec_buf != NULL) {
922 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
923 context->codec_priv = g_malloc0 (context->codec_priv_size);
924 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
925 context->codec_priv_size);
927 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
929 /* can only make it here if preceding case verified it was version 3 */
930 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
931 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
933 const GValue *mdpr_data;
935 gst_structure_get_int (structure, "rmversion", &rmversion);
938 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
941 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
944 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
947 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
953 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
954 if (mdpr_data != NULL) {
955 guint8 *priv_data = NULL;
956 guint priv_data_size = 0;
958 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
960 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
961 priv_data = g_malloc0 (priv_data_size);
963 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
965 context->codec_priv = priv_data;
966 context->codec_priv_size = priv_data_size;
975 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
976 GST_PAD_NAME (pad), caps);
981 /* N > 0 to expect a particular number of headers, negative if the
982 number of headers is variable */
984 xiphN_streamheader_to_codecdata (const GValue * streamheader,
985 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
987 GstBuffer **buf = NULL;
990 guint bufi, i, offset, priv_data_size;
992 if (streamheader == NULL)
993 goto no_stream_headers;
995 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
998 bufarr = g_value_peek_pointer (streamheader);
999 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1001 if (N > 0 && bufarr->len != N)
1004 context->xiph_headers_to_skip = bufarr->len;
1006 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1007 for (i = 0; i < bufarr->len; i++) {
1008 GValue *bufval = &g_array_index (bufarr, GValue, i);
1010 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1012 goto wrong_content_type;
1015 buf[i] = g_value_peek_pointer (bufval);
1019 if (bufarr->len > 0) {
1020 for (i = 0; i < bufarr->len - 1; i++) {
1021 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1025 for (i = 0; i < bufarr->len; ++i) {
1026 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1029 priv_data = g_malloc0 (priv_data_size);
1031 priv_data[0] = bufarr->len - 1;
1034 if (bufarr->len > 0) {
1035 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1036 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1037 priv_data[offset++] = 0xff;
1039 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1043 for (i = 0; i < bufarr->len; ++i) {
1044 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1045 GST_BUFFER_SIZE (buf[i]));
1046 offset += GST_BUFFER_SIZE (buf[i]);
1049 context->codec_priv = priv_data;
1050 context->codec_priv_size = priv_data_size;
1053 *p_buf0 = gst_buffer_ref (buf[0]);
1062 GST_WARNING ("required streamheaders missing in sink caps!");
1067 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1068 G_VALUE_TYPE_NAME (streamheader));
1073 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1078 GST_WARNING ("streamheaders array does not contain GstBuffers");
1084 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1085 GstMatroskaTrackContext * context)
1087 GstBuffer *buf0 = NULL;
1089 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1092 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1093 GST_WARNING ("First vorbis header too small, ignoring");
1095 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1096 GstMatroskaTrackAudioContext *audiocontext;
1099 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1100 audiocontext = (GstMatroskaTrackAudioContext *) context;
1101 audiocontext->channels = GST_READ_UINT8 (hdr);
1102 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1107 gst_buffer_unref (buf0);
1113 theora_streamheader_to_codecdata (const GValue * streamheader,
1114 GstMatroskaTrackContext * context)
1116 GstBuffer *buf0 = NULL;
1118 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1121 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1122 GST_WARNING ("First theora header too small, ignoring");
1123 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1124 GST_WARNING ("First header not a theora identification header, ignoring");
1126 GstMatroskaTrackVideoContext *videocontext;
1127 guint fps_num, fps_denom, par_num, par_denom;
1130 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1132 videocontext = (GstMatroskaTrackVideoContext *) context;
1133 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1134 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1135 hdr += 3 + 3 + 1 + 1;
1136 fps_num = GST_READ_UINT32_BE (hdr);
1137 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1138 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1139 fps_denom, fps_num);
1141 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1142 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1143 if (par_num > 0 && par_num > 0) {
1144 if (par_num > par_denom) {
1145 videocontext->display_width =
1146 videocontext->pixel_width * par_num / par_denom;
1147 videocontext->display_height = videocontext->pixel_height;
1148 } else if (par_num < par_denom) {
1149 videocontext->display_width = videocontext->pixel_width;
1150 videocontext->display_height =
1151 videocontext->pixel_height * par_denom / par_num;
1153 videocontext->display_width = 0;
1154 videocontext->display_height = 0;
1157 videocontext->display_width = 0;
1158 videocontext->display_height = 0;
1164 gst_buffer_unref (buf0);
1170 kate_streamheader_to_codecdata (const GValue * streamheader,
1171 GstMatroskaTrackContext * context)
1173 GstBuffer *buf0 = NULL;
1175 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1178 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1179 GST_WARNING ("First kate header too small, ignoring");
1180 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1181 GST_WARNING ("First header not a kate identification header, ignoring");
1185 gst_buffer_unref (buf0);
1191 flac_streamheader_to_codecdata (const GValue * streamheader,
1192 GstMatroskaTrackContext * context)
1199 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1200 GST_WARNING ("No or invalid streamheader field in the caps");
1204 bufarr = g_value_peek_pointer (streamheader);
1205 if (bufarr->len < 2) {
1206 GST_WARNING ("Too few headers in streamheader field");
1210 context->xiph_headers_to_skip = bufarr->len + 1;
1212 bufval = &g_array_index (bufarr, GValue, 0);
1213 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1214 GST_WARNING ("streamheaders array does not contain GstBuffers");
1218 buffer = g_value_peek_pointer (bufval);
1220 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1221 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1222 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1223 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1224 GST_WARNING ("Invalid streamheader for FLAC");
1228 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1229 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1230 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1231 GST_BUFFER_SIZE (buffer) - 9);
1233 for (i = 1; i < bufarr->len; i++) {
1234 bufval = &g_array_index (bufarr, GValue, i);
1236 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1237 g_free (context->codec_priv);
1238 context->codec_priv = NULL;
1239 context->codec_priv_size = 0;
1240 GST_WARNING ("streamheaders array does not contain GstBuffers");
1244 buffer = g_value_peek_pointer (bufval);
1246 context->codec_priv =
1247 g_realloc (context->codec_priv,
1248 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1249 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1250 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1251 context->codec_priv_size =
1252 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1259 speex_streamheader_to_codecdata (const GValue * streamheader,
1260 GstMatroskaTrackContext * context)
1266 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1267 GST_WARNING ("No or invalid streamheader field in the caps");
1271 bufarr = g_value_peek_pointer (streamheader);
1272 if (bufarr->len != 2) {
1273 GST_WARNING ("Too few headers in streamheader field");
1277 context->xiph_headers_to_skip = bufarr->len + 1;
1279 bufval = &g_array_index (bufarr, GValue, 0);
1280 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1281 GST_WARNING ("streamheaders array does not contain GstBuffers");
1285 buffer = g_value_peek_pointer (bufval);
1287 if (GST_BUFFER_SIZE (buffer) < 80
1288 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1289 GST_WARNING ("Invalid streamheader for Speex");
1293 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1294 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1295 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1296 GST_BUFFER_SIZE (buffer));
1298 bufval = &g_array_index (bufarr, GValue, 1);
1300 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1301 g_free (context->codec_priv);
1302 context->codec_priv = NULL;
1303 context->codec_priv_size = 0;
1304 GST_WARNING ("streamheaders array does not contain GstBuffers");
1308 buffer = g_value_peek_pointer (bufval);
1310 context->codec_priv =
1311 g_realloc (context->codec_priv,
1312 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1313 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1314 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1315 context->codec_priv_size =
1316 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1321 static const gchar *
1322 aac_codec_data_to_codec_id (const GstBuffer * buf)
1324 const gchar *result;
1327 /* default to MAIN */
1330 if (GST_BUFFER_SIZE (buf) >= 2) {
1331 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1349 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1358 * gst_matroska_mux_audio_pad_setcaps:
1359 * @pad: Pad which got the caps.
1362 * Setcaps function for audio sink pad.
1364 * Returns: #TRUE on success.
1367 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1369 GstMatroskaTrackContext *context = NULL;
1370 GstMatroskaTrackAudioContext *audiocontext;
1371 GstMatroskaMux *mux;
1372 GstMatroskaPad *collect_pad;
1373 const gchar *mimetype;
1374 gint samplerate = 0, channels = 0;
1375 GstStructure *structure;
1376 const GValue *codec_data = NULL;
1377 const GstBuffer *buf = NULL;
1378 const gchar *stream_format = NULL;
1380 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1383 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1384 g_assert (collect_pad);
1385 context = collect_pad->track;
1387 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1388 audiocontext = (GstMatroskaTrackAudioContext *) context;
1390 structure = gst_caps_get_structure (caps, 0);
1391 mimetype = gst_structure_get_name (structure);
1394 gst_structure_get_int (structure, "rate", &samplerate);
1395 gst_structure_get_int (structure, "channels", &channels);
1397 audiocontext->samplerate = samplerate;
1398 audiocontext->channels = channels;
1399 audiocontext->bitdepth = 0;
1400 context->default_duration = 0;
1402 codec_data = gst_structure_get_value (structure, "codec_data");
1404 buf = gst_value_get_buffer (codec_data);
1406 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1407 * data and other settings
1411 if (!strcmp (mimetype, "audio/mpeg")) {
1412 gint mpegversion = 0;
1414 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1415 switch (mpegversion) {
1421 gst_structure_get_int (structure, "layer", &layer);
1423 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1424 GST_WARNING_OBJECT (mux,
1425 "Unable to determine MPEG audio version, assuming 1");
1431 else if (layer == 2)
1433 else if (version == 2)
1438 context->default_duration =
1439 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1443 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1446 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1449 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1458 stream_format = gst_structure_get_string (structure, "stream-format");
1459 /* check this is raw aac */
1460 if (stream_format) {
1461 if (strcmp (stream_format, "raw") != 0) {
1462 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1466 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1471 if (mpegversion == 2)
1473 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1474 aac_codec_data_to_codec_id (buf));
1475 else if (mpegversion == 4)
1477 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1478 aac_codec_data_to_codec_id (buf));
1480 g_assert_not_reached ();
1482 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1489 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1491 gint endianness = G_LITTLE_ENDIAN;
1492 gboolean signedness = TRUE;
1494 if (!gst_structure_get_int (structure, "width", &width) ||
1495 !gst_structure_get_int (structure, "depth", &depth) ||
1496 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1497 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1502 !gst_structure_get_int (structure, "endianness", &endianness)) {
1503 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1507 if (width != depth) {
1508 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1512 /* FIXME: where is this spec'ed out? (tpm) */
1513 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1514 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1518 audiocontext->bitdepth = depth;
1519 if (endianness == G_BIG_ENDIAN)
1520 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1522 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1524 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1527 if (!gst_structure_get_int (structure, "width", &width)) {
1528 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1532 audiocontext->bitdepth = width;
1533 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1535 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1536 const GValue *streamheader;
1538 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1540 if (context->codec_priv != NULL) {
1541 g_free (context->codec_priv);
1542 context->codec_priv = NULL;
1543 context->codec_priv_size = 0;
1546 streamheader = gst_structure_get_value (structure, "streamheader");
1547 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1548 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1549 ("vorbis stream headers missing or malformed"));
1552 } else if (!strcmp (mimetype, "audio/x-flac")) {
1553 const GValue *streamheader;
1555 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1556 if (context->codec_priv != NULL) {
1557 g_free (context->codec_priv);
1558 context->codec_priv = NULL;
1559 context->codec_priv_size = 0;
1562 streamheader = gst_structure_get_value (structure, "streamheader");
1563 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1564 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1565 ("flac stream headers missing or malformed"));
1568 } else if (!strcmp (mimetype, "audio/x-speex")) {
1569 const GValue *streamheader;
1571 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1572 if (context->codec_priv != NULL) {
1573 g_free (context->codec_priv);
1574 context->codec_priv = NULL;
1575 context->codec_priv_size = 0;
1578 streamheader = gst_structure_get_value (structure, "streamheader");
1579 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1580 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1581 ("speex stream headers missing or malformed"));
1584 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1585 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1586 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1588 } else if (!strcmp (mimetype, "audio/x-dts")) {
1589 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1590 } else if (!strcmp (mimetype, "audio/x-tta")) {
1593 /* TTA frame duration */
1594 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1596 gst_structure_get_int (structure, "width", &width);
1597 audiocontext->bitdepth = width;
1598 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1600 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1602 const GValue *mdpr_data;
1604 gst_structure_get_int (structure, "raversion", &raversion);
1605 switch (raversion) {
1607 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1610 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1613 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1619 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1620 if (mdpr_data != NULL) {
1621 guint8 *priv_data = NULL;
1622 guint priv_data_size = 0;
1624 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1626 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1627 priv_data = g_malloc0 (priv_data_size);
1629 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1631 context->codec_priv = priv_data;
1632 context->codec_priv_size = priv_data_size;
1635 } else if (!strcmp (mimetype, "audio/x-wma")) {
1637 guint codec_priv_size;
1644 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1645 || !gst_structure_get_int (structure, "block_align", &block_align)
1646 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1647 || samplerate == 0 || channels == 0) {
1648 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1649 "channels/rate on WMA caps");
1653 switch (wmaversion) {
1655 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1658 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1661 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1664 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1668 if (gst_structure_get_int (structure, "depth", &depth))
1669 audiocontext->bitdepth = depth;
1671 codec_priv_size = WAVEFORMATEX_SIZE;
1673 codec_priv_size += GST_BUFFER_SIZE (buf);
1675 /* serialize waveformatex structure */
1676 codec_priv = g_malloc0 (codec_priv_size);
1677 GST_WRITE_UINT16_LE (codec_priv, format);
1678 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1679 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1680 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1681 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1682 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1684 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1686 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1688 /* process codec private/initialization data, if any */
1690 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1691 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1694 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1695 context->codec_priv = (gpointer) codec_priv;
1696 context->codec_priv_size = codec_priv_size;
1704 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1705 GST_PAD_NAME (pad), caps);
1712 * gst_matroska_mux_subtitle_pad_setcaps:
1713 * @pad: Pad which got the caps.
1716 * Setcaps function for subtitle sink pad.
1718 * Returns: #TRUE on success.
1721 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1724 * Consider this as boilerplate code for now. There is
1725 * no single subtitle creation element in GStreamer,
1726 * neither do I know how subtitling works at all. */
1728 /* There is now (at least) one such alement (kateenc), and I'm going
1729 to handle it here and claim it works when it can be piped back
1730 through GStreamer and VLC */
1732 GstMatroskaTrackContext *context = NULL;
1733 GstMatroskaTrackSubtitleContext *scontext;
1734 GstMatroskaMux *mux;
1735 GstMatroskaPad *collect_pad;
1736 const gchar *mimetype;
1737 GstStructure *structure;
1739 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1742 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1743 g_assert (collect_pad);
1744 context = collect_pad->track;
1746 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1747 scontext = (GstMatroskaTrackSubtitleContext *) context;
1749 structure = gst_caps_get_structure (caps, 0);
1750 mimetype = gst_structure_get_name (structure);
1753 scontext->check_utf8 = 1;
1754 scontext->invalid_utf8 = 0;
1755 context->default_duration = 0;
1757 /* TODO: - other format than Kate */
1759 if (!strcmp (mimetype, "subtitle/x-kate")) {
1760 const GValue *streamheader;
1762 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1764 if (context->codec_priv != NULL) {
1765 g_free (context->codec_priv);
1766 context->codec_priv = NULL;
1767 context->codec_priv_size = 0;
1770 streamheader = gst_structure_get_value (structure, "streamheader");
1771 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1772 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1773 ("kate stream headers missing or malformed"));
1784 * gst_matroska_mux_request_new_pad:
1785 * @element: #GstMatroskaMux.
1786 * @templ: #GstPadTemplate.
1787 * @pad_name: New pad name.
1789 * Request pad function for sink templates.
1791 * Returns: New #GstPad.
1794 gst_matroska_mux_request_new_pad (GstElement * element,
1795 GstPadTemplate * templ, const gchar * req_name)
1797 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1798 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1799 GstMatroskaPad *collect_pad;
1800 GstPad *newpad = NULL;
1802 const gchar *pad_name = NULL;
1803 GstPadSetCapsFunction setcapsfunc = NULL;
1804 GstMatroskaTrackContext *context = NULL;
1807 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1808 /* don't mix named and unnamed pads, if the pad already exists we fail when
1809 * trying to add it */
1810 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1811 pad_name = req_name;
1813 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1816 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1817 context = (GstMatroskaTrackContext *)
1818 g_new0 (GstMatroskaTrackAudioContext, 1);
1819 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1820 context->name = g_strdup ("Audio");
1821 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1822 /* don't mix named and unnamed pads, if the pad already exists we fail when
1823 * trying to add it */
1824 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1825 pad_name = req_name;
1827 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1830 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1831 context = (GstMatroskaTrackContext *)
1832 g_new0 (GstMatroskaTrackVideoContext, 1);
1833 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1834 context->name = g_strdup ("Video");
1835 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1836 /* don't mix named and unnamed pads, if the pad already exists we fail when
1837 * trying to add it */
1838 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1839 pad_name = req_name;
1841 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1844 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1845 context = (GstMatroskaTrackContext *)
1846 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1847 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1848 context->name = g_strdup ("Subtitle");
1850 GST_WARNING_OBJECT (mux, "This is not our template!");
1854 newpad = gst_pad_new_from_template (templ, pad_name);
1856 collect_pad = (GstMatroskaPad *)
1857 gst_collect_pads_add_pad_full (mux->collect, newpad,
1858 sizeof (GstMatroskaPad),
1859 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1861 collect_pad->track = context;
1862 gst_matroska_pad_reset (collect_pad, FALSE);
1864 /* FIXME: hacked way to override/extend the event function of
1865 * GstCollectPads; because it sets its own event function giving the
1866 * element no access to events.
1867 * TODO GstCollectPads should really give its 'users' a clean chance to
1868 * properly handle events that are not meant for collectpads itself.
1869 * Perhaps a callback or so, though rejected (?) in #340060.
1870 * This would allow (clean) transcoding of info from demuxer/streams
1871 * to another muxer */
1872 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1873 gst_pad_set_event_function (newpad,
1874 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1876 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1877 gst_pad_set_active (newpad, TRUE);
1878 if (!gst_element_add_pad (element, newpad))
1879 goto pad_add_failed;
1883 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1890 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1891 gst_object_unref (newpad);
1897 * gst_matroska_mux_release_pad:
1898 * @element: #GstMatroskaMux.
1899 * @pad: Pad to release.
1901 * Release a previously requested pad.
1904 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1906 GstMatroskaMux *mux;
1909 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1911 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1912 GstCollectData *cdata = (GstCollectData *) walk->data;
1913 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1915 if (cdata->pad == pad) {
1916 GstClockTime min_dur; /* observed minimum duration */
1918 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1919 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1920 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1921 if (collect_pad->duration < min_dur)
1922 collect_pad->duration = min_dur;
1925 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1926 mux->duration < collect_pad->duration)
1927 mux->duration = collect_pad->duration;
1933 gst_collect_pads_remove_pad (mux->collect, pad);
1934 if (gst_element_remove_pad (element, pad))
1940 * gst_matroska_mux_track_header:
1941 * @mux: #GstMatroskaMux
1942 * @context: Tack context.
1944 * Write a track header.
1947 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1948 GstMatroskaTrackContext * context)
1950 GstEbmlWrite *ebml = mux->ebml_write;
1953 /* TODO: check if everything necessary is written and check default values */
1955 /* track type goes before the type-specific stuff */
1956 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1957 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1959 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1960 gst_matroska_mux_create_uid ());
1961 if (context->default_duration) {
1962 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1963 context->default_duration);
1965 if (context->language) {
1966 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1970 /* type-specific stuff */
1971 switch (context->type) {
1972 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1973 GstMatroskaTrackVideoContext *videocontext =
1974 (GstMatroskaTrackVideoContext *) context;
1976 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1977 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1978 videocontext->pixel_width);
1979 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1980 videocontext->pixel_height);
1981 if (videocontext->display_width && videocontext->display_height) {
1982 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1983 videocontext->display_width);
1984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1985 videocontext->display_height);
1987 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1988 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1989 if (videocontext->fourcc) {
1990 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1992 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1993 (gpointer) & fcc_le, 4);
1995 gst_ebml_write_master_finish (ebml, master);
2000 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2001 GstMatroskaTrackAudioContext *audiocontext =
2002 (GstMatroskaTrackAudioContext *) context;
2004 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2005 if (audiocontext->samplerate != 8000)
2006 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2007 audiocontext->samplerate);
2008 if (audiocontext->channels != 1)
2009 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2010 audiocontext->channels);
2011 if (audiocontext->bitdepth) {
2012 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2013 audiocontext->bitdepth);
2015 gst_ebml_write_master_finish (ebml, master);
2021 /* doesn't need type-specific data */
2025 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2026 if (context->codec_priv)
2027 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2028 context->codec_priv, context->codec_priv_size);
2029 /* FIXME: until we have a nice way of getting the codecname
2030 * out of the caps, I'm not going to enable this. Too much
2031 * (useless, double, boring) work... */
2032 /* TODO: Use value from tags if any */
2033 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2034 context->codec_name); */
2035 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2040 * gst_matroska_mux_start:
2041 * @mux: #GstMatroskaMux
2043 * Start a new matroska file (write headers etc...)
2046 gst_matroska_mux_start (GstMatroskaMux * mux)
2048 GstEbmlWrite *ebml = mux->ebml_write;
2049 const gchar *doctype;
2050 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2051 GST_MATROSKA_ID_TRACKS,
2052 GST_MATROSKA_ID_CUES,
2053 GST_MATROSKA_ID_TAGS,
2056 guint64 master, child;
2060 GstClockTime duration = 0;
2061 guint32 segment_uid[4];
2062 GTimeVal time = { 0, 0 };
2064 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2065 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2067 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2069 /* we start with a EBML header */
2070 doctype = mux->doctype;
2071 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2072 doctype, mux->doctype_version);
2073 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2075 /* the rest of the header is cached */
2076 gst_ebml_write_set_cache (ebml, 0x1000);
2078 /* start a segment */
2080 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2081 mux->segment_master = ebml->pos;
2083 if (!mux->streamable) {
2084 /* seekhead (table of contents) - we set the positions later */
2085 mux->seekhead_pos = ebml->pos;
2086 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2087 for (i = 0; seekhead_id[i] != 0; i++) {
2088 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2089 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2090 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2091 gst_ebml_write_master_finish (ebml, child);
2093 gst_ebml_write_master_finish (ebml, master);
2097 mux->info_pos = ebml->pos;
2098 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2099 for (i = 0; i < 4; i++) {
2100 segment_uid[i] = g_random_int ();
2102 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2103 (guint8 *) segment_uid, 16);
2104 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2105 mux->duration_pos = ebml->pos;
2107 if (!mux->streamable) {
2108 for (collected = mux->collect->data; collected;
2109 collected = g_slist_next (collected)) {
2110 GstMatroskaPad *collect_pad;
2111 GstFormat format = GST_FORMAT_TIME;
2113 gint64 trackduration;
2115 collect_pad = (GstMatroskaPad *) collected->data;
2116 thepad = collect_pad->collect.pad;
2118 /* Query the total length of the track. */
2119 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2120 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2121 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2122 GST_TIME_ARGS (trackduration));
2123 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2124 duration = (GstClockTime) trackduration;
2128 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2129 gst_guint64_to_gdouble (duration) /
2130 gst_guint64_to_gdouble (mux->time_scale));
2132 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2133 "GStreamer plugin version " PACKAGE_VERSION);
2134 if (mux->writing_app && mux->writing_app[0]) {
2135 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2137 g_get_current_time (&time);
2138 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2139 gst_ebml_write_master_finish (ebml, master);
2142 mux->tracks_pos = ebml->pos;
2143 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2145 for (collected = mux->collect->data; collected;
2146 collected = g_slist_next (collected)) {
2147 GstMatroskaPad *collect_pad;
2150 collect_pad = (GstMatroskaPad *) collected->data;
2151 thepad = collect_pad->collect.pad;
2153 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2154 collect_pad->track->codec_id != 0) {
2155 collect_pad->track->num = tracknum++;
2156 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2157 gst_matroska_mux_track_header (mux, collect_pad->track);
2158 gst_ebml_write_master_finish (ebml, child);
2161 gst_ebml_write_master_finish (ebml, master);
2163 /* lastly, flush the cache */
2164 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2168 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2171 /* TODO: more sensible tag mappings */
2174 const gchar *matroska_tagname;
2175 const gchar *gstreamer_tagname;
2179 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2180 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2181 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2182 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2183 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2184 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2185 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2186 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2187 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2188 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2189 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2190 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2191 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2192 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2193 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2195 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2197 guint64 simpletag_master;
2199 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2200 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2201 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2203 if (strcmp (tagname_gst, tag) == 0) {
2204 GValue src = { 0, };
2207 if (!gst_tag_list_copy_value (&src, list, tag))
2209 if ((dest = gst_value_serialize (&src))) {
2211 simpletag_master = gst_ebml_write_master_start (ebml,
2212 GST_MATROSKA_ID_SIMPLETAG);
2213 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2214 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2215 gst_ebml_write_master_finish (ebml, simpletag_master);
2218 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2220 g_value_unset (&src);
2228 * gst_matroska_mux_finish:
2229 * @mux: #GstMatroskaMux
2231 * Finish a new matroska file (write index etc...)
2234 gst_matroska_mux_finish (GstMatroskaMux * mux)
2236 GstEbmlWrite *ebml = mux->ebml_write;
2238 guint64 duration = 0;
2240 const GstTagList *tags;
2242 /* finish last cluster */
2244 gst_ebml_write_master_finish (ebml, mux->cluster);
2248 if (mux->index != NULL) {
2250 guint64 master, pointentry_master, trackpos_master;
2252 mux->cues_pos = ebml->pos;
2253 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2254 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2256 for (n = 0; n < mux->num_indexes; n++) {
2257 GstMatroskaIndex *idx = &mux->index[n];
2259 pointentry_master = gst_ebml_write_master_start (ebml,
2260 GST_MATROSKA_ID_POINTENTRY);
2261 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2262 idx->time / mux->time_scale);
2263 trackpos_master = gst_ebml_write_master_start (ebml,
2264 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2265 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2266 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2267 idx->pos - mux->segment_master);
2268 gst_ebml_write_master_finish (ebml, trackpos_master);
2269 gst_ebml_write_master_finish (ebml, pointentry_master);
2272 gst_ebml_write_master_finish (ebml, master);
2273 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2277 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2279 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2280 guint64 master_tags, master_tag;
2282 GST_DEBUG ("Writing tags");
2284 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2285 mux->tags_pos = ebml->pos;
2286 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2287 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2288 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2289 gst_ebml_write_master_finish (ebml, master_tag);
2290 gst_ebml_write_master_finish (ebml, master_tags);
2293 /* update seekhead. We know that:
2294 * - a seekhead contains 4 entries.
2295 * - order of entries is as above.
2296 * - a seekhead has a 4-byte header + 8-byte length
2297 * - each entry is 2-byte master, 2-byte ID pointer,
2298 * 2-byte length pointer, all 8/1-byte length, 4-
2299 * byte ID and 8-byte length pointer, where the
2300 * length pointer starts at 20.
2301 * - all entries are local to the segment (so pos - segment_master).
2302 * - so each entry is at 12 + 20 + num * 28. */
2303 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2304 mux->info_pos - mux->segment_master);
2305 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2306 mux->tracks_pos - mux->segment_master);
2307 if (mux->index != NULL) {
2308 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2309 mux->cues_pos - mux->segment_master);
2312 guint64 my_pos = ebml->pos;
2314 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2315 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2316 gst_ebml_write_seek (ebml, my_pos);
2319 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2320 mux->tags_pos - mux->segment_master);
2323 guint64 my_pos = ebml->pos;
2325 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2326 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2327 gst_ebml_write_seek (ebml, my_pos);
2330 /* update duration */
2331 /* first get the overall duration */
2332 /* a released track may have left a duration in here */
2333 duration = mux->duration;
2334 for (collected = mux->collect->data; collected;
2335 collected = g_slist_next (collected)) {
2336 GstMatroskaPad *collect_pad;
2337 GstClockTime min_duration; /* observed minimum duration */
2339 collect_pad = (GstMatroskaPad *) collected->data;
2341 GST_DEBUG_OBJECT (mux,
2342 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2343 " end ts %" GST_TIME_FORMAT, collect_pad,
2344 GST_TIME_ARGS (collect_pad->start_ts),
2345 GST_TIME_ARGS (collect_pad->end_ts));
2347 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2348 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2350 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2351 if (collect_pad->duration < min_duration)
2352 collect_pad->duration = min_duration;
2353 GST_DEBUG_OBJECT (collect_pad,
2354 "final track duration: %" GST_TIME_FORMAT,
2355 GST_TIME_ARGS (collect_pad->duration));
2358 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2359 duration < collect_pad->duration)
2360 duration = collect_pad->duration;
2362 if (duration != 0) {
2363 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2364 GST_TIME_ARGS (duration));
2365 pos = mux->ebml_write->pos;
2366 gst_ebml_write_seek (ebml, mux->duration_pos);
2367 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2368 gst_guint64_to_gdouble (duration) /
2369 gst_guint64_to_gdouble (mux->time_scale));
2370 gst_ebml_write_seek (ebml, pos);
2373 guint64 my_pos = ebml->pos;
2375 gst_ebml_write_seek (ebml, mux->duration_pos);
2376 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2377 gst_ebml_write_seek (ebml, my_pos);
2379 GST_DEBUG_OBJECT (mux, "finishing segment");
2380 /* finish segment - this also writes element length */
2381 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2386 * gst_matroska_mux_best_pad:
2387 * @mux: #GstMatroskaMux
2388 * @popped: True if at least one buffer was popped from #GstCollectPads
2390 * Find a pad with the oldest data
2391 * (data from this pad should be written first).
2393 * Returns: Selected pad.
2395 static GstMatroskaPad *
2396 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2399 GstMatroskaPad *best = NULL;
2402 for (collected = mux->collect->data; collected;
2403 collected = g_slist_next (collected)) {
2404 GstMatroskaPad *collect_pad;
2406 collect_pad = (GstMatroskaPad *) collected->data;
2407 /* fetch a new buffer if needed */
2408 if (collect_pad->buffer == NULL) {
2409 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2410 (GstCollectData *) collect_pad);
2412 if (collect_pad->buffer != NULL) {
2416 /* convert to running time */
2417 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2418 /* invalid should pass */
2419 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2420 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2421 GST_FORMAT_TIME, time);
2422 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2423 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2424 GST_PAD_NAME (collect_pad->collect.pad));
2425 gst_buffer_unref (collect_pad->buffer);
2426 collect_pad->buffer = NULL;
2429 collect_pad->buffer =
2430 gst_buffer_make_metadata_writable (collect_pad->buffer);
2431 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2437 /* if we have a buffer check if it is better then the current best one */
2438 if (collect_pad->buffer != NULL) {
2439 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2440 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2441 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2442 GST_BUFFER_TIMESTAMP (best->buffer))) {
2452 * gst_matroska_mux_buffer_header:
2453 * @track: Track context.
2454 * @relative_timestamp: relative timestamp of the buffer
2455 * @flags: Buffer flags.
2457 * Create a buffer containing buffer header.
2459 * Returns: New buffer.
2462 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2463 gint16 relative_timestamp, int flags)
2467 hdr = gst_buffer_new_and_alloc (4);
2468 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2469 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2470 /* time relative to clustertime */
2471 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2474 GST_BUFFER_DATA (hdr)[3] = flags;
2479 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2480 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2481 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2484 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2485 GstMatroskaPad * collect_pad, GstBuffer * buf)
2487 GstMatroskaTrackVideoContext *ctx =
2488 (GstMatroskaTrackVideoContext *) collect_pad->track;
2489 const guint8 *data = GST_BUFFER_DATA (buf);
2490 guint size = GST_BUFFER_SIZE (buf);
2492 guint32 next_parse_offset;
2493 GstBuffer *ret = NULL;
2494 gboolean is_muxing_unit = FALSE;
2496 if (GST_BUFFER_SIZE (buf) < 13) {
2497 gst_buffer_unref (buf);
2501 /* Check if this buffer contains a picture or end-of-sequence packet */
2502 while (size >= 13) {
2503 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2504 gst_buffer_unref (buf);
2508 parse_code = GST_READ_UINT8 (data + 4);
2509 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2510 if (ctx->dirac_unit) {
2511 gst_buffer_unref (ctx->dirac_unit);
2512 ctx->dirac_unit = NULL;
2514 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2515 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2516 is_muxing_unit = TRUE;
2520 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2522 if (G_UNLIKELY (next_parse_offset == 0))
2525 data += next_parse_offset;
2526 size -= next_parse_offset;
2529 if (ctx->dirac_unit)
2530 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2532 ctx->dirac_unit = gst_buffer_ref (buf);
2534 if (is_muxing_unit) {
2535 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2536 ctx->dirac_unit = NULL;
2537 gst_buffer_copy_metadata (ret, buf,
2538 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2539 GST_BUFFER_COPY_CAPS);
2540 gst_buffer_unref (buf);
2542 gst_buffer_unref (buf);
2550 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2554 GValue streamheader = { 0 };
2555 GValue bufval = { 0 };
2556 GstBuffer *streamheader_buffer;
2557 GstEbmlWrite *ebml = mux->ebml_write;
2559 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2560 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2561 caps = gst_caps_new_simple ("video/webm", NULL);
2563 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2565 s = gst_caps_get_structure (caps, 0);
2566 g_value_init (&streamheader, GST_TYPE_ARRAY);
2567 g_value_init (&bufval, GST_TYPE_BUFFER);
2568 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2569 gst_value_set_buffer (&bufval, streamheader_buffer);
2570 gst_value_array_append_value (&streamheader, &bufval);
2571 g_value_unset (&bufval);
2572 gst_structure_set_value (s, "streamheader", &streamheader);
2573 g_value_unset (&streamheader);
2574 gst_caps_replace (&ebml->caps, caps);
2575 gst_buffer_unref (streamheader_buffer);
2576 gst_caps_unref (caps);
2580 * gst_matroska_mux_write_data:
2581 * @mux: #GstMatroskaMux
2582 * @collect_pad: #GstMatroskaPad with the data
2584 * Write collected data (called from gst_matroska_mux_collected).
2586 * Returns: Result of the gst_pad_push issued to write the data.
2588 static GstFlowReturn
2589 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2591 GstEbmlWrite *ebml = mux->ebml_write;
2592 GstBuffer *buf, *hdr;
2594 gboolean write_duration;
2595 gint16 relative_timestamp;
2596 gint64 relative_timestamp64;
2597 guint64 block_duration;
2598 gboolean is_video_keyframe = FALSE;
2601 buf = collect_pad->buffer;
2602 collect_pad->buffer = NULL;
2604 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2605 if (collect_pad->track->xiph_headers_to_skip > 0) {
2606 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2607 gst_buffer_unref (buf);
2608 --collect_pad->track->xiph_headers_to_skip;
2612 /* for dirac we have to queue up everything up to a picture unit */
2613 if (collect_pad->track->codec_id != NULL &&
2614 strcmp (collect_pad->track->codec_id,
2615 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2616 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2621 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2622 * this would wreak havoc with time stored in matroska file */
2623 /* TODO: maybe calculate a timestamp by using the previous timestamp
2624 * and default duration */
2625 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2626 GST_WARNING_OBJECT (collect_pad->collect.pad,
2627 "Invalid buffer timestamp; dropping buffer");
2628 gst_buffer_unref (buf);
2632 /* set the timestamp for outgoing buffers */
2633 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2635 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2636 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2637 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2638 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2639 is_video_keyframe = TRUE;
2643 /* start a new cluster at every keyframe or when we may be reaching the
2644 * limit of the relative timestamp */
2645 if (mux->cluster_time +
2646 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2647 || is_video_keyframe) {
2648 if (!mux->streamable)
2649 gst_ebml_write_master_finish (ebml, mux->cluster);
2650 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2651 mux->cluster_pos = ebml->pos;
2652 gst_ebml_write_set_cache (ebml, 0x20);
2654 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2655 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2656 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2658 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2659 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2661 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2662 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2663 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2664 mux->prev_cluster_size);
2669 mux->cluster_pos = ebml->pos;
2670 gst_ebml_write_set_cache (ebml, 0x20);
2671 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2672 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2673 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2674 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2675 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2678 /* update duration of this track */
2679 if (GST_BUFFER_DURATION_IS_VALID (buf))
2680 collect_pad->duration += GST_BUFFER_DURATION (buf);
2682 /* We currently write index entries for all video tracks or for the audio
2683 * track in a single-track audio file. This could be improved by keeping the
2684 * index only for the *first* video track. */
2686 /* TODO: index is useful for every track, should contain the number of
2687 * the block in the cluster which contains the timestamp, should also work
2688 * for files with multiple audio tracks.
2690 if (is_video_keyframe ||
2691 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2692 (mux->num_streams == 1))) {
2695 if (mux->min_index_interval != 0) {
2696 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2697 if (mux->index[last_idx].track == collect_pad->track->num)
2702 if (last_idx < 0 || mux->min_index_interval == 0 ||
2703 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2704 >= mux->min_index_interval)) {
2705 GstMatroskaIndex *idx;
2707 if (mux->num_indexes % 32 == 0) {
2708 mux->index = g_renew (GstMatroskaIndex, mux->index,
2709 mux->num_indexes + 32);
2711 idx = &mux->index[mux->num_indexes++];
2713 idx->pos = mux->cluster_pos;
2714 idx->time = GST_BUFFER_TIMESTAMP (buf);
2715 idx->track = collect_pad->track->num;
2719 /* Check if the duration differs from the default duration. */
2720 write_duration = FALSE;
2721 block_duration = GST_BUFFER_DURATION (buf);
2722 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2723 if (block_duration != collect_pad->track->default_duration) {
2724 write_duration = TRUE;
2728 /* write the block, for doctype v2 use SimpleBlock if possible
2729 * one slice (*breath*).
2730 * FIXME: Need to do correct lacing! */
2731 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2732 if (relative_timestamp64 >= 0) {
2733 /* round the timestamp */
2734 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2736 /* round the timestamp */
2737 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2739 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2741 if (mux->doctype_version > 1 && !write_duration) {
2743 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2746 gst_matroska_mux_create_buffer_header (collect_pad->track,
2747 relative_timestamp, flags);
2748 gst_ebml_write_set_cache (ebml, 0x40);
2749 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2750 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2751 gst_ebml_write_buffer (ebml, hdr);
2752 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2753 gst_ebml_write_buffer (ebml, buf);
2755 return gst_ebml_last_write_result (ebml);
2757 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2758 /* write and call order slightly unnatural,
2759 * but avoids seek and minizes pushing */
2760 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2762 gst_matroska_mux_create_buffer_header (collect_pad->track,
2763 relative_timestamp, 0);
2764 if (write_duration) {
2765 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2766 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2768 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2769 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2770 gst_ebml_write_buffer (ebml, hdr);
2771 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2772 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2773 gst_ebml_write_buffer (ebml, buf);
2775 return gst_ebml_last_write_result (ebml);
2781 * gst_matroska_mux_collected:
2782 * @pads: #GstCollectPads
2783 * @uuser_data: #GstMatroskaMux
2785 * Collectpads callback.
2787 * Returns: #GstFlowReturn
2789 static GstFlowReturn
2790 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2792 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2793 GstEbmlWrite *ebml = mux->ebml_write;
2794 GstMatroskaPad *best;
2796 GstFlowReturn ret = GST_FLOW_OK;
2798 GST_DEBUG_OBJECT (mux, "Collected pads");
2800 /* start with a header */
2801 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2802 if (mux->collect->data == NULL) {
2803 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2804 ("No input streams configured"));
2805 return GST_FLOW_ERROR;
2807 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2808 gst_ebml_start_streamheader (ebml);
2809 gst_matroska_mux_start (mux);
2810 gst_matroska_mux_stop_streamheader (mux);
2811 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2815 /* which stream to write from? */
2816 best = gst_matroska_mux_best_pad (mux, &popped);
2818 /* if there is no best pad, we have reached EOS */
2820 /* buffer popped, but none returned means it was clipped */
2823 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2824 if (!mux->streamable) {
2825 gst_matroska_mux_finish (mux);
2827 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2829 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2830 ret = GST_FLOW_UNEXPECTED;
2833 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2834 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2835 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2836 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2838 /* make note of first and last encountered timestamps, so we can calculate
2839 * the actual duration later when we send an updated header on eos */
2840 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2841 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2842 GstClockTime end_ts = start_ts;
2844 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2845 end_ts += GST_BUFFER_DURATION (best->buffer);
2846 else if (best->track->default_duration)
2847 end_ts += best->track->default_duration;
2849 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2850 best->end_ts = end_ts;
2852 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2853 start_ts < best->start_ts))
2854 best->start_ts = start_ts;
2857 /* write one buffer */
2858 ret = gst_matroska_mux_write_data (mux, best);
2859 } while (ret == GST_FLOW_OK && !popped);
2866 * gst_matroska_mux_change_state:
2867 * @element: #GstMatroskaMux
2868 * @transition: State change transition.
2870 * Change the muxer state.
2872 * Returns: #GstStateChangeReturn
2874 static GstStateChangeReturn
2875 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2877 GstStateChangeReturn ret;
2878 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2880 switch (transition) {
2881 case GST_STATE_CHANGE_NULL_TO_READY:
2883 case GST_STATE_CHANGE_READY_TO_PAUSED:
2884 gst_collect_pads_start (mux->collect);
2886 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2888 case GST_STATE_CHANGE_PAUSED_TO_READY:
2889 gst_collect_pads_stop (mux->collect);
2895 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2897 switch (transition) {
2898 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2900 case GST_STATE_CHANGE_PAUSED_TO_READY:
2901 gst_matroska_mux_reset (GST_ELEMENT (mux));
2903 case GST_STATE_CHANGE_READY_TO_NULL:
2913 gst_matroska_mux_set_property (GObject * object,
2914 guint prop_id, const GValue * value, GParamSpec * pspec)
2916 GstMatroskaMux *mux;
2918 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2919 mux = GST_MATROSKA_MUX (object);
2922 case ARG_WRITING_APP:
2923 if (!g_value_get_string (value)) {
2924 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2927 g_free (mux->writing_app);
2928 mux->writing_app = g_value_dup_string (value);
2930 case ARG_DOCTYPE_VERSION:
2931 mux->doctype_version = g_value_get_int (value);
2933 case ARG_MIN_INDEX_INTERVAL:
2934 mux->min_index_interval = g_value_get_int64 (value);
2936 case ARG_STREAMABLE:
2937 mux->streamable = g_value_get_boolean (value);
2940 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2946 gst_matroska_mux_get_property (GObject * object,
2947 guint prop_id, GValue * value, GParamSpec * pspec)
2949 GstMatroskaMux *mux;
2951 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2952 mux = GST_MATROSKA_MUX (object);
2955 case ARG_WRITING_APP:
2956 g_value_set_string (value, mux->writing_app);
2958 case ARG_DOCTYPE_VERSION:
2959 g_value_set_int (value, mux->doctype_version);
2961 case ARG_MIN_INDEX_INTERVAL:
2962 g_value_set_int64 (value, mux->min_index_interval);
2964 case ARG_STREAMABLE:
2965 g_value_set_boolean (value, mux->streamable);
2968 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);