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);
263 gst_matroska_mux_add_interfaces (GType type)
265 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
267 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
271 gst_matroska_mux_base_init (gpointer g_class)
276 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
278 GObjectClass *gobject_class;
279 GstElementClass *gstelement_class;
281 gobject_class = (GObjectClass *) klass;
282 gstelement_class = (GstElementClass *) klass;
284 gst_element_class_add_pad_template (gstelement_class,
285 gst_static_pad_template_get (&videosink_templ));
286 gst_element_class_add_pad_template (gstelement_class,
287 gst_static_pad_template_get (&audiosink_templ));
288 gst_element_class_add_pad_template (gstelement_class,
289 gst_static_pad_template_get (&subtitlesink_templ));
290 gst_element_class_add_pad_template (gstelement_class,
291 gst_static_pad_template_get (&src_templ));
292 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
294 "Muxes video/audio/subtitle streams into a matroska stream",
295 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
297 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
300 gobject_class->finalize = gst_matroska_mux_finalize;
302 gobject_class->get_property = gst_matroska_mux_get_property;
303 gobject_class->set_property = gst_matroska_mux_set_property;
305 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
306 g_param_spec_string ("writing-app", "Writing application.",
307 "The name the application that creates the matroska file.",
308 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
309 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
310 g_param_spec_int ("version", "DocType version",
311 "This parameter determines what Matroska features can be used.",
312 1, 2, DEFAULT_DOCTYPE_VERSION,
313 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
315 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
316 "entries", "An index entry is created every so many nanoseconds.",
317 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
318 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
319 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
320 g_param_spec_boolean ("streamable", "Determines whether output should "
321 "be streamable", "If set to true, the output should be as if it is "
322 "to be streamed and hence no indexes written or duration written.",
324 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
326 gstelement_class->change_state =
327 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
328 gstelement_class->request_new_pad =
329 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
330 gstelement_class->release_pad =
331 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
336 * gst_matroska_mux_init:
337 * @mux: #GstMatroskaMux that should be initialized.
338 * @g_class: Class of the muxer.
340 * Matroska muxer constructor.
343 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
345 GstPadTemplate *templ;
348 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
349 mux->srcpad = gst_pad_new_from_template (templ, "src");
351 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
352 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
354 mux->collect = gst_collect_pads_new ();
355 gst_collect_pads_set_function (mux->collect,
356 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
359 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
360 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
362 /* property defaults */
363 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
364 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
365 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
366 mux->streamable = DEFAULT_STREAMABLE;
368 /* initialize internal variables */
370 mux->num_streams = 0;
371 mux->num_a_streams = 0;
372 mux->num_t_streams = 0;
373 mux->num_v_streams = 0;
375 /* initialize remaining variables */
376 gst_matroska_mux_reset (GST_ELEMENT (mux));
381 * gst_matroska_mux_finalize:
382 * @object: #GstMatroskaMux that should be finalized.
384 * Finalize matroska muxer.
387 gst_matroska_mux_finalize (GObject * object)
389 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
391 gst_object_unref (mux->collect);
392 gst_object_unref (mux->ebml_write);
393 if (mux->writing_app)
394 g_free (mux->writing_app);
396 G_OBJECT_CLASS (parent_class)->finalize (object);
401 * gst_matroska_mux_create_uid:
403 * Generate new unused track UID.
405 * Returns: New track UID.
408 gst_matroska_mux_create_uid (void)
415 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
420 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
421 for (i = 0; i < used_uids->len; i++) {
422 if (g_array_index (used_uids, guint64, i) == uid) {
427 g_array_append_val (used_uids, uid);
430 G_UNLOCK (used_uids);
436 * gst_matroska_pad_reset:
437 * @collect_pad: the #GstMatroskaPad
439 * Reset and/or release resources of a matroska collect pad.
442 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
445 GstMatroskaTrackType type = 0;
447 /* free track information */
448 if (collect_pad->track != NULL) {
449 /* retrieve for optional later use */
450 name = collect_pad->track->name;
451 type = collect_pad->track->type;
452 /* extra for video */
453 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
454 GstMatroskaTrackVideoContext *ctx =
455 (GstMatroskaTrackVideoContext *) collect_pad->track;
457 if (ctx->dirac_unit) {
458 gst_buffer_unref (ctx->dirac_unit);
459 ctx->dirac_unit = NULL;
462 g_free (collect_pad->track->codec_id);
463 g_free (collect_pad->track->codec_name);
465 g_free (collect_pad->track->name);
466 g_free (collect_pad->track->language);
467 g_free (collect_pad->track->codec_priv);
468 g_free (collect_pad->track);
469 collect_pad->track = NULL;
472 /* free cached buffer */
473 if (collect_pad->buffer != NULL) {
474 gst_buffer_unref (collect_pad->buffer);
475 collect_pad->buffer = NULL;
478 if (!full && type != 0) {
479 GstMatroskaTrackContext *context;
481 /* create a fresh context */
483 case GST_MATROSKA_TRACK_TYPE_VIDEO:
484 context = (GstMatroskaTrackContext *)
485 g_new0 (GstMatroskaTrackVideoContext, 1);
487 case GST_MATROSKA_TRACK_TYPE_AUDIO:
488 context = (GstMatroskaTrackContext *)
489 g_new0 (GstMatroskaTrackAudioContext, 1);
491 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
492 context = (GstMatroskaTrackContext *)
493 g_new0 (GstMatroskaTrackSubtitleContext, 1);
496 g_assert_not_reached ();
500 context->type = type;
501 context->name = name;
502 /* TODO: check default values for the context */
503 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
504 collect_pad->track = context;
505 collect_pad->buffer = NULL;
506 collect_pad->duration = 0;
507 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
508 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
513 * gst_matroska_pad_free:
514 * @collect_pad: the #GstMatroskaPad
516 * Release resources of a matroska collect pad.
519 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
521 gst_matroska_pad_reset (collect_pad, TRUE);
526 * gst_matroska_mux_reset:
527 * @element: #GstMatroskaMux that should be reseted.
529 * Reset matroska muxer back to initial state.
532 gst_matroska_mux_reset (GstElement * element)
534 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
537 /* reset EBML write */
538 gst_ebml_write_reset (mux->ebml_write);
541 mux->state = GST_MATROSKA_MUX_STATE_START;
543 /* clean up existing streams */
545 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
546 GstMatroskaPad *collect_pad;
548 collect_pad = (GstMatroskaPad *) walk->data;
550 /* reset collect pad to pristine state */
551 gst_matroska_pad_reset (collect_pad, FALSE);
555 mux->num_indexes = 0;
560 mux->time_scale = GST_MSECOND;
561 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
566 mux->cluster_time = 0;
567 mux->cluster_pos = 0;
568 mux->prev_cluster_size = 0;
571 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
575 * gst_matroska_mux_handle_src_event:
576 * @pad: Pad which received the event.
577 * @event: Received event.
579 * handle events - copied from oggmux without understanding
581 * Returns: #TRUE on success.
584 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
588 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
592 /* disable seeking for now */
598 return gst_pad_event_default (pad, event);
602 * gst_matroska_mux_handle_sink_event:
603 * @pad: Pad which received the event.
604 * @event: Received event.
606 * handle events - informational ones like tags
608 * Returns: #TRUE on success.
611 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
613 GstMatroskaTrackContext *context;
614 GstMatroskaPad *collect_pad;
619 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
621 switch (GST_EVENT_TYPE (event)) {
625 GST_DEBUG_OBJECT (mux, "received tag event");
626 gst_event_parse_tag (event, &list);
628 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
629 g_assert (collect_pad);
630 context = collect_pad->track;
633 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
634 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
635 const gchar *lang_code;
637 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
639 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
640 context->language = g_strdup (lang_code);
642 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
647 /* FIXME: what about stream-specific tags? */
648 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
649 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
651 gst_event_unref (event);
652 /* handled this, don't want collectpads to forward it downstream */
656 case GST_EVENT_NEWSEGMENT:
657 /* We don't support NEWSEGMENT events */
659 gst_event_unref (event);
666 /* now GstCollectPads can take care of the rest, e.g. EOS */
668 ret = mux->collect_event (pad, event);
670 gst_object_unref (mux);
677 * gst_matroska_mux_video_pad_setcaps:
678 * @pad: Pad which got the caps.
681 * Setcaps function for video sink pad.
683 * Returns: #TRUE on success.
686 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
688 GstMatroskaTrackContext *context = NULL;
689 GstMatroskaTrackVideoContext *videocontext;
691 GstMatroskaPad *collect_pad;
692 GstStructure *structure;
693 const gchar *mimetype;
694 const GValue *value = NULL;
695 const GstBuffer *codec_buf = NULL;
696 gint width, height, pixel_width, pixel_height;
698 gboolean interlaced = FALSE;
700 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
703 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
704 g_assert (collect_pad);
705 context = collect_pad->track;
707 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
708 videocontext = (GstMatroskaTrackVideoContext *) context;
710 /* gst -> matroska ID'ing */
711 structure = gst_caps_get_structure (caps, 0);
713 mimetype = gst_structure_get_name (structure);
715 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
717 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
719 if (!strcmp (mimetype, "video/x-theora")) {
720 /* we'll extract the details later from the theora identification header */
724 /* get general properties */
725 /* spec says it is mandatory */
726 if (!gst_structure_get_int (structure, "width", &width) ||
727 !gst_structure_get_int (structure, "height", &height))
730 videocontext->pixel_width = width;
731 videocontext->pixel_height = height;
732 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
734 context->default_duration =
735 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
736 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
737 GST_TIME_ARGS (context->default_duration));
739 context->default_duration = 0;
741 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
742 &pixel_width, &pixel_height)) {
743 if (pixel_width > pixel_height) {
744 videocontext->display_width = width * pixel_width / pixel_height;
745 videocontext->display_height = height;
746 } else if (pixel_width < pixel_height) {
747 videocontext->display_width = width;
748 videocontext->display_height = height * pixel_height / pixel_width;
750 videocontext->display_width = 0;
751 videocontext->display_height = 0;
754 videocontext->display_width = 0;
755 videocontext->display_height = 0;
760 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
761 videocontext->fourcc = 0;
763 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
764 * data and other settings
768 /* extract codec_data, may turn out needed */
769 value = gst_structure_get_value (structure, "codec_data");
771 codec_buf = gst_value_get_buffer (value);
774 if (!strcmp (mimetype, "video/x-raw-yuv")) {
775 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
776 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
777 } else if (!strcmp (mimetype, "image/jpeg")) {
778 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
779 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
780 ||!strcmp (mimetype, "video/x-huffyuv")
781 || !strcmp (mimetype, "video/x-divx")
782 || !strcmp (mimetype, "video/x-dv")
783 || !strcmp (mimetype, "video/x-h263")
784 || !strcmp (mimetype, "video/x-msmpeg")
785 || !strcmp (mimetype, "video/x-wmv")) {
786 gst_riff_strf_vids *bih;
787 gint size = sizeof (gst_riff_strf_vids);
790 if (!strcmp (mimetype, "video/x-xvid"))
791 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
792 else if (!strcmp (mimetype, "video/x-huffyuv"))
793 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
794 else if (!strcmp (mimetype, "video/x-dv"))
795 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
796 else if (!strcmp (mimetype, "video/x-h263"))
797 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
798 else if (!strcmp (mimetype, "video/x-divx")) {
801 gst_structure_get_int (structure, "divxversion", &divxversion);
802 switch (divxversion) {
804 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
807 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
810 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
813 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
816 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
817 switch (msmpegversion) {
819 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
822 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
828 } else if (!strcmp (mimetype, "video/x-wmv")) {
831 if (gst_structure_get_fourcc (structure, "format", &format)) {
833 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
834 if (wmvversion == 2) {
835 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
836 } else if (wmvversion == 1) {
837 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
838 } else if (wmvversion == 3) {
839 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
847 bih = g_new0 (gst_riff_strf_vids, 1);
848 GST_WRITE_UINT32_LE (&bih->size, size);
849 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
850 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
851 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
852 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
853 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
854 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
855 videocontext->pixel_height * 3);
857 /* process codec private/initialization data, if any */
859 size += GST_BUFFER_SIZE (codec_buf);
860 bih = g_realloc (bih, size);
861 GST_WRITE_UINT32_LE (&bih->size, size);
862 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
863 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
866 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
867 context->codec_priv = (gpointer) bih;
868 context->codec_priv_size = size;
869 } else if (!strcmp (mimetype, "video/x-h264")) {
870 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
872 if (context->codec_priv != NULL) {
873 g_free (context->codec_priv);
874 context->codec_priv = NULL;
875 context->codec_priv_size = 0;
878 /* Create avcC header */
879 if (codec_buf != NULL) {
880 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
881 context->codec_priv = g_malloc0 (context->codec_priv_size);
882 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
883 context->codec_priv_size);
885 } else if (!strcmp (mimetype, "video/x-theora")) {
886 const GValue *streamheader;
888 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
890 if (context->codec_priv != NULL) {
891 g_free (context->codec_priv);
892 context->codec_priv = NULL;
893 context->codec_priv_size = 0;
896 streamheader = gst_structure_get_value (structure, "streamheader");
897 if (!theora_streamheader_to_codecdata (streamheader, context)) {
898 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
899 ("theora stream headers missing or malformed"));
902 } else if (!strcmp (mimetype, "video/x-dirac")) {
903 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
904 } else if (!strcmp (mimetype, "video/x-vp8")) {
905 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
906 } else if (!strcmp (mimetype, "video/mpeg")) {
909 gst_structure_get_int (structure, "mpegversion", &mpegversion);
910 switch (mpegversion) {
912 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
915 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
918 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
924 /* global headers may be in codec data */
925 if (codec_buf != NULL) {
926 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
927 context->codec_priv = g_malloc0 (context->codec_priv_size);
928 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
929 context->codec_priv_size);
931 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
933 /* can only make it here if preceding case verified it was version 3 */
934 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
935 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
937 const GValue *mdpr_data;
939 gst_structure_get_int (structure, "rmversion", &rmversion);
942 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
945 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
948 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
951 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
957 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
958 if (mdpr_data != NULL) {
959 guint8 *priv_data = NULL;
960 guint priv_data_size = 0;
962 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
964 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
965 priv_data = g_malloc0 (priv_data_size);
967 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
969 context->codec_priv = priv_data;
970 context->codec_priv_size = priv_data_size;
979 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
980 GST_PAD_NAME (pad), caps);
985 /* N > 0 to expect a particular number of headers, negative if the
986 number of headers is variable */
988 xiphN_streamheader_to_codecdata (const GValue * streamheader,
989 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
991 GstBuffer **buf = NULL;
994 guint bufi, i, offset, priv_data_size;
996 if (streamheader == NULL)
997 goto no_stream_headers;
999 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1002 bufarr = g_value_peek_pointer (streamheader);
1003 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1005 if (N > 0 && bufarr->len != N)
1008 context->xiph_headers_to_skip = bufarr->len;
1010 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1011 for (i = 0; i < bufarr->len; i++) {
1012 GValue *bufval = &g_array_index (bufarr, GValue, i);
1014 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1016 goto wrong_content_type;
1019 buf[i] = g_value_peek_pointer (bufval);
1023 if (bufarr->len > 0) {
1024 for (i = 0; i < bufarr->len - 1; i++) {
1025 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1029 for (i = 0; i < bufarr->len; ++i) {
1030 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1033 priv_data = g_malloc0 (priv_data_size);
1035 priv_data[0] = bufarr->len - 1;
1038 if (bufarr->len > 0) {
1039 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1040 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1041 priv_data[offset++] = 0xff;
1043 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1047 for (i = 0; i < bufarr->len; ++i) {
1048 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1049 GST_BUFFER_SIZE (buf[i]));
1050 offset += GST_BUFFER_SIZE (buf[i]);
1053 context->codec_priv = priv_data;
1054 context->codec_priv_size = priv_data_size;
1057 *p_buf0 = gst_buffer_ref (buf[0]);
1066 GST_WARNING ("required streamheaders missing in sink caps!");
1071 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1072 G_VALUE_TYPE_NAME (streamheader));
1077 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1082 GST_WARNING ("streamheaders array does not contain GstBuffers");
1088 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1089 GstMatroskaTrackContext * context)
1091 GstBuffer *buf0 = NULL;
1093 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1096 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1097 GST_WARNING ("First vorbis header too small, ignoring");
1099 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1100 GstMatroskaTrackAudioContext *audiocontext;
1103 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1104 audiocontext = (GstMatroskaTrackAudioContext *) context;
1105 audiocontext->channels = GST_READ_UINT8 (hdr);
1106 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1111 gst_buffer_unref (buf0);
1117 theora_streamheader_to_codecdata (const GValue * streamheader,
1118 GstMatroskaTrackContext * context)
1120 GstBuffer *buf0 = NULL;
1122 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1125 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1126 GST_WARNING ("First theora header too small, ignoring");
1127 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1128 GST_WARNING ("First header not a theora identification header, ignoring");
1130 GstMatroskaTrackVideoContext *videocontext;
1131 guint fps_num, fps_denom, par_num, par_denom;
1134 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1136 videocontext = (GstMatroskaTrackVideoContext *) context;
1137 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1138 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1139 hdr += 3 + 3 + 1 + 1;
1140 fps_num = GST_READ_UINT32_BE (hdr);
1141 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1142 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1143 fps_denom, fps_num);
1145 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1146 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1147 if (par_num > 0 && par_num > 0) {
1148 if (par_num > par_denom) {
1149 videocontext->display_width =
1150 videocontext->pixel_width * par_num / par_denom;
1151 videocontext->display_height = videocontext->pixel_height;
1152 } else if (par_num < par_denom) {
1153 videocontext->display_width = videocontext->pixel_width;
1154 videocontext->display_height =
1155 videocontext->pixel_height * par_denom / par_num;
1157 videocontext->display_width = 0;
1158 videocontext->display_height = 0;
1161 videocontext->display_width = 0;
1162 videocontext->display_height = 0;
1168 gst_buffer_unref (buf0);
1174 kate_streamheader_to_codecdata (const GValue * streamheader,
1175 GstMatroskaTrackContext * context)
1177 GstBuffer *buf0 = NULL;
1179 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1182 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1183 GST_WARNING ("First kate header too small, ignoring");
1184 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1185 GST_WARNING ("First header not a kate identification header, ignoring");
1189 gst_buffer_unref (buf0);
1195 flac_streamheader_to_codecdata (const GValue * streamheader,
1196 GstMatroskaTrackContext * context)
1203 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1204 GST_WARNING ("No or invalid streamheader field in the caps");
1208 bufarr = g_value_peek_pointer (streamheader);
1209 if (bufarr->len < 2) {
1210 GST_WARNING ("Too few headers in streamheader field");
1214 context->xiph_headers_to_skip = bufarr->len + 1;
1216 bufval = &g_array_index (bufarr, GValue, 0);
1217 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1218 GST_WARNING ("streamheaders array does not contain GstBuffers");
1222 buffer = g_value_peek_pointer (bufval);
1224 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1225 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1226 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1227 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1228 GST_WARNING ("Invalid streamheader for FLAC");
1232 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1233 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1234 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1235 GST_BUFFER_SIZE (buffer) - 9);
1237 for (i = 1; i < bufarr->len; i++) {
1238 bufval = &g_array_index (bufarr, GValue, i);
1240 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1241 g_free (context->codec_priv);
1242 context->codec_priv = NULL;
1243 context->codec_priv_size = 0;
1244 GST_WARNING ("streamheaders array does not contain GstBuffers");
1248 buffer = g_value_peek_pointer (bufval);
1250 context->codec_priv =
1251 g_realloc (context->codec_priv,
1252 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1253 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1254 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1255 context->codec_priv_size =
1256 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1263 speex_streamheader_to_codecdata (const GValue * streamheader,
1264 GstMatroskaTrackContext * context)
1270 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1271 GST_WARNING ("No or invalid streamheader field in the caps");
1275 bufarr = g_value_peek_pointer (streamheader);
1276 if (bufarr->len != 2) {
1277 GST_WARNING ("Too few headers in streamheader field");
1281 context->xiph_headers_to_skip = bufarr->len + 1;
1283 bufval = &g_array_index (bufarr, GValue, 0);
1284 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1285 GST_WARNING ("streamheaders array does not contain GstBuffers");
1289 buffer = g_value_peek_pointer (bufval);
1291 if (GST_BUFFER_SIZE (buffer) < 80
1292 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1293 GST_WARNING ("Invalid streamheader for Speex");
1297 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1298 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1299 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1300 GST_BUFFER_SIZE (buffer));
1302 bufval = &g_array_index (bufarr, GValue, 1);
1304 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1305 g_free (context->codec_priv);
1306 context->codec_priv = NULL;
1307 context->codec_priv_size = 0;
1308 GST_WARNING ("streamheaders array does not contain GstBuffers");
1312 buffer = g_value_peek_pointer (bufval);
1314 context->codec_priv =
1315 g_realloc (context->codec_priv,
1316 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1317 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1318 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1319 context->codec_priv_size =
1320 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1325 static const gchar *
1326 aac_codec_data_to_codec_id (const GstBuffer * buf)
1328 const gchar *result;
1331 /* default to MAIN */
1334 if (GST_BUFFER_SIZE (buf) >= 2) {
1335 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1353 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1362 * gst_matroska_mux_audio_pad_setcaps:
1363 * @pad: Pad which got the caps.
1366 * Setcaps function for audio sink pad.
1368 * Returns: #TRUE on success.
1371 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1373 GstMatroskaTrackContext *context = NULL;
1374 GstMatroskaTrackAudioContext *audiocontext;
1375 GstMatroskaMux *mux;
1376 GstMatroskaPad *collect_pad;
1377 const gchar *mimetype;
1378 gint samplerate = 0, channels = 0;
1379 GstStructure *structure;
1380 const GValue *codec_data = NULL;
1381 const GstBuffer *buf = NULL;
1382 const gchar *stream_format = NULL;
1384 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1387 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1388 g_assert (collect_pad);
1389 context = collect_pad->track;
1391 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1392 audiocontext = (GstMatroskaTrackAudioContext *) context;
1394 structure = gst_caps_get_structure (caps, 0);
1395 mimetype = gst_structure_get_name (structure);
1398 gst_structure_get_int (structure, "rate", &samplerate);
1399 gst_structure_get_int (structure, "channels", &channels);
1401 audiocontext->samplerate = samplerate;
1402 audiocontext->channels = channels;
1403 audiocontext->bitdepth = 0;
1404 context->default_duration = 0;
1406 codec_data = gst_structure_get_value (structure, "codec_data");
1408 buf = gst_value_get_buffer (codec_data);
1410 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1411 * data and other settings
1415 if (!strcmp (mimetype, "audio/mpeg")) {
1416 gint mpegversion = 0;
1418 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1419 switch (mpegversion) {
1425 gst_structure_get_int (structure, "layer", &layer);
1427 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1428 GST_WARNING_OBJECT (mux,
1429 "Unable to determine MPEG audio version, assuming 1");
1435 else if (layer == 2)
1437 else if (version == 2)
1442 context->default_duration =
1443 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1447 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1450 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1453 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1462 stream_format = gst_structure_get_string (structure, "stream-format");
1463 /* check this is raw aac */
1464 if (stream_format) {
1465 if (strcmp (stream_format, "raw") != 0) {
1466 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1470 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1475 if (mpegversion == 2)
1477 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1478 aac_codec_data_to_codec_id (buf));
1479 else if (mpegversion == 4)
1481 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1482 aac_codec_data_to_codec_id (buf));
1484 g_assert_not_reached ();
1486 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1493 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1495 gint endianness = G_LITTLE_ENDIAN;
1496 gboolean signedness = TRUE;
1498 if (!gst_structure_get_int (structure, "width", &width) ||
1499 !gst_structure_get_int (structure, "depth", &depth) ||
1500 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1501 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1506 !gst_structure_get_int (structure, "endianness", &endianness)) {
1507 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1511 if (width != depth) {
1512 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1516 /* FIXME: where is this spec'ed out? (tpm) */
1517 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1518 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1522 audiocontext->bitdepth = depth;
1523 if (endianness == G_BIG_ENDIAN)
1524 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1526 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1528 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1531 if (!gst_structure_get_int (structure, "width", &width)) {
1532 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1536 audiocontext->bitdepth = width;
1537 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1539 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1540 const GValue *streamheader;
1542 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1544 if (context->codec_priv != NULL) {
1545 g_free (context->codec_priv);
1546 context->codec_priv = NULL;
1547 context->codec_priv_size = 0;
1550 streamheader = gst_structure_get_value (structure, "streamheader");
1551 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1552 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1553 ("vorbis stream headers missing or malformed"));
1556 } else if (!strcmp (mimetype, "audio/x-flac")) {
1557 const GValue *streamheader;
1559 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1560 if (context->codec_priv != NULL) {
1561 g_free (context->codec_priv);
1562 context->codec_priv = NULL;
1563 context->codec_priv_size = 0;
1566 streamheader = gst_structure_get_value (structure, "streamheader");
1567 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1568 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1569 ("flac stream headers missing or malformed"));
1572 } else if (!strcmp (mimetype, "audio/x-speex")) {
1573 const GValue *streamheader;
1575 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1576 if (context->codec_priv != NULL) {
1577 g_free (context->codec_priv);
1578 context->codec_priv = NULL;
1579 context->codec_priv_size = 0;
1582 streamheader = gst_structure_get_value (structure, "streamheader");
1583 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1584 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1585 ("speex stream headers missing or malformed"));
1588 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1589 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1590 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1591 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1592 } else if (!strcmp (mimetype, "audio/x-dts")) {
1593 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1594 } else if (!strcmp (mimetype, "audio/x-tta")) {
1597 /* TTA frame duration */
1598 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1600 gst_structure_get_int (structure, "width", &width);
1601 audiocontext->bitdepth = width;
1602 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1604 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1606 const GValue *mdpr_data;
1608 gst_structure_get_int (structure, "raversion", &raversion);
1609 switch (raversion) {
1611 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1614 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1617 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1623 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1624 if (mdpr_data != NULL) {
1625 guint8 *priv_data = NULL;
1626 guint priv_data_size = 0;
1628 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1630 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1631 priv_data = g_malloc0 (priv_data_size);
1633 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1635 context->codec_priv = priv_data;
1636 context->codec_priv_size = priv_data_size;
1639 } else if (!strcmp (mimetype, "audio/x-wma")
1640 || !strcmp (mimetype, "audio/x-alaw")
1641 || !strcmp (mimetype, "audio/x-mulaw")) {
1643 guint codec_priv_size;
1648 if (samplerate == 0 || channels == 0) {
1649 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1653 if (!strcmp (mimetype, "audio/x-wma")) {
1657 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1658 || !gst_structure_get_int (structure, "block_align", &block_align)
1659 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1660 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1665 switch (wmaversion) {
1667 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1670 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1673 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1676 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1680 if (gst_structure_get_int (structure, "depth", &depth))
1681 audiocontext->bitdepth = depth;
1682 } else if (!strcmp (mimetype, "audio/x-alaw")
1683 || !strcmp (mimetype, "audio/x-mulaw")) {
1684 audiocontext->bitdepth = 8;
1685 if (!strcmp (mimetype, "audio/x-alaw"))
1686 format = GST_RIFF_WAVE_FORMAT_ALAW;
1688 format = GST_RIFF_WAVE_FORMAT_MULAW;
1690 block_align = channels;
1691 bitrate = block_align * samplerate;
1693 g_assert (format != 0);
1695 codec_priv_size = WAVEFORMATEX_SIZE;
1697 codec_priv_size += GST_BUFFER_SIZE (buf);
1699 /* serialize waveformatex structure */
1700 codec_priv = g_malloc0 (codec_priv_size);
1701 GST_WRITE_UINT16_LE (codec_priv, format);
1702 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1703 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1704 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1705 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1706 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1708 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1710 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1712 /* process codec private/initialization data, if any */
1714 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1715 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1718 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1719 context->codec_priv = (gpointer) codec_priv;
1720 context->codec_priv_size = codec_priv_size;
1728 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1729 GST_PAD_NAME (pad), caps);
1736 * gst_matroska_mux_subtitle_pad_setcaps:
1737 * @pad: Pad which got the caps.
1740 * Setcaps function for subtitle sink pad.
1742 * Returns: #TRUE on success.
1745 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1748 * Consider this as boilerplate code for now. There is
1749 * no single subtitle creation element in GStreamer,
1750 * neither do I know how subtitling works at all. */
1752 /* There is now (at least) one such alement (kateenc), and I'm going
1753 to handle it here and claim it works when it can be piped back
1754 through GStreamer and VLC */
1756 GstMatroskaTrackContext *context = NULL;
1757 GstMatroskaTrackSubtitleContext *scontext;
1758 GstMatroskaMux *mux;
1759 GstMatroskaPad *collect_pad;
1760 const gchar *mimetype;
1761 GstStructure *structure;
1763 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1766 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1767 g_assert (collect_pad);
1768 context = collect_pad->track;
1770 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1771 scontext = (GstMatroskaTrackSubtitleContext *) context;
1773 structure = gst_caps_get_structure (caps, 0);
1774 mimetype = gst_structure_get_name (structure);
1777 scontext->check_utf8 = 1;
1778 scontext->invalid_utf8 = 0;
1779 context->default_duration = 0;
1781 /* TODO: - other format than Kate */
1783 if (!strcmp (mimetype, "subtitle/x-kate")) {
1784 const GValue *streamheader;
1786 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1788 if (context->codec_priv != NULL) {
1789 g_free (context->codec_priv);
1790 context->codec_priv = NULL;
1791 context->codec_priv_size = 0;
1794 streamheader = gst_structure_get_value (structure, "streamheader");
1795 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1796 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1797 ("kate stream headers missing or malformed"));
1808 * gst_matroska_mux_request_new_pad:
1809 * @element: #GstMatroskaMux.
1810 * @templ: #GstPadTemplate.
1811 * @pad_name: New pad name.
1813 * Request pad function for sink templates.
1815 * Returns: New #GstPad.
1818 gst_matroska_mux_request_new_pad (GstElement * element,
1819 GstPadTemplate * templ, const gchar * req_name)
1821 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1822 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1823 GstMatroskaPad *collect_pad;
1824 GstPad *newpad = NULL;
1826 const gchar *pad_name = NULL;
1827 GstPadSetCapsFunction setcapsfunc = NULL;
1828 GstMatroskaTrackContext *context = NULL;
1831 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1832 /* don't mix named and unnamed pads, if the pad already exists we fail when
1833 * trying to add it */
1834 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
1835 pad_name = req_name;
1837 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1840 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1841 context = (GstMatroskaTrackContext *)
1842 g_new0 (GstMatroskaTrackAudioContext, 1);
1843 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1844 context->name = g_strdup ("Audio");
1845 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1846 /* don't mix named and unnamed pads, if the pad already exists we fail when
1847 * trying to add it */
1848 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
1849 pad_name = req_name;
1851 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1854 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1855 context = (GstMatroskaTrackContext *)
1856 g_new0 (GstMatroskaTrackVideoContext, 1);
1857 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1858 context->name = g_strdup ("Video");
1859 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1860 /* don't mix named and unnamed pads, if the pad already exists we fail when
1861 * trying to add it */
1862 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
1863 pad_name = req_name;
1865 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1868 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1869 context = (GstMatroskaTrackContext *)
1870 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1871 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1872 context->name = g_strdup ("Subtitle");
1874 GST_WARNING_OBJECT (mux, "This is not our template!");
1878 newpad = gst_pad_new_from_template (templ, pad_name);
1880 collect_pad = (GstMatroskaPad *)
1881 gst_collect_pads_add_pad_full (mux->collect, newpad,
1882 sizeof (GstMatroskaPad),
1883 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1885 collect_pad->track = context;
1886 gst_matroska_pad_reset (collect_pad, FALSE);
1888 /* FIXME: hacked way to override/extend the event function of
1889 * GstCollectPads; because it sets its own event function giving the
1890 * element no access to events.
1891 * TODO GstCollectPads should really give its 'users' a clean chance to
1892 * properly handle events that are not meant for collectpads itself.
1893 * Perhaps a callback or so, though rejected (?) in #340060.
1894 * This would allow (clean) transcoding of info from demuxer/streams
1895 * to another muxer */
1896 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1897 gst_pad_set_event_function (newpad,
1898 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1900 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1901 gst_pad_set_active (newpad, TRUE);
1902 if (!gst_element_add_pad (element, newpad))
1903 goto pad_add_failed;
1907 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1914 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
1915 gst_object_unref (newpad);
1921 * gst_matroska_mux_release_pad:
1922 * @element: #GstMatroskaMux.
1923 * @pad: Pad to release.
1925 * Release a previously requested pad.
1928 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1930 GstMatroskaMux *mux;
1933 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1935 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1936 GstCollectData *cdata = (GstCollectData *) walk->data;
1937 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1939 if (cdata->pad == pad) {
1940 GstClockTime min_dur; /* observed minimum duration */
1942 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1943 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1944 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1945 if (collect_pad->duration < min_dur)
1946 collect_pad->duration = min_dur;
1949 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1950 mux->duration < collect_pad->duration)
1951 mux->duration = collect_pad->duration;
1957 gst_collect_pads_remove_pad (mux->collect, pad);
1958 if (gst_element_remove_pad (element, pad))
1964 * gst_matroska_mux_track_header:
1965 * @mux: #GstMatroskaMux
1966 * @context: Tack context.
1968 * Write a track header.
1971 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1972 GstMatroskaTrackContext * context)
1974 GstEbmlWrite *ebml = mux->ebml_write;
1977 /* TODO: check if everything necessary is written and check default values */
1979 /* track type goes before the type-specific stuff */
1980 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1981 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1983 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1984 gst_matroska_mux_create_uid ());
1985 if (context->default_duration) {
1986 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1987 context->default_duration);
1989 if (context->language) {
1990 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1994 /* type-specific stuff */
1995 switch (context->type) {
1996 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1997 GstMatroskaTrackVideoContext *videocontext =
1998 (GstMatroskaTrackVideoContext *) context;
2000 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2001 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2002 videocontext->pixel_width);
2003 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2004 videocontext->pixel_height);
2005 if (videocontext->display_width && videocontext->display_height) {
2006 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2007 videocontext->display_width);
2008 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2009 videocontext->display_height);
2011 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2012 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2013 if (videocontext->fourcc) {
2014 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2016 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2017 (gpointer) & fcc_le, 4);
2019 gst_ebml_write_master_finish (ebml, master);
2024 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2025 GstMatroskaTrackAudioContext *audiocontext =
2026 (GstMatroskaTrackAudioContext *) context;
2028 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2029 if (audiocontext->samplerate != 8000)
2030 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2031 audiocontext->samplerate);
2032 if (audiocontext->channels != 1)
2033 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2034 audiocontext->channels);
2035 if (audiocontext->bitdepth) {
2036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2037 audiocontext->bitdepth);
2039 gst_ebml_write_master_finish (ebml, master);
2045 /* doesn't need type-specific data */
2049 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2050 if (context->codec_priv)
2051 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2052 context->codec_priv, context->codec_priv_size);
2053 /* FIXME: until we have a nice way of getting the codecname
2054 * out of the caps, I'm not going to enable this. Too much
2055 * (useless, double, boring) work... */
2056 /* TODO: Use value from tags if any */
2057 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2058 context->codec_name); */
2059 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2064 * gst_matroska_mux_start:
2065 * @mux: #GstMatroskaMux
2067 * Start a new matroska file (write headers etc...)
2070 gst_matroska_mux_start (GstMatroskaMux * mux)
2072 GstEbmlWrite *ebml = mux->ebml_write;
2073 const gchar *doctype;
2074 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2075 GST_MATROSKA_ID_TRACKS,
2076 GST_MATROSKA_ID_CUES,
2077 GST_MATROSKA_ID_TAGS,
2080 guint64 master, child;
2084 GstClockTime duration = 0;
2085 guint32 segment_uid[4];
2086 GTimeVal time = { 0, 0 };
2088 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2089 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2091 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2093 /* we start with a EBML header */
2094 doctype = mux->doctype;
2095 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2096 doctype, mux->doctype_version);
2097 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2099 /* the rest of the header is cached */
2100 gst_ebml_write_set_cache (ebml, 0x1000);
2102 /* start a segment */
2104 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2105 mux->segment_master = ebml->pos;
2107 if (!mux->streamable) {
2108 /* seekhead (table of contents) - we set the positions later */
2109 mux->seekhead_pos = ebml->pos;
2110 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2111 for (i = 0; seekhead_id[i] != 0; i++) {
2112 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2113 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2114 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2115 gst_ebml_write_master_finish (ebml, child);
2117 gst_ebml_write_master_finish (ebml, master);
2121 mux->info_pos = ebml->pos;
2122 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2123 for (i = 0; i < 4; i++) {
2124 segment_uid[i] = g_random_int ();
2126 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2127 (guint8 *) segment_uid, 16);
2128 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2129 mux->duration_pos = ebml->pos;
2131 if (!mux->streamable) {
2132 for (collected = mux->collect->data; collected;
2133 collected = g_slist_next (collected)) {
2134 GstMatroskaPad *collect_pad;
2135 GstFormat format = GST_FORMAT_TIME;
2137 gint64 trackduration;
2139 collect_pad = (GstMatroskaPad *) collected->data;
2140 thepad = collect_pad->collect.pad;
2142 /* Query the total length of the track. */
2143 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2144 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2145 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2146 GST_TIME_ARGS (trackduration));
2147 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2148 duration = (GstClockTime) trackduration;
2152 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2153 gst_guint64_to_gdouble (duration) /
2154 gst_guint64_to_gdouble (mux->time_scale));
2156 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2157 "GStreamer plugin version " PACKAGE_VERSION);
2158 if (mux->writing_app && mux->writing_app[0]) {
2159 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2161 g_get_current_time (&time);
2162 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2163 gst_ebml_write_master_finish (ebml, master);
2166 mux->tracks_pos = ebml->pos;
2167 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2169 for (collected = mux->collect->data; collected;
2170 collected = g_slist_next (collected)) {
2171 GstMatroskaPad *collect_pad;
2174 collect_pad = (GstMatroskaPad *) collected->data;
2175 thepad = collect_pad->collect.pad;
2177 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2178 collect_pad->track->codec_id != 0) {
2179 collect_pad->track->num = tracknum++;
2180 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2181 gst_matroska_mux_track_header (mux, collect_pad->track);
2182 gst_ebml_write_master_finish (ebml, child);
2185 gst_ebml_write_master_finish (ebml, master);
2187 /* lastly, flush the cache */
2188 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2192 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2195 /* TODO: more sensible tag mappings */
2198 const gchar *matroska_tagname;
2199 const gchar *gstreamer_tagname;
2203 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2204 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2205 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2206 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2207 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2208 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2209 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2210 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2211 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2212 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2213 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2214 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2215 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2216 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2217 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2219 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2221 guint64 simpletag_master;
2223 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2224 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2225 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2227 if (strcmp (tagname_gst, tag) == 0) {
2228 GValue src = { 0, };
2231 if (!gst_tag_list_copy_value (&src, list, tag))
2233 if ((dest = gst_value_serialize (&src))) {
2235 simpletag_master = gst_ebml_write_master_start (ebml,
2236 GST_MATROSKA_ID_SIMPLETAG);
2237 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2238 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2239 gst_ebml_write_master_finish (ebml, simpletag_master);
2242 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2244 g_value_unset (&src);
2252 * gst_matroska_mux_finish:
2253 * @mux: #GstMatroskaMux
2255 * Finish a new matroska file (write index etc...)
2258 gst_matroska_mux_finish (GstMatroskaMux * mux)
2260 GstEbmlWrite *ebml = mux->ebml_write;
2262 guint64 duration = 0;
2264 const GstTagList *tags;
2266 /* finish last cluster */
2268 gst_ebml_write_master_finish (ebml, mux->cluster);
2272 if (mux->index != NULL) {
2274 guint64 master, pointentry_master, trackpos_master;
2276 mux->cues_pos = ebml->pos;
2277 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2278 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2280 for (n = 0; n < mux->num_indexes; n++) {
2281 GstMatroskaIndex *idx = &mux->index[n];
2283 pointentry_master = gst_ebml_write_master_start (ebml,
2284 GST_MATROSKA_ID_POINTENTRY);
2285 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2286 idx->time / mux->time_scale);
2287 trackpos_master = gst_ebml_write_master_start (ebml,
2288 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2289 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2290 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2291 idx->pos - mux->segment_master);
2292 gst_ebml_write_master_finish (ebml, trackpos_master);
2293 gst_ebml_write_master_finish (ebml, pointentry_master);
2296 gst_ebml_write_master_finish (ebml, master);
2297 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2301 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2303 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2304 guint64 master_tags, master_tag;
2306 GST_DEBUG ("Writing tags");
2308 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2309 mux->tags_pos = ebml->pos;
2310 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2311 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2312 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2313 gst_ebml_write_master_finish (ebml, master_tag);
2314 gst_ebml_write_master_finish (ebml, master_tags);
2317 /* update seekhead. We know that:
2318 * - a seekhead contains 4 entries.
2319 * - order of entries is as above.
2320 * - a seekhead has a 4-byte header + 8-byte length
2321 * - each entry is 2-byte master, 2-byte ID pointer,
2322 * 2-byte length pointer, all 8/1-byte length, 4-
2323 * byte ID and 8-byte length pointer, where the
2324 * length pointer starts at 20.
2325 * - all entries are local to the segment (so pos - segment_master).
2326 * - so each entry is at 12 + 20 + num * 28. */
2327 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2328 mux->info_pos - mux->segment_master);
2329 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2330 mux->tracks_pos - mux->segment_master);
2331 if (mux->index != NULL) {
2332 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2333 mux->cues_pos - mux->segment_master);
2336 guint64 my_pos = ebml->pos;
2338 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2339 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2340 gst_ebml_write_seek (ebml, my_pos);
2343 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2344 mux->tags_pos - mux->segment_master);
2347 guint64 my_pos = ebml->pos;
2349 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2350 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2351 gst_ebml_write_seek (ebml, my_pos);
2354 /* update duration */
2355 /* first get the overall duration */
2356 /* a released track may have left a duration in here */
2357 duration = mux->duration;
2358 for (collected = mux->collect->data; collected;
2359 collected = g_slist_next (collected)) {
2360 GstMatroskaPad *collect_pad;
2361 GstClockTime min_duration; /* observed minimum duration */
2363 collect_pad = (GstMatroskaPad *) collected->data;
2365 GST_DEBUG_OBJECT (mux,
2366 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2367 " end ts %" GST_TIME_FORMAT, collect_pad,
2368 GST_TIME_ARGS (collect_pad->start_ts),
2369 GST_TIME_ARGS (collect_pad->end_ts));
2371 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2372 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2374 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2375 if (collect_pad->duration < min_duration)
2376 collect_pad->duration = min_duration;
2377 GST_DEBUG_OBJECT (collect_pad,
2378 "final track duration: %" GST_TIME_FORMAT,
2379 GST_TIME_ARGS (collect_pad->duration));
2382 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2383 duration < collect_pad->duration)
2384 duration = collect_pad->duration;
2386 if (duration != 0) {
2387 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2388 GST_TIME_ARGS (duration));
2389 pos = mux->ebml_write->pos;
2390 gst_ebml_write_seek (ebml, mux->duration_pos);
2391 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2392 gst_guint64_to_gdouble (duration) /
2393 gst_guint64_to_gdouble (mux->time_scale));
2394 gst_ebml_write_seek (ebml, pos);
2397 guint64 my_pos = ebml->pos;
2399 gst_ebml_write_seek (ebml, mux->duration_pos);
2400 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2401 gst_ebml_write_seek (ebml, my_pos);
2403 GST_DEBUG_OBJECT (mux, "finishing segment");
2404 /* finish segment - this also writes element length */
2405 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2410 * gst_matroska_mux_best_pad:
2411 * @mux: #GstMatroskaMux
2412 * @popped: True if at least one buffer was popped from #GstCollectPads
2414 * Find a pad with the oldest data
2415 * (data from this pad should be written first).
2417 * Returns: Selected pad.
2419 static GstMatroskaPad *
2420 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2423 GstMatroskaPad *best = NULL;
2426 for (collected = mux->collect->data; collected;
2427 collected = g_slist_next (collected)) {
2428 GstMatroskaPad *collect_pad;
2430 collect_pad = (GstMatroskaPad *) collected->data;
2431 /* fetch a new buffer if needed */
2432 if (collect_pad->buffer == NULL) {
2433 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2434 (GstCollectData *) collect_pad);
2436 if (collect_pad->buffer != NULL) {
2440 /* convert to running time */
2441 time = GST_BUFFER_TIMESTAMP (collect_pad->buffer);
2442 /* invalid should pass */
2443 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2444 time = gst_segment_to_running_time (&collect_pad->collect.segment,
2445 GST_FORMAT_TIME, time);
2446 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2447 GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
2448 GST_PAD_NAME (collect_pad->collect.pad));
2449 gst_buffer_unref (collect_pad->buffer);
2450 collect_pad->buffer = NULL;
2453 collect_pad->buffer =
2454 gst_buffer_make_metadata_writable (collect_pad->buffer);
2455 GST_BUFFER_TIMESTAMP (collect_pad->buffer) = time;
2461 /* if we have a buffer check if it is better then the current best one */
2462 if (collect_pad->buffer != NULL) {
2463 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2464 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2465 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2466 GST_BUFFER_TIMESTAMP (best->buffer))) {
2476 * gst_matroska_mux_buffer_header:
2477 * @track: Track context.
2478 * @relative_timestamp: relative timestamp of the buffer
2479 * @flags: Buffer flags.
2481 * Create a buffer containing buffer header.
2483 * Returns: New buffer.
2486 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2487 gint16 relative_timestamp, int flags)
2491 hdr = gst_buffer_new_and_alloc (4);
2492 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2493 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2494 /* time relative to clustertime */
2495 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2498 GST_BUFFER_DATA (hdr)[3] = flags;
2503 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2504 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2505 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2508 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2509 GstMatroskaPad * collect_pad, GstBuffer * buf)
2511 GstMatroskaTrackVideoContext *ctx =
2512 (GstMatroskaTrackVideoContext *) collect_pad->track;
2513 const guint8 *data = GST_BUFFER_DATA (buf);
2514 guint size = GST_BUFFER_SIZE (buf);
2516 guint32 next_parse_offset;
2517 GstBuffer *ret = NULL;
2518 gboolean is_muxing_unit = FALSE;
2520 if (GST_BUFFER_SIZE (buf) < 13) {
2521 gst_buffer_unref (buf);
2525 /* Check if this buffer contains a picture or end-of-sequence packet */
2526 while (size >= 13) {
2527 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2528 gst_buffer_unref (buf);
2532 parse_code = GST_READ_UINT8 (data + 4);
2533 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2534 if (ctx->dirac_unit) {
2535 gst_buffer_unref (ctx->dirac_unit);
2536 ctx->dirac_unit = NULL;
2538 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2539 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2540 is_muxing_unit = TRUE;
2544 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2546 if (G_UNLIKELY (next_parse_offset == 0))
2549 data += next_parse_offset;
2550 size -= next_parse_offset;
2553 if (ctx->dirac_unit)
2554 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2556 ctx->dirac_unit = gst_buffer_ref (buf);
2558 if (is_muxing_unit) {
2559 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2560 ctx->dirac_unit = NULL;
2561 gst_buffer_copy_metadata (ret, buf,
2562 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2563 GST_BUFFER_COPY_CAPS);
2564 gst_buffer_unref (buf);
2566 gst_buffer_unref (buf);
2574 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2578 GValue streamheader = { 0 };
2579 GValue bufval = { 0 };
2580 GstBuffer *streamheader_buffer;
2581 GstEbmlWrite *ebml = mux->ebml_write;
2583 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2584 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2585 caps = gst_caps_new_simple ("video/webm", NULL);
2587 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2589 s = gst_caps_get_structure (caps, 0);
2590 g_value_init (&streamheader, GST_TYPE_ARRAY);
2591 g_value_init (&bufval, GST_TYPE_BUFFER);
2592 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2593 gst_value_set_buffer (&bufval, streamheader_buffer);
2594 gst_value_array_append_value (&streamheader, &bufval);
2595 g_value_unset (&bufval);
2596 gst_structure_set_value (s, "streamheader", &streamheader);
2597 g_value_unset (&streamheader);
2598 gst_caps_replace (&ebml->caps, caps);
2599 gst_buffer_unref (streamheader_buffer);
2600 gst_caps_unref (caps);
2604 * gst_matroska_mux_write_data:
2605 * @mux: #GstMatroskaMux
2606 * @collect_pad: #GstMatroskaPad with the data
2608 * Write collected data (called from gst_matroska_mux_collected).
2610 * Returns: Result of the gst_pad_push issued to write the data.
2612 static GstFlowReturn
2613 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2615 GstEbmlWrite *ebml = mux->ebml_write;
2616 GstBuffer *buf, *hdr;
2618 gboolean write_duration;
2619 gint16 relative_timestamp;
2620 gint64 relative_timestamp64;
2621 guint64 block_duration;
2622 gboolean is_video_keyframe = FALSE;
2625 buf = collect_pad->buffer;
2626 collect_pad->buffer = NULL;
2628 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2629 if (collect_pad->track->xiph_headers_to_skip > 0) {
2630 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2631 gst_buffer_unref (buf);
2632 --collect_pad->track->xiph_headers_to_skip;
2636 /* for dirac we have to queue up everything up to a picture unit */
2637 if (collect_pad->track->codec_id != NULL &&
2638 strcmp (collect_pad->track->codec_id,
2639 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2640 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2645 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2646 * this would wreak havoc with time stored in matroska file */
2647 /* TODO: maybe calculate a timestamp by using the previous timestamp
2648 * and default duration */
2649 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2650 GST_WARNING_OBJECT (collect_pad->collect.pad,
2651 "Invalid buffer timestamp; dropping buffer");
2652 gst_buffer_unref (buf);
2656 /* set the timestamp for outgoing buffers */
2657 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2659 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2660 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2661 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2662 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2663 is_video_keyframe = TRUE;
2667 /* start a new cluster at every keyframe or when we may be reaching the
2668 * limit of the relative timestamp */
2669 if (mux->cluster_time +
2670 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2671 || is_video_keyframe) {
2672 if (!mux->streamable)
2673 gst_ebml_write_master_finish (ebml, mux->cluster);
2674 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2675 mux->cluster_pos = ebml->pos;
2676 gst_ebml_write_set_cache (ebml, 0x20);
2678 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2679 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2680 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2682 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2683 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2685 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2686 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2687 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2688 mux->prev_cluster_size);
2693 mux->cluster_pos = ebml->pos;
2694 gst_ebml_write_set_cache (ebml, 0x20);
2695 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2696 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2697 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2698 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2699 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2702 /* update duration of this track */
2703 if (GST_BUFFER_DURATION_IS_VALID (buf))
2704 collect_pad->duration += GST_BUFFER_DURATION (buf);
2706 /* We currently write index entries for all video tracks or for the audio
2707 * track in a single-track audio file. This could be improved by keeping the
2708 * index only for the *first* video track. */
2710 /* TODO: index is useful for every track, should contain the number of
2711 * the block in the cluster which contains the timestamp, should also work
2712 * for files with multiple audio tracks.
2714 if (!mux->streamable &&
2715 (is_video_keyframe ||
2716 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2717 (mux->num_streams == 1)))) {
2720 if (mux->min_index_interval != 0) {
2721 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2722 if (mux->index[last_idx].track == collect_pad->track->num)
2727 if (last_idx < 0 || mux->min_index_interval == 0 ||
2728 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2729 >= mux->min_index_interval)) {
2730 GstMatroskaIndex *idx;
2732 if (mux->num_indexes % 32 == 0) {
2733 mux->index = g_renew (GstMatroskaIndex, mux->index,
2734 mux->num_indexes + 32);
2736 idx = &mux->index[mux->num_indexes++];
2738 idx->pos = mux->cluster_pos;
2739 idx->time = GST_BUFFER_TIMESTAMP (buf);
2740 idx->track = collect_pad->track->num;
2744 /* Check if the duration differs from the default duration. */
2745 write_duration = FALSE;
2746 block_duration = GST_BUFFER_DURATION (buf);
2747 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2748 if (block_duration != collect_pad->track->default_duration) {
2749 write_duration = TRUE;
2753 /* write the block, for doctype v2 use SimpleBlock if possible
2754 * one slice (*breath*).
2755 * FIXME: Need to do correct lacing! */
2756 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2757 if (relative_timestamp64 >= 0) {
2758 /* round the timestamp */
2759 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2761 /* round the timestamp */
2762 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2764 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2766 if (mux->doctype_version > 1 && !write_duration) {
2768 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2771 gst_matroska_mux_create_buffer_header (collect_pad->track,
2772 relative_timestamp, flags);
2773 gst_ebml_write_set_cache (ebml, 0x40);
2774 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2775 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2776 gst_ebml_write_buffer (ebml, hdr);
2777 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2778 gst_ebml_write_buffer (ebml, buf);
2780 return gst_ebml_last_write_result (ebml);
2782 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2783 /* write and call order slightly unnatural,
2784 * but avoids seek and minizes pushing */
2785 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2787 gst_matroska_mux_create_buffer_header (collect_pad->track,
2788 relative_timestamp, 0);
2789 if (write_duration) {
2790 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2791 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2793 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2794 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2795 gst_ebml_write_buffer (ebml, hdr);
2796 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2797 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2798 gst_ebml_write_buffer (ebml, buf);
2800 return gst_ebml_last_write_result (ebml);
2806 * gst_matroska_mux_collected:
2807 * @pads: #GstCollectPads
2808 * @uuser_data: #GstMatroskaMux
2810 * Collectpads callback.
2812 * Returns: #GstFlowReturn
2814 static GstFlowReturn
2815 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2817 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2818 GstEbmlWrite *ebml = mux->ebml_write;
2819 GstMatroskaPad *best;
2821 GstFlowReturn ret = GST_FLOW_OK;
2823 GST_DEBUG_OBJECT (mux, "Collected pads");
2825 /* start with a header */
2826 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2827 if (mux->collect->data == NULL) {
2828 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2829 ("No input streams configured"));
2830 return GST_FLOW_ERROR;
2832 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2833 gst_ebml_start_streamheader (ebml);
2834 gst_matroska_mux_start (mux);
2835 gst_matroska_mux_stop_streamheader (mux);
2836 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2840 /* which stream to write from? */
2841 best = gst_matroska_mux_best_pad (mux, &popped);
2843 /* if there is no best pad, we have reached EOS */
2845 /* buffer popped, but none returned means it was clipped */
2848 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2849 if (!mux->streamable) {
2850 gst_matroska_mux_finish (mux);
2852 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2854 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2855 ret = GST_FLOW_UNEXPECTED;
2858 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2859 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2860 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2861 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2863 /* make note of first and last encountered timestamps, so we can calculate
2864 * the actual duration later when we send an updated header on eos */
2865 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2866 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2867 GstClockTime end_ts = start_ts;
2869 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2870 end_ts += GST_BUFFER_DURATION (best->buffer);
2871 else if (best->track->default_duration)
2872 end_ts += best->track->default_duration;
2874 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2875 best->end_ts = end_ts;
2877 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2878 start_ts < best->start_ts))
2879 best->start_ts = start_ts;
2882 /* write one buffer */
2883 ret = gst_matroska_mux_write_data (mux, best);
2884 } while (ret == GST_FLOW_OK && !popped);
2891 * gst_matroska_mux_change_state:
2892 * @element: #GstMatroskaMux
2893 * @transition: State change transition.
2895 * Change the muxer state.
2897 * Returns: #GstStateChangeReturn
2899 static GstStateChangeReturn
2900 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2902 GstStateChangeReturn ret;
2903 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2905 switch (transition) {
2906 case GST_STATE_CHANGE_NULL_TO_READY:
2908 case GST_STATE_CHANGE_READY_TO_PAUSED:
2909 gst_collect_pads_start (mux->collect);
2911 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2913 case GST_STATE_CHANGE_PAUSED_TO_READY:
2914 gst_collect_pads_stop (mux->collect);
2920 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2922 switch (transition) {
2923 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2925 case GST_STATE_CHANGE_PAUSED_TO_READY:
2926 gst_matroska_mux_reset (GST_ELEMENT (mux));
2928 case GST_STATE_CHANGE_READY_TO_NULL:
2938 gst_matroska_mux_set_property (GObject * object,
2939 guint prop_id, const GValue * value, GParamSpec * pspec)
2941 GstMatroskaMux *mux;
2943 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2944 mux = GST_MATROSKA_MUX (object);
2947 case ARG_WRITING_APP:
2948 if (!g_value_get_string (value)) {
2949 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2952 g_free (mux->writing_app);
2953 mux->writing_app = g_value_dup_string (value);
2955 case ARG_DOCTYPE_VERSION:
2956 mux->doctype_version = g_value_get_int (value);
2958 case ARG_MIN_INDEX_INTERVAL:
2959 mux->min_index_interval = g_value_get_int64 (value);
2961 case ARG_STREAMABLE:
2962 mux->streamable = g_value_get_boolean (value);
2965 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2971 gst_matroska_mux_get_property (GObject * object,
2972 guint prop_id, GValue * value, GParamSpec * pspec)
2974 GstMatroskaMux *mux;
2976 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2977 mux = GST_MATROSKA_MUX (object);
2980 case ARG_WRITING_APP:
2981 g_value_set_string (value, mux->writing_app);
2983 case ARG_DOCTYPE_VERSION:
2984 g_value_set_int (value, mux->doctype_version);
2986 case ARG_MIN_INDEX_INTERVAL:
2987 g_value_set_int64 (value, mux->min_index_interval);
2989 case ARG_STREAMABLE:
2990 g_value_set_boolean (value, mux->streamable);
2993 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);