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>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
7 * matroska-mux.c: matroska file/stream muxer
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
30 * SECTION:element-matroskamux
32 * matroskamux muxes different input streams into a Matroska file.
35 * <title>Example launch line</title>
37 * 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.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
40 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
57 #include "matroska-mux.h"
58 #include "matroska-ids.h"
60 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
61 #define GST_CAT_DEFAULT matroskamux_debug
68 ARG_MIN_INDEX_INTERVAL,
72 #define DEFAULT_DOCTYPE_VERSION 2
73 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
74 #define DEFAULT_MIN_INDEX_INTERVAL 0
75 #define DEFAULT_STREAMABLE FALSE
77 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
78 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
80 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
83 GST_STATIC_CAPS ("video/x-matroska")
86 #define COMMON_VIDEO_CAPS \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ], " \
89 "framerate = (fraction) [ 0, MAX ]"
91 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
92 "width = (int) [ 16, 4096 ], " \
93 "height = (int) [ 16, 4096 ] "
96 * * require codec data, etc as needed
99 static GstStaticPadTemplate videosink_templ =
100 GST_STATIC_PAD_TEMPLATE ("video_%u",
103 GST_STATIC_CAPS ("video/mpeg, "
104 "mpegversion = (int) { 1, 2, 4 }, "
105 "systemstream = (boolean) false, "
106 COMMON_VIDEO_CAPS "; "
107 "video/x-h264, stream-format=avc, alignment=au, "
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 "; "
122 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
125 COMMON_VIDEO_CAPS "; "
126 "video/x-pn-realvideo, "
127 "rmversion = (int) [1, 4], "
128 COMMON_VIDEO_CAPS "; "
130 COMMON_VIDEO_CAPS "; "
132 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
133 COMMON_VIDEO_CAPS "; "
134 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
137 #define COMMON_AUDIO_CAPS \
138 "channels = (int) [ 1, MAX ], " \
139 "rate = (int) [ 1, MAX ]"
142 * * require codec data, etc as needed
144 static GstStaticPadTemplate audiosink_templ =
145 GST_STATIC_PAD_TEMPLATE ("audio_%u",
148 GST_STATIC_CAPS ("audio/mpeg, "
149 "mpegversion = (int) 1, "
150 "layer = (int) [ 1, 3 ], "
151 COMMON_AUDIO_CAPS "; "
153 "mpegversion = (int) { 2, 4 }, "
154 "stream-format = (string) raw, "
155 COMMON_AUDIO_CAPS "; "
157 COMMON_AUDIO_CAPS "; "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
167 COMMON_AUDIO_CAPS "; "
169 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
170 "layout = (string) interleaved, "
171 COMMON_AUDIO_CAPS ";"
173 "width = (int) { 8, 16, 24 }, "
174 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
175 "audio/x-pn-realaudio, "
176 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
177 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
178 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
179 COMMON_AUDIO_CAPS ";"
181 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
183 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
186 static GstStaticPadTemplate subtitlesink_templ =
187 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
190 GST_STATIC_CAPS ("subtitle/x-kate; "
191 "text/plain; application/x-ssa; application/x-ass; "
192 "application/x-usf; video/x-dvd-subpicture; "
193 "application/x-subtitle-unknown")
196 static GArray *used_uids;
197 G_LOCK_DEFINE_STATIC (used_uids);
199 #define parent_class gst_matroska_mux_parent_class
200 G_DEFINE_TYPE_WITH_CODE (GstMatroskaMux, gst_matroska_mux, GST_TYPE_ELEMENT,
201 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
203 /* Matroska muxer destructor */
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
207 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
208 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
209 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
210 GstCollectData2 * data, GstEvent * event, gpointer user_data);
213 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
214 GstObject * parent, GstEvent * event);
215 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
216 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
217 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
219 /* gst internal change state handler */
220 static GstStateChangeReturn
221 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
223 /* gobject bla bla */
224 static void gst_matroska_mux_set_property (GObject * object,
225 guint prop_id, const GValue * value, GParamSpec * pspec);
226 static void gst_matroska_mux_get_property (GObject * object,
227 guint prop_id, GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_reset (GstElement * element);
233 static guint64 gst_matroska_mux_create_uid ();
235 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
246 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
250 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
252 GObjectClass *gobject_class;
253 GstElementClass *gstelement_class;
255 gobject_class = (GObjectClass *) klass;
256 gstelement_class = (GstElementClass *) klass;
258 gst_element_class_add_pad_template (gstelement_class,
259 gst_static_pad_template_get (&videosink_templ));
260 gst_element_class_add_pad_template (gstelement_class,
261 gst_static_pad_template_get (&audiosink_templ));
262 gst_element_class_add_pad_template (gstelement_class,
263 gst_static_pad_template_get (&subtitlesink_templ));
264 gst_element_class_add_pad_template (gstelement_class,
265 gst_static_pad_template_get (&src_templ));
266 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
268 "Muxes video/audio/subtitle streams into a matroska stream",
269 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
271 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
274 gobject_class->finalize = gst_matroska_mux_finalize;
276 gobject_class->get_property = gst_matroska_mux_get_property;
277 gobject_class->set_property = gst_matroska_mux_set_property;
279 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
280 g_param_spec_string ("writing-app", "Writing application.",
281 "The name the application that creates the matroska file.",
282 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
284 g_param_spec_int ("version", "DocType version",
285 "This parameter determines what Matroska features can be used.",
286 1, 2, DEFAULT_DOCTYPE_VERSION,
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
289 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
290 "entries", "An index entry is created every so many nanoseconds.",
291 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
292 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
294 g_param_spec_boolean ("streamable", "Determines whether output should "
295 "be streamable", "If set to true, the output should be as if it is "
296 "to be streamed and hence no indexes written or duration written.",
298 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
300 gstelement_class->change_state =
301 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
302 gstelement_class->request_new_pad =
303 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
304 gstelement_class->release_pad =
305 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
309 * Start of pad option handler code
311 #define DEFAULT_PAD_FRAME_DURATION TRUE
312 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
317 PROP_PAD_FRAME_DURATION
323 gboolean frame_duration;
324 gboolean frame_duration_user;
327 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
330 gst_matroskamux_pad_get_type (void)
332 static GType type = 0;
334 if (G_UNLIKELY (type == 0)) {
335 type = g_type_register_static_simple (GST_TYPE_PAD,
336 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
337 (GClassInitFunc) gst_matroskamux_pad_class_init,
338 sizeof (GstMatroskamuxPad), NULL, 0);
343 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
344 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
345 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
346 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
349 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
350 GValue * value, GParamSpec * pspec)
352 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
355 case PROP_PAD_FRAME_DURATION:
356 g_value_set_boolean (value, pad->frame_duration);
359 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
366 const GValue * value, GParamSpec * pspec)
368 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
371 case PROP_PAD_FRAME_DURATION:
372 pad->frame_duration = g_value_get_boolean (value);
373 pad->frame_duration_user = TRUE;
376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382 gst_matroskamux_pad_class_init (GstPadClass * klass)
384 GObjectClass *gobject_class = (GObjectClass *) klass;
386 gobject_class->set_property = gst_matroskamux_pad_set_property;
387 gobject_class->get_property = gst_matroskamux_pad_get_property;
389 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
390 g_param_spec_boolean ("frame-duration", "Frame duration",
391 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
392 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
396 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
398 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
399 pad->frame_duration_user = FALSE;
403 * End of pad option handler code
407 * gst_matroska_mux_init:
408 * @mux: #GstMatroskaMux that should be initialized.
409 * @g_class: Class of the muxer.
411 * Matroska muxer constructor.
414 gst_matroska_mux_init (GstMatroskaMux * mux)
416 GstPadTemplate *templ;
419 gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mux), "src");
420 mux->srcpad = gst_pad_new_from_template (templ, "src");
422 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
423 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
425 mux->collect = gst_collect_pads2_new ();
426 gst_collect_pads2_set_clip_function (mux->collect,
427 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
428 gst_collect_pads2_set_buffer_function (mux->collect,
429 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
430 gst_collect_pads2_set_event_function (mux->collect,
431 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
433 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
434 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
436 /* property defaults */
437 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
438 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
439 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
440 mux->streamable = DEFAULT_STREAMABLE;
442 /* initialize internal variables */
444 mux->num_streams = 0;
445 mux->num_a_streams = 0;
446 mux->num_t_streams = 0;
447 mux->num_v_streams = 0;
449 /* initialize remaining variables */
450 gst_matroska_mux_reset (GST_ELEMENT (mux));
455 * gst_matroska_mux_finalize:
456 * @object: #GstMatroskaMux that should be finalized.
458 * Finalize matroska muxer.
461 gst_matroska_mux_finalize (GObject * object)
463 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
465 gst_event_replace (&mux->force_key_unit_event, NULL);
467 gst_object_unref (mux->collect);
468 gst_object_unref (mux->ebml_write);
469 if (mux->writing_app)
470 g_free (mux->writing_app);
472 G_OBJECT_CLASS (parent_class)->finalize (object);
477 * gst_matroska_mux_create_uid:
479 * Generate new unused track UID.
481 * Returns: New track UID.
484 gst_matroska_mux_create_uid (void)
491 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
496 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
497 for (i = 0; i < used_uids->len; i++) {
498 if (g_array_index (used_uids, guint64, i) == uid) {
503 g_array_append_val (used_uids, uid);
506 G_UNLOCK (used_uids);
512 * gst_matroska_pad_reset:
513 * @collect_pad: the #GstMatroskaPad
515 * Reset and/or release resources of a matroska collect pad.
518 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
521 GstMatroskaTrackType type = 0;
523 /* free track information */
524 if (collect_pad->track != NULL) {
525 /* retrieve for optional later use */
526 name = collect_pad->track->name;
527 type = collect_pad->track->type;
528 /* extra for video */
529 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
530 GstMatroskaTrackVideoContext *ctx =
531 (GstMatroskaTrackVideoContext *) collect_pad->track;
533 if (ctx->dirac_unit) {
534 gst_buffer_unref (ctx->dirac_unit);
535 ctx->dirac_unit = NULL;
538 g_free (collect_pad->track->codec_id);
539 g_free (collect_pad->track->codec_name);
541 g_free (collect_pad->track->name);
542 g_free (collect_pad->track->language);
543 g_free (collect_pad->track->codec_priv);
544 g_free (collect_pad->track);
545 collect_pad->track = NULL;
548 if (!full && type != 0) {
549 GstMatroskaTrackContext *context;
551 /* create a fresh context */
553 case GST_MATROSKA_TRACK_TYPE_VIDEO:
554 context = (GstMatroskaTrackContext *)
555 g_new0 (GstMatroskaTrackVideoContext, 1);
557 case GST_MATROSKA_TRACK_TYPE_AUDIO:
558 context = (GstMatroskaTrackContext *)
559 g_new0 (GstMatroskaTrackAudioContext, 1);
561 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
562 context = (GstMatroskaTrackContext *)
563 g_new0 (GstMatroskaTrackSubtitleContext, 1);
566 g_assert_not_reached ();
570 context->type = type;
571 context->name = name;
572 /* TODO: check default values for the context */
573 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
574 collect_pad->track = context;
575 collect_pad->duration = 0;
576 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
577 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
582 * gst_matroska_pad_free:
583 * @collect_pad: the #GstMatroskaPad
585 * Release resources of a matroska collect pad.
588 gst_matroska_pad_free (GstPad * collect_pad)
590 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
595 * gst_matroska_mux_reset:
596 * @element: #GstMatroskaMux that should be reseted.
598 * Reset matroska muxer back to initial state.
601 gst_matroska_mux_reset (GstElement * element)
603 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
606 /* reset EBML write */
607 gst_ebml_write_reset (mux->ebml_write);
610 mux->state = GST_MATROSKA_MUX_STATE_START;
612 /* clean up existing streams */
614 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
615 GstMatroskaPad *collect_pad;
617 collect_pad = (GstMatroskaPad *) walk->data;
619 /* reset collect pad to pristine state */
620 gst_matroska_pad_reset (collect_pad, FALSE);
624 mux->num_indexes = 0;
629 mux->time_scale = GST_MSECOND;
630 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
635 mux->cluster_time = 0;
636 mux->cluster_pos = 0;
637 mux->prev_cluster_size = 0;
640 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
644 * gst_matroska_mux_handle_src_event:
645 * @pad: Pad which received the event.
646 * @event: Received event.
648 * handle events - copied from oggmux without understanding
650 * Returns: #TRUE on success.
653 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
658 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
662 /* disable seeking for now */
668 return gst_pad_event_default (pad, parent, event);
673 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
675 if (context->codec_priv != NULL) {
676 g_free (context->codec_priv);
677 context->codec_priv = NULL;
678 context->codec_priv_size = 0;
683 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
693 /* produce comma-separated list in hex format */
694 for (i = 0; i < 16; ++i) {
696 /* replicate vobsub's slightly off RGB conversion calculation */
697 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
698 u = ((col >> 8) & 0xff) - 128;
699 v = (col & 0xff) - 128;
700 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
701 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
702 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
703 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
706 sclut = g_strjoinv (",", clutv);
708 /* build codec private; only palette for now */
709 gst_matroska_mux_free_codec_priv (context);
710 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
711 /* include terminating 0 */
712 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
714 for (i = 0; i < 16; ++i) {
721 * gst_matroska_mux_handle_sink_event:
722 * @pad: Pad which received the event.
723 * @event: Received event.
725 * handle events - informational ones like tags
727 * Returns: #TRUE on success.
730 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
731 GstCollectData2 * data, GstEvent * event, gpointer user_data)
733 GstMatroskaPad *collect_pad;
734 GstMatroskaTrackContext *context;
738 gboolean ret = FALSE;
740 mux = GST_MATROSKA_MUX (user_data);
741 collect_pad = (GstMatroskaPad *) data;
743 context = collect_pad->track;
746 switch (GST_EVENT_TYPE (event)) {
747 case GST_EVENT_CAPS:{
750 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
751 gst_event_parse_caps (event, &caps);
753 ret = collect_pad->capsfunc (pad, caps);
754 gst_event_unref (event);
761 GST_DEBUG_OBJECT (mux, "received tag event");
762 gst_event_parse_tag (event, &list);
764 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
765 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
766 const gchar *lang_code;
768 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
770 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
771 context->language = g_strdup (lang_code);
773 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
778 /* FIXME: what about stream-specific tags? */
779 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
780 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
782 gst_event_unref (event);
783 /* handled this, don't want collectpads to forward it downstream */
788 case GST_EVENT_SEGMENT:{
789 const GstSegment *segment;
791 gst_event_parse_segment (event, &segment);
792 if (segment->format != GST_FORMAT_TIME) {
795 gst_event_unref (event);
800 case GST_EVENT_CUSTOM_DOWNSTREAM:{
801 const GstStructure *structure;
803 structure = gst_event_get_structure (event);
804 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
805 gst_event_replace (&mux->force_key_unit_event, NULL);
806 mux->force_key_unit_event = event;
808 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
809 !strcmp ("dvd-spu-clut-change",
810 gst_structure_get_string (structure, "event"))) {
815 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
816 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
817 GST_DEBUG_OBJECT (pad, "... discarding");
820 /* first transform event data into table form */
821 for (i = 0; i < 16; i++) {
822 g_snprintf (name, sizeof (name), "clut%02d", i);
823 if (!gst_structure_get_int (structure, name, &value)) {
824 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
825 "contain %s field", name);
831 /* transform into private data for stream; text form */
832 gst_matroska_mux_build_vobsub_private (context, clut);
837 ret = gst_pad_event_default (data->pad, GST_OBJECT (mux), event);
840 gst_event_unref (event);
849 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
852 g_assert (context && id);
853 if (context->codec_id)
854 g_free (context->codec_id);
855 context->codec_id = g_strdup (id);
859 * gst_matroska_mux_video_pad_setcaps:
860 * @pad: Pad which got the caps.
863 * Setcaps function for video sink pad.
865 * Returns: #TRUE on success.
868 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
870 GstMatroskaTrackContext *context = NULL;
871 GstMatroskaTrackVideoContext *videocontext;
873 GstMatroskaPad *collect_pad;
874 GstStructure *structure;
875 const gchar *mimetype;
876 const GValue *value = NULL;
877 GstBuffer *codec_buf = NULL;
878 gint width, height, pixel_width, pixel_height;
880 gboolean interlaced = FALSE;
882 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
885 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
886 g_assert (collect_pad);
887 context = collect_pad->track;
889 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
890 videocontext = (GstMatroskaTrackVideoContext *) context;
892 /* gst -> matroska ID'ing */
893 structure = gst_caps_get_structure (caps, 0);
895 mimetype = gst_structure_get_name (structure);
897 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
899 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
901 if (!strcmp (mimetype, "video/x-theora")) {
902 /* we'll extract the details later from the theora identification header */
906 /* get general properties */
907 /* spec says it is mandatory */
908 if (!gst_structure_get_int (structure, "width", &width) ||
909 !gst_structure_get_int (structure, "height", &height))
912 videocontext->pixel_width = width;
913 videocontext->pixel_height = height;
915 /* set vp8 defaults or let user override it */
916 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
917 && (!strcmp (mimetype, "video/x-vp8")))
918 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
919 DEFAULT_PAD_FRAME_DURATION_VP8;
921 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
922 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
924 context->default_duration =
925 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
926 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
927 GST_TIME_ARGS (context->default_duration));
929 context->default_duration = 0;
931 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
932 &pixel_width, &pixel_height)) {
933 if (pixel_width > pixel_height) {
934 videocontext->display_width = width * pixel_width / pixel_height;
935 videocontext->display_height = height;
936 } else if (pixel_width < pixel_height) {
937 videocontext->display_width = width;
938 videocontext->display_height = height * pixel_height / pixel_width;
940 videocontext->display_width = 0;
941 videocontext->display_height = 0;
944 videocontext->display_width = 0;
945 videocontext->display_height = 0;
950 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
951 videocontext->fourcc = 0;
953 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
954 * data and other settings
958 /* extract codec_data, may turn out needed */
959 value = gst_structure_get_value (structure, "codec_data");
961 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
964 if (!strcmp (mimetype, "video/x-raw")) {
966 gst_matroska_mux_set_codec_id (context,
967 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
968 fstr = gst_structure_get_string (structure, "format");
969 if (fstr && strlen (fstr) == 4)
970 videocontext->fourcc = GST_STR_FOURCC (fstr);
971 } else if (!strcmp (mimetype, "image/jpeg")) {
972 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
973 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
974 ||!strcmp (mimetype, "video/x-huffyuv")
975 || !strcmp (mimetype, "video/x-divx")
976 || !strcmp (mimetype, "video/x-dv")
977 || !strcmp (mimetype, "video/x-h263")
978 || !strcmp (mimetype, "video/x-msmpeg")
979 || !strcmp (mimetype, "video/x-wmv")
980 || !strcmp (mimetype, "image/jpeg")) {
981 gst_riff_strf_vids *bih;
982 gint size = sizeof (gst_riff_strf_vids);
985 if (!strcmp (mimetype, "video/x-xvid"))
986 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
987 else if (!strcmp (mimetype, "video/x-huffyuv"))
988 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
989 else if (!strcmp (mimetype, "video/x-dv"))
990 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
991 else if (!strcmp (mimetype, "video/x-h263"))
992 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
993 else if (!strcmp (mimetype, "video/x-divx")) {
996 gst_structure_get_int (structure, "divxversion", &divxversion);
997 switch (divxversion) {
999 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1002 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1005 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1008 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1011 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1012 switch (msmpegversion) {
1014 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1017 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1023 } else if (!strcmp (mimetype, "video/x-wmv")) {
1027 fstr = gst_structure_get_string (structure, "format");
1028 if (fstr && strlen (fstr) == 4) {
1029 fourcc = GST_STR_FOURCC (fstr);
1030 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1031 if (wmvversion == 2) {
1032 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1033 } else if (wmvversion == 1) {
1034 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1035 } else if (wmvversion == 3) {
1036 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1039 } else if (!strcmp (mimetype, "image/jpeg")) {
1040 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1046 bih = g_new0 (gst_riff_strf_vids, 1);
1047 GST_WRITE_UINT32_LE (&bih->size, size);
1048 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1049 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1050 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1051 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1052 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1053 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1054 videocontext->pixel_height * 3);
1056 /* process codec private/initialization data, if any */
1058 size += gst_buffer_get_size (codec_buf);
1059 bih = g_realloc (bih, size);
1060 GST_WRITE_UINT32_LE (&bih->size, size);
1061 gst_buffer_extract (codec_buf, 0,
1062 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1065 gst_matroska_mux_set_codec_id (context,
1066 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1067 gst_matroska_mux_free_codec_priv (context);
1068 context->codec_priv = (gpointer) bih;
1069 context->codec_priv_size = size;
1070 } else if (!strcmp (mimetype, "video/x-h264")) {
1071 gst_matroska_mux_set_codec_id (context,
1072 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1073 gst_matroska_mux_free_codec_priv (context);
1074 /* Create avcC header */
1075 if (codec_buf != NULL) {
1076 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1077 context->codec_priv = g_malloc0 (context->codec_priv_size);
1078 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1080 } else if (!strcmp (mimetype, "video/x-theora")) {
1081 const GValue *streamheader;
1083 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1085 gst_matroska_mux_free_codec_priv (context);
1087 streamheader = gst_structure_get_value (structure, "streamheader");
1088 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1089 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1090 ("theora stream headers missing or malformed"));
1093 } else if (!strcmp (mimetype, "video/x-dirac")) {
1094 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1095 } else if (!strcmp (mimetype, "video/x-vp8")) {
1096 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1097 } else if (!strcmp (mimetype, "video/mpeg")) {
1100 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1101 switch (mpegversion) {
1103 gst_matroska_mux_set_codec_id (context,
1104 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1107 gst_matroska_mux_set_codec_id (context,
1108 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1111 gst_matroska_mux_set_codec_id (context,
1112 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1118 /* global headers may be in codec data */
1119 if (codec_buf != NULL) {
1120 gst_matroska_mux_free_codec_priv (context);
1121 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1122 context->codec_priv = g_malloc0 (context->codec_priv_size);
1123 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1125 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1127 /* can only make it here if preceding case verified it was version 3 */
1128 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1129 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1131 const GValue *mdpr_data;
1133 gst_structure_get_int (structure, "rmversion", &rmversion);
1134 switch (rmversion) {
1136 gst_matroska_mux_set_codec_id (context,
1137 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1140 gst_matroska_mux_set_codec_id (context,
1141 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1144 gst_matroska_mux_set_codec_id (context,
1145 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1148 gst_matroska_mux_set_codec_id (context,
1149 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1155 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1156 if (mdpr_data != NULL) {
1157 guint8 *priv_data = NULL;
1158 guint priv_data_size = 0;
1160 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1162 priv_data_size = gst_buffer_get_size (codec_data_buf);
1163 priv_data = g_malloc0 (priv_data_size);
1165 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1167 gst_matroska_mux_free_codec_priv (context);
1168 context->codec_priv = priv_data;
1169 context->codec_priv_size = priv_data_size;
1178 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1179 GST_PAD_NAME (pad), caps);
1184 /* N > 0 to expect a particular number of headers, negative if the
1185 number of headers is variable */
1187 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1188 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1190 GstBuffer **buf = NULL;
1193 guint bufi, i, offset, priv_data_size;
1195 if (streamheader == NULL)
1196 goto no_stream_headers;
1198 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1201 bufarr = g_value_peek_pointer (streamheader);
1202 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1204 if (N > 0 && bufarr->len != N)
1207 context->xiph_headers_to_skip = bufarr->len;
1209 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1210 for (i = 0; i < bufarr->len; i++) {
1211 GValue *bufval = &g_array_index (bufarr, GValue, i);
1213 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1215 goto wrong_content_type;
1218 buf[i] = g_value_peek_pointer (bufval);
1222 if (bufarr->len > 0) {
1223 for (i = 0; i < bufarr->len - 1; i++) {
1224 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1228 for (i = 0; i < bufarr->len; ++i) {
1229 priv_data_size += gst_buffer_get_size (buf[i]);
1232 priv_data = g_malloc0 (priv_data_size);
1234 priv_data[0] = bufarr->len - 1;
1237 if (bufarr->len > 0) {
1238 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1239 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1240 priv_data[offset++] = 0xff;
1242 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1246 for (i = 0; i < bufarr->len; ++i) {
1247 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1248 offset += gst_buffer_get_size (buf[i]);
1251 gst_matroska_mux_free_codec_priv (context);
1252 context->codec_priv = priv_data;
1253 context->codec_priv_size = priv_data_size;
1256 *p_buf0 = gst_buffer_ref (buf[0]);
1265 GST_WARNING ("required streamheaders missing in sink caps!");
1270 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1271 G_VALUE_TYPE_NAME (streamheader));
1276 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1281 GST_WARNING ("streamheaders array does not contain GstBuffers");
1287 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1288 GstMatroskaTrackContext * context)
1290 GstBuffer *buf0 = NULL;
1292 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1295 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1296 GST_WARNING ("First vorbis header too small, ignoring");
1298 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1299 GstMatroskaTrackAudioContext *audiocontext;
1303 gst_buffer_map (buf0, &map, GST_MAP_READ);
1304 hdr = map.data + 1 + 6 + 4;
1305 audiocontext = (GstMatroskaTrackAudioContext *) context;
1306 audiocontext->channels = GST_READ_UINT8 (hdr);
1307 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1308 gst_buffer_unmap (buf0, &map);
1313 gst_buffer_unref (buf0);
1319 theora_streamheader_to_codecdata (const GValue * streamheader,
1320 GstMatroskaTrackContext * context)
1322 GstBuffer *buf0 = NULL;
1324 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1327 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1328 GST_WARNING ("First theora header too small, ignoring");
1329 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1330 GST_WARNING ("First header not a theora identification header, ignoring");
1332 GstMatroskaTrackVideoContext *videocontext;
1333 guint fps_num, fps_denom, par_num, par_denom;
1337 gst_buffer_map (buf0, &map, GST_MAP_READ);
1338 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1340 videocontext = (GstMatroskaTrackVideoContext *) context;
1341 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1342 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1343 hdr += 3 + 3 + 1 + 1;
1344 fps_num = GST_READ_UINT32_BE (hdr);
1345 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1346 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1347 fps_denom, fps_num);
1349 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1350 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1351 if (par_num > 0 && par_num > 0) {
1352 if (par_num > par_denom) {
1353 videocontext->display_width =
1354 videocontext->pixel_width * par_num / par_denom;
1355 videocontext->display_height = videocontext->pixel_height;
1356 } else if (par_num < par_denom) {
1357 videocontext->display_width = videocontext->pixel_width;
1358 videocontext->display_height =
1359 videocontext->pixel_height * par_denom / par_num;
1361 videocontext->display_width = 0;
1362 videocontext->display_height = 0;
1365 videocontext->display_width = 0;
1366 videocontext->display_height = 0;
1370 gst_buffer_unmap (buf0, &map);
1374 gst_buffer_unref (buf0);
1380 kate_streamheader_to_codecdata (const GValue * streamheader,
1381 GstMatroskaTrackContext * context)
1383 GstBuffer *buf0 = NULL;
1385 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1388 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1389 GST_WARNING ("First kate header too small, ignoring");
1390 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1391 GST_WARNING ("First header not a kate identification header, ignoring");
1395 gst_buffer_unref (buf0);
1401 flac_streamheader_to_codecdata (const GValue * streamheader,
1402 GstMatroskaTrackContext * context)
1409 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1410 GST_WARNING ("No or invalid streamheader field in the caps");
1414 bufarr = g_value_peek_pointer (streamheader);
1415 if (bufarr->len < 2) {
1416 GST_WARNING ("Too few headers in streamheader field");
1420 context->xiph_headers_to_skip = bufarr->len + 1;
1422 bufval = &g_array_index (bufarr, GValue, 0);
1423 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1424 GST_WARNING ("streamheaders array does not contain GstBuffers");
1428 buffer = g_value_peek_pointer (bufval);
1430 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1431 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1432 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1433 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1434 GST_WARNING ("Invalid streamheader for FLAC");
1438 gst_matroska_mux_free_codec_priv (context);
1439 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1440 context->codec_priv = g_malloc (context->codec_priv_size);
1441 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1443 for (i = 1; i < bufarr->len; i++) {
1445 bufval = &g_array_index (bufarr, GValue, i);
1447 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1448 gst_matroska_mux_free_codec_priv (context);
1449 GST_WARNING ("streamheaders array does not contain GstBuffers");
1453 buffer = g_value_peek_pointer (bufval);
1455 old_size = context->codec_priv_size;
1456 context->codec_priv_size += gst_buffer_get_size (buffer);
1458 context->codec_priv = g_realloc (context->codec_priv,
1459 context->codec_priv_size);
1460 gst_buffer_extract (buffer, 0,
1461 (guint8 *) context->codec_priv + old_size, -1);
1468 speex_streamheader_to_codecdata (const GValue * streamheader,
1469 GstMatroskaTrackContext * context)
1476 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1477 GST_WARNING ("No or invalid streamheader field in the caps");
1481 bufarr = g_value_peek_pointer (streamheader);
1482 if (bufarr->len != 2) {
1483 GST_WARNING ("Too few headers in streamheader field");
1487 context->xiph_headers_to_skip = bufarr->len + 1;
1489 bufval = &g_array_index (bufarr, GValue, 0);
1490 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1491 GST_WARNING ("streamheaders array does not contain GstBuffers");
1495 buffer = g_value_peek_pointer (bufval);
1497 if (gst_buffer_get_size (buffer) < 80
1498 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1499 GST_WARNING ("Invalid streamheader for Speex");
1503 gst_matroska_mux_free_codec_priv (context);
1504 context->codec_priv_size = gst_buffer_get_size (buffer);
1505 context->codec_priv = g_malloc (context->codec_priv_size);
1506 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1508 bufval = &g_array_index (bufarr, GValue, 1);
1510 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1511 gst_matroska_mux_free_codec_priv (context);
1512 GST_WARNING ("streamheaders array does not contain GstBuffers");
1516 buffer = g_value_peek_pointer (bufval);
1518 old_size = context->codec_priv_size;
1519 context->codec_priv_size += gst_buffer_get_size (buffer);
1520 context->codec_priv = g_realloc (context->codec_priv,
1521 context->codec_priv_size);
1522 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1527 static const gchar *
1528 aac_codec_data_to_codec_id (GstBuffer * buf)
1530 const gchar *result;
1533 /* default to MAIN */
1536 if (gst_buffer_get_size (buf) >= 2) {
1537 gst_buffer_extract (buf, 0, &profile, 1);
1555 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1564 * gst_matroska_mux_audio_pad_setcaps:
1565 * @pad: Pad which got the caps.
1568 * Setcaps function for audio sink pad.
1570 * Returns: #TRUE on success.
1573 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1575 GstMatroskaTrackContext *context = NULL;
1576 GstMatroskaTrackAudioContext *audiocontext;
1577 GstMatroskaMux *mux;
1578 GstMatroskaPad *collect_pad;
1579 const gchar *mimetype;
1580 gint samplerate = 0, channels = 0;
1581 GstStructure *structure;
1582 const GValue *codec_data = NULL;
1583 GstBuffer *buf = NULL;
1584 const gchar *stream_format = NULL;
1586 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1589 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1590 g_assert (collect_pad);
1591 context = collect_pad->track;
1593 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1594 audiocontext = (GstMatroskaTrackAudioContext *) context;
1596 structure = gst_caps_get_structure (caps, 0);
1597 mimetype = gst_structure_get_name (structure);
1600 gst_structure_get_int (structure, "rate", &samplerate);
1601 gst_structure_get_int (structure, "channels", &channels);
1603 audiocontext->samplerate = samplerate;
1604 audiocontext->channels = channels;
1605 audiocontext->bitdepth = 0;
1606 context->default_duration = 0;
1608 codec_data = gst_structure_get_value (structure, "codec_data");
1610 buf = gst_value_get_buffer (codec_data);
1612 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1613 * data and other settings
1617 if (!strcmp (mimetype, "audio/mpeg")) {
1618 gint mpegversion = 0;
1620 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1621 switch (mpegversion) {
1627 gst_structure_get_int (structure, "layer", &layer);
1629 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1630 GST_WARNING_OBJECT (mux,
1631 "Unable to determine MPEG audio version, assuming 1");
1637 else if (layer == 2)
1639 else if (version == 2)
1644 context->default_duration =
1645 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1649 gst_matroska_mux_set_codec_id (context,
1650 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1653 gst_matroska_mux_set_codec_id (context,
1654 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1657 gst_matroska_mux_set_codec_id (context,
1658 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1667 stream_format = gst_structure_get_string (structure, "stream-format");
1668 /* check this is raw aac */
1669 if (stream_format) {
1670 if (strcmp (stream_format, "raw") != 0) {
1671 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1675 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1680 if (mpegversion == 2)
1682 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1683 aac_codec_data_to_codec_id (buf));
1684 else if (mpegversion == 4)
1686 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1687 aac_codec_data_to_codec_id (buf));
1689 g_assert_not_reached ();
1691 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1698 } else if (!strcmp (mimetype, "audio/x-raw")) {
1701 gst_audio_info_init (&info);
1702 if (!gst_audio_info_from_caps (&info, caps)) {
1703 GST_DEBUG_OBJECT (mux,
1704 "broken caps, rejected by gst_audio_info_from_caps");
1708 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1709 case GST_AUDIO_FORMAT_U8:
1710 case GST_AUDIO_FORMAT_S16BE:
1711 case GST_AUDIO_FORMAT_S16LE:
1712 case GST_AUDIO_FORMAT_S24BE:
1713 case GST_AUDIO_FORMAT_S24LE:
1714 case GST_AUDIO_FORMAT_S32BE:
1715 case GST_AUDIO_FORMAT_S32LE:
1716 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1717 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1720 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1721 gst_matroska_mux_set_codec_id (context,
1722 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1724 gst_matroska_mux_set_codec_id (context,
1725 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1727 case GST_AUDIO_FORMAT_F32LE:
1728 case GST_AUDIO_FORMAT_F64LE:
1729 gst_matroska_mux_set_codec_id (context,
1730 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1734 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1738 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1739 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1740 const GValue *streamheader;
1742 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1744 gst_matroska_mux_free_codec_priv (context);
1746 streamheader = gst_structure_get_value (structure, "streamheader");
1747 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1748 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1749 ("vorbis stream headers missing or malformed"));
1752 } else if (!strcmp (mimetype, "audio/x-flac")) {
1753 const GValue *streamheader;
1755 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1757 gst_matroska_mux_free_codec_priv (context);
1759 streamheader = gst_structure_get_value (structure, "streamheader");
1760 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1761 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1762 ("flac stream headers missing or malformed"));
1765 } else if (!strcmp (mimetype, "audio/x-speex")) {
1766 const GValue *streamheader;
1768 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1769 gst_matroska_mux_free_codec_priv (context);
1771 streamheader = gst_structure_get_value (structure, "streamheader");
1772 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1773 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1774 ("speex stream headers missing or malformed"));
1777 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1778 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1779 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1780 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1781 } else if (!strcmp (mimetype, "audio/x-dts")) {
1782 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1783 } else if (!strcmp (mimetype, "audio/x-tta")) {
1786 /* TTA frame duration */
1787 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1789 gst_structure_get_int (structure, "width", &width);
1790 audiocontext->bitdepth = width;
1791 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1793 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1795 const GValue *mdpr_data;
1797 gst_structure_get_int (structure, "raversion", &raversion);
1798 switch (raversion) {
1800 gst_matroska_mux_set_codec_id (context,
1801 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1804 gst_matroska_mux_set_codec_id (context,
1805 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1808 gst_matroska_mux_set_codec_id (context,
1809 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1815 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1816 if (mdpr_data != NULL) {
1817 guint8 *priv_data = NULL;
1818 guint priv_data_size = 0;
1820 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1822 priv_data_size = gst_buffer_get_size (codec_data_buf);
1823 priv_data = g_malloc0 (priv_data_size);
1825 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1827 gst_matroska_mux_free_codec_priv (context);
1829 context->codec_priv = priv_data;
1830 context->codec_priv_size = priv_data_size;
1833 } else if (!strcmp (mimetype, "audio/x-wma")
1834 || !strcmp (mimetype, "audio/x-alaw")
1835 || !strcmp (mimetype, "audio/x-mulaw")) {
1837 guint codec_priv_size;
1842 if (samplerate == 0 || channels == 0) {
1843 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1847 if (!strcmp (mimetype, "audio/x-wma")) {
1851 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1852 || !gst_structure_get_int (structure, "block_align", &block_align)
1853 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1854 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1859 switch (wmaversion) {
1861 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1864 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1867 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1870 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1874 if (gst_structure_get_int (structure, "depth", &depth))
1875 audiocontext->bitdepth = depth;
1876 } else if (!strcmp (mimetype, "audio/x-alaw")
1877 || !strcmp (mimetype, "audio/x-mulaw")) {
1878 audiocontext->bitdepth = 8;
1879 if (!strcmp (mimetype, "audio/x-alaw"))
1880 format = GST_RIFF_WAVE_FORMAT_ALAW;
1882 format = GST_RIFF_WAVE_FORMAT_MULAW;
1884 block_align = channels;
1885 bitrate = block_align * samplerate;
1887 g_assert (format != 0);
1889 codec_priv_size = WAVEFORMATEX_SIZE;
1891 codec_priv_size += gst_buffer_get_size (buf);
1893 /* serialize waveformatex structure */
1894 codec_priv = g_malloc0 (codec_priv_size);
1895 GST_WRITE_UINT16_LE (codec_priv, format);
1896 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1897 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1898 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1899 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1900 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1902 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1904 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1906 /* process codec private/initialization data, if any */
1908 gst_buffer_extract (buf, 0,
1909 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1912 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1913 gst_matroska_mux_free_codec_priv (context);
1914 context->codec_priv = (gpointer) codec_priv;
1915 context->codec_priv_size = codec_priv_size;
1923 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1924 GST_PAD_NAME (pad), caps);
1929 /* we probably don't have the data at start,
1930 * so have to reserve (a maximum) space to write this at the end.
1931 * bit spacy, but some formats can hold quite some */
1932 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1935 * gst_matroska_mux_subtitle_pad_setcaps:
1936 * @pad: Pad which got the caps.
1939 * Setcaps function for subtitle sink pad.
1941 * Returns: #TRUE on success.
1944 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1946 /* There is now (at least) one such alement (kateenc), and I'm going
1947 to handle it here and claim it works when it can be piped back
1948 through GStreamer and VLC */
1950 GstMatroskaTrackContext *context = NULL;
1951 GstMatroskaTrackSubtitleContext *scontext;
1952 GstMatroskaMux *mux;
1953 GstMatroskaPad *collect_pad;
1954 const gchar *mimetype;
1955 GstStructure *structure;
1956 const GValue *value = NULL;
1957 GstBuffer *buf = NULL;
1958 gboolean ret = TRUE;
1960 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1963 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1964 g_assert (collect_pad);
1965 context = collect_pad->track;
1967 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1968 scontext = (GstMatroskaTrackSubtitleContext *) context;
1970 structure = gst_caps_get_structure (caps, 0);
1971 mimetype = gst_structure_get_name (structure);
1974 scontext->check_utf8 = 1;
1975 scontext->invalid_utf8 = 0;
1976 context->default_duration = 0;
1978 if (!strcmp (mimetype, "subtitle/x-kate")) {
1979 const GValue *streamheader;
1981 gst_matroska_mux_set_codec_id (context,
1982 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1984 gst_matroska_mux_free_codec_priv (context);
1986 streamheader = gst_structure_get_value (structure, "streamheader");
1987 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1988 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1989 ("kate stream headers missing or malformed"));
1993 } else if (!strcmp (mimetype, "text/plain")) {
1994 gst_matroska_mux_set_codec_id (context,
1995 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
1996 } else if (!strcmp (mimetype, "application/x-ssa")) {
1997 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
1998 } else if (!strcmp (mimetype, "application/x-ass")) {
1999 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2000 } else if (!strcmp (mimetype, "application/x-usf")) {
2001 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2002 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2003 gst_matroska_mux_set_codec_id (context,
2004 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2010 /* maybe some private data, e.g. vobsub */
2011 value = gst_structure_get_value (structure, "codec_data");
2013 buf = gst_value_get_buffer (value);
2016 guint8 *priv_data = NULL;
2018 gst_buffer_map (buf, &map, GST_MAP_READ);
2020 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2021 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2022 " exceeded maximum (%d); discarding", pad,
2023 SUBTITLE_MAX_CODEC_PRIVATE);
2024 gst_buffer_unmap (buf, &map);
2028 gst_matroska_mux_free_codec_priv (context);
2030 priv_data = g_malloc0 (map.size);
2031 memcpy (priv_data, map.data, map.size);
2032 context->codec_priv = priv_data;
2033 context->codec_priv_size = map.size;
2034 gst_buffer_unmap (buf, &map);
2037 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2038 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2047 * gst_matroska_mux_request_new_pad:
2048 * @element: #GstMatroskaMux.
2049 * @templ: #GstPadTemplate.
2050 * @pad_name: New pad name.
2052 * Request pad function for sink templates.
2054 * Returns: New #GstPad.
2057 gst_matroska_mux_request_new_pad (GstElement * element,
2058 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2060 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2061 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2062 GstMatroskaPad *collect_pad;
2063 GstMatroskamuxPad *newpad;
2065 const gchar *pad_name = NULL;
2066 GstMatroskaCapsFunc capsfunc = NULL;
2067 GstMatroskaTrackContext *context = NULL;
2069 gboolean locked = TRUE;
2072 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2073 /* don't mix named and unnamed pads, if the pad already exists we fail when
2074 * trying to add it */
2075 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2076 pad_name = req_name;
2078 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2081 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2082 context = (GstMatroskaTrackContext *)
2083 g_new0 (GstMatroskaTrackAudioContext, 1);
2084 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2085 context->name = g_strdup ("Audio");
2086 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2087 /* don't mix named and unnamed pads, if the pad already exists we fail when
2088 * trying to add it */
2089 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2090 pad_name = req_name;
2092 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2095 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2096 context = (GstMatroskaTrackContext *)
2097 g_new0 (GstMatroskaTrackVideoContext, 1);
2098 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2099 context->name = g_strdup ("Video");
2100 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2101 /* don't mix named and unnamed pads, if the pad already exists we fail when
2102 * trying to add it */
2103 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2104 pad_name = req_name;
2106 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2109 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2110 context = (GstMatroskaTrackContext *)
2111 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2112 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2113 context->name = g_strdup ("Subtitle");
2114 /* setcaps may only provide proper one a lot later */
2115 id = g_strdup ("S_SUB_UNKNOWN");
2118 GST_WARNING_OBJECT (mux, "This is not our template!");
2122 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2123 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2126 gst_matroskamux_pad_init (newpad);
2127 collect_pad = (GstMatroskaPad *)
2128 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2129 sizeof (GstMatroskamuxPad),
2130 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2132 collect_pad->track = context;
2133 gst_matroska_pad_reset (collect_pad, FALSE);
2134 collect_pad->track->codec_id = id;
2136 collect_pad->capsfunc = capsfunc;
2137 gst_pad_set_active (GST_PAD (newpad), TRUE);
2138 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2139 goto pad_add_failed;
2143 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2145 return GST_PAD (newpad);
2150 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2151 gst_object_unref (newpad);
2157 * gst_matroska_mux_release_pad:
2158 * @element: #GstMatroskaMux.
2159 * @pad: Pad to release.
2161 * Release a previously requested pad.
2164 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2166 GstMatroskaMux *mux;
2169 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2171 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2172 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2173 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2175 if (cdata->pad == pad) {
2176 GstClockTime min_dur; /* observed minimum duration */
2178 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2179 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2180 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2181 if (collect_pad->duration < min_dur)
2182 collect_pad->duration = min_dur;
2185 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2186 mux->duration < collect_pad->duration)
2187 mux->duration = collect_pad->duration;
2193 gst_collect_pads2_remove_pad (mux->collect, pad);
2194 if (gst_element_remove_pad (element, pad))
2200 * gst_matroska_mux_track_header:
2201 * @mux: #GstMatroskaMux
2202 * @context: Tack context.
2204 * Write a track header.
2207 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2208 GstMatroskaTrackContext * context)
2210 GstEbmlWrite *ebml = mux->ebml_write;
2213 /* TODO: check if everything necessary is written and check default values */
2215 /* track type goes before the type-specific stuff */
2216 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2217 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2219 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2220 gst_matroska_mux_create_uid ());
2221 if (context->default_duration) {
2222 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2223 context->default_duration);
2225 if (context->language) {
2226 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2230 /* FIXME: until we have a nice way of getting the codecname
2231 * out of the caps, I'm not going to enable this. Too much
2232 * (useless, double, boring) work... */
2233 /* TODO: Use value from tags if any */
2234 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2235 context->codec_name); */
2236 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2238 /* type-specific stuff */
2239 switch (context->type) {
2240 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2241 GstMatroskaTrackVideoContext *videocontext =
2242 (GstMatroskaTrackVideoContext *) context;
2244 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2245 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2246 videocontext->pixel_width);
2247 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2248 videocontext->pixel_height);
2249 if (videocontext->display_width && videocontext->display_height) {
2250 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2251 videocontext->display_width);
2252 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2253 videocontext->display_height);
2255 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2256 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2257 if (videocontext->fourcc) {
2258 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2260 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2261 (gpointer) & fcc_le, 4);
2263 gst_ebml_write_master_finish (ebml, master);
2268 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2269 GstMatroskaTrackAudioContext *audiocontext =
2270 (GstMatroskaTrackAudioContext *) context;
2272 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2273 if (audiocontext->samplerate != 8000)
2274 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2275 audiocontext->samplerate);
2276 if (audiocontext->channels != 1)
2277 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2278 audiocontext->channels);
2279 if (audiocontext->bitdepth) {
2280 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2281 audiocontext->bitdepth);
2283 gst_ebml_write_master_finish (ebml, master);
2288 /* this is what we write for now and must be filled
2289 * and remainder void'ed later on */
2290 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2292 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2295 context->pos = ebml->pos;
2296 /* CodecID is mandatory ... */
2297 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2299 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2300 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2301 SUBTITLE_MAX_CODEC_PRIVATE);
2303 /* real data has to be written at finish */
2307 /* doesn't need type-specific data */
2311 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2312 if (context->codec_priv)
2313 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2314 context->codec_priv, context->codec_priv_size);
2319 * gst_matroska_mux_start:
2320 * @mux: #GstMatroskaMux
2322 * Start a new matroska file (write headers etc...)
2325 gst_matroska_mux_start (GstMatroskaMux * mux)
2327 GstEbmlWrite *ebml = mux->ebml_write;
2328 const gchar *doctype;
2329 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2330 GST_MATROSKA_ID_TRACKS,
2331 GST_MATROSKA_ID_CUES,
2332 GST_MATROSKA_ID_TAGS,
2335 guint64 master, child;
2339 GstClockTime duration = 0;
2340 guint32 segment_uid[4];
2341 GTimeVal time = { 0, 0 };
2343 /* if not streaming, check if downstream is seekable */
2344 if (!mux->streamable) {
2348 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2349 if (gst_pad_peer_query (mux->srcpad, query)) {
2350 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2351 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2353 /* have to assume seeking is supported if query not handled downstream */
2354 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2358 mux->streamable = TRUE;
2359 g_object_notify (G_OBJECT (mux), "streamable");
2360 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2361 "streamable=false. Will ignore that and create streamable output "
2364 gst_query_unref (query);
2367 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2368 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2370 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2372 /* we start with a EBML header */
2373 doctype = mux->doctype;
2374 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2375 doctype, mux->doctype_version);
2376 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2378 /* the rest of the header is cached */
2379 gst_ebml_write_set_cache (ebml, 0x1000);
2381 /* start a segment */
2383 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2384 mux->segment_master = ebml->pos;
2386 if (!mux->streamable) {
2387 /* seekhead (table of contents) - we set the positions later */
2388 mux->seekhead_pos = ebml->pos;
2389 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2390 for (i = 0; seekhead_id[i] != 0; i++) {
2391 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2392 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2393 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2394 gst_ebml_write_master_finish (ebml, child);
2396 gst_ebml_write_master_finish (ebml, master);
2399 if (mux->streamable) {
2400 const GstTagList *tags;
2403 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2405 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2406 guint64 master_tags, master_tag;
2408 GST_DEBUG_OBJECT (mux, "Writing tags");
2410 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2411 mux->tags_pos = ebml->pos;
2412 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2413 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2414 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2415 gst_ebml_write_master_finish (ebml, master_tag);
2416 gst_ebml_write_master_finish (ebml, master_tags);
2421 mux->info_pos = ebml->pos;
2422 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2423 for (i = 0; i < 4; i++) {
2424 segment_uid[i] = g_random_int ();
2426 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2427 (guint8 *) segment_uid, 16);
2428 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2429 mux->duration_pos = ebml->pos;
2431 if (!mux->streamable) {
2432 for (collected = mux->collect->data; collected;
2433 collected = g_slist_next (collected)) {
2434 GstMatroskaPad *collect_pad;
2436 gint64 trackduration;
2438 collect_pad = (GstMatroskaPad *) collected->data;
2439 thepad = collect_pad->collect.pad;
2441 /* Query the total length of the track. */
2442 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2443 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2444 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2445 GST_TIME_ARGS (trackduration));
2446 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2447 duration = (GstClockTime) trackduration;
2451 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2452 gst_guint64_to_gdouble (duration) /
2453 gst_guint64_to_gdouble (mux->time_scale));
2455 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2456 "GStreamer plugin version " PACKAGE_VERSION);
2457 if (mux->writing_app && mux->writing_app[0]) {
2458 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2460 g_get_current_time (&time);
2461 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2462 gst_ebml_write_master_finish (ebml, master);
2465 mux->tracks_pos = ebml->pos;
2466 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2468 for (collected = mux->collect->data; collected;
2469 collected = g_slist_next (collected)) {
2470 GstMatroskaPad *collect_pad;
2473 collect_pad = (GstMatroskaPad *) collected->data;
2474 thepad = collect_pad->collect.pad;
2476 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2477 collect_pad->track->codec_id != 0) {
2478 collect_pad->track->num = tracknum++;
2479 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2480 gst_matroska_mux_track_header (mux, collect_pad->track);
2481 gst_ebml_write_master_finish (ebml, child);
2482 /* some remaining pad/track setup */
2483 collect_pad->default_duration_scaled =
2484 gst_util_uint64_scale (collect_pad->track->default_duration,
2485 1, mux->time_scale);
2488 gst_ebml_write_master_finish (ebml, master);
2490 /* lastly, flush the cache */
2491 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2495 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2498 /* TODO: more sensible tag mappings */
2501 const gchar *matroska_tagname;
2502 const gchar *gstreamer_tagname;
2506 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2507 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2508 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2509 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2510 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2511 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2512 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2513 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2514 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2515 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2516 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2517 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2518 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2519 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2520 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2522 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2524 guint64 simpletag_master;
2526 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2527 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2528 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2530 if (strcmp (tagname_gst, tag) == 0) {
2531 GValue src = { 0, };
2534 if (!gst_tag_list_copy_value (&src, list, tag))
2536 if ((dest = gst_value_serialize (&src))) {
2538 simpletag_master = gst_ebml_write_master_start (ebml,
2539 GST_MATROSKA_ID_SIMPLETAG);
2540 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2541 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2542 gst_ebml_write_master_finish (ebml, simpletag_master);
2545 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2547 g_value_unset (&src);
2555 * gst_matroska_mux_finish:
2556 * @mux: #GstMatroskaMux
2558 * Finish a new matroska file (write index etc...)
2561 gst_matroska_mux_finish (GstMatroskaMux * mux)
2563 GstEbmlWrite *ebml = mux->ebml_write;
2565 guint64 duration = 0;
2567 const GstTagList *tags;
2569 /* finish last cluster */
2571 gst_ebml_write_master_finish (ebml, mux->cluster);
2575 if (mux->index != NULL) {
2577 guint64 master, pointentry_master, trackpos_master;
2579 mux->cues_pos = ebml->pos;
2580 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2581 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2583 for (n = 0; n < mux->num_indexes; n++) {
2584 GstMatroskaIndex *idx = &mux->index[n];
2586 pointentry_master = gst_ebml_write_master_start (ebml,
2587 GST_MATROSKA_ID_POINTENTRY);
2588 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2589 idx->time / mux->time_scale);
2590 trackpos_master = gst_ebml_write_master_start (ebml,
2591 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2592 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2593 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2594 idx->pos - mux->segment_master);
2595 gst_ebml_write_master_finish (ebml, trackpos_master);
2596 gst_ebml_write_master_finish (ebml, pointentry_master);
2599 gst_ebml_write_master_finish (ebml, master);
2600 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2604 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2606 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2607 guint64 master_tags, master_tag;
2609 GST_DEBUG_OBJECT (mux, "Writing tags");
2611 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2612 mux->tags_pos = ebml->pos;
2613 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2614 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2615 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2616 gst_ebml_write_master_finish (ebml, master_tag);
2617 gst_ebml_write_master_finish (ebml, master_tags);
2620 /* update seekhead. We know that:
2621 * - a seekhead contains 4 entries.
2622 * - order of entries is as above.
2623 * - a seekhead has a 4-byte header + 8-byte length
2624 * - each entry is 2-byte master, 2-byte ID pointer,
2625 * 2-byte length pointer, all 8/1-byte length, 4-
2626 * byte ID and 8-byte length pointer, where the
2627 * length pointer starts at 20.
2628 * - all entries are local to the segment (so pos - segment_master).
2629 * - so each entry is at 12 + 20 + num * 28. */
2630 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2631 mux->info_pos - mux->segment_master);
2632 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2633 mux->tracks_pos - mux->segment_master);
2634 if (mux->index != NULL) {
2635 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2636 mux->cues_pos - mux->segment_master);
2639 guint64 my_pos = ebml->pos;
2641 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2642 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2643 gst_ebml_write_seek (ebml, my_pos);
2646 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2647 mux->tags_pos - mux->segment_master);
2650 guint64 my_pos = ebml->pos;
2652 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2653 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2654 gst_ebml_write_seek (ebml, my_pos);
2658 * - first get the overall duration
2659 * (a released track may have left a duration in here)
2660 * - write some track header data for subtitles
2662 duration = mux->duration;
2664 for (collected = mux->collect->data; collected;
2665 collected = g_slist_next (collected)) {
2666 GstMatroskaPad *collect_pad;
2667 GstClockTime min_duration; /* observed minimum duration */
2668 GstMatroskaTrackContext *context;
2669 gint voidleft = 0, fill = 0;
2672 collect_pad = (GstMatroskaPad *) collected->data;
2673 context = collect_pad->track;
2675 GST_DEBUG_OBJECT (mux,
2676 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2677 " end ts %" GST_TIME_FORMAT, collect_pad,
2678 GST_TIME_ARGS (collect_pad->start_ts),
2679 GST_TIME_ARGS (collect_pad->end_ts));
2681 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2682 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2684 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2685 if (collect_pad->duration < min_duration)
2686 collect_pad->duration = min_duration;
2687 GST_DEBUG_OBJECT (collect_pad,
2688 "final track duration: %" GST_TIME_FORMAT,
2689 GST_TIME_ARGS (collect_pad->duration));
2692 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2693 duration < collect_pad->duration)
2694 duration = collect_pad->duration;
2696 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2700 /* write subtitle type and possible private data */
2701 gst_ebml_write_seek (ebml, context->pos);
2702 /* complex way to write ascii to account for extra filling */
2703 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2704 strcpy (codec_id, context->codec_id);
2705 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2706 codec_id, strlen (context->codec_id) + 1 + fill);
2708 if (context->codec_priv)
2709 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2710 context->codec_priv, context->codec_priv_size);
2711 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2712 /* void'ify; sigh, variable sized length field */
2713 if (voidleft == 1) {
2716 } else if (voidleft && voidleft <= 128)
2717 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2718 else if (voidleft >= 130)
2719 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2720 else if (voidleft == 129) {
2721 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2722 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2726 /* seek back (optional, but do anyway) */
2727 gst_ebml_write_seek (ebml, pos);
2729 /* update duration */
2730 if (duration != 0) {
2731 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2732 GST_TIME_ARGS (duration));
2733 pos = mux->ebml_write->pos;
2734 gst_ebml_write_seek (ebml, mux->duration_pos);
2735 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2736 gst_guint64_to_gdouble (duration) /
2737 gst_guint64_to_gdouble (mux->time_scale));
2738 gst_ebml_write_seek (ebml, pos);
2741 guint64 my_pos = ebml->pos;
2743 gst_ebml_write_seek (ebml, mux->duration_pos);
2744 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2745 gst_ebml_write_seek (ebml, my_pos);
2747 GST_DEBUG_OBJECT (mux, "finishing segment");
2748 /* finish segment - this also writes element length */
2749 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2753 * gst_matroska_mux_buffer_header:
2754 * @track: Track context.
2755 * @relative_timestamp: relative timestamp of the buffer
2756 * @flags: Buffer flags.
2758 * Create a buffer containing buffer header.
2760 * Returns: New buffer.
2763 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2764 gint16 relative_timestamp, int flags)
2767 guint8 *data = g_malloc (4);
2769 hdr = gst_buffer_new_wrapped (data, 4);
2770 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2771 data[0] = track->num | 0x80;
2772 /* time relative to clustertime */
2773 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
2781 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2782 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2783 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2786 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2787 GstMatroskaPad * collect_pad, GstBuffer * buf)
2789 GstMatroskaTrackVideoContext *ctx =
2790 (GstMatroskaTrackVideoContext *) collect_pad->track;
2795 guint32 next_parse_offset;
2796 GstBuffer *ret = NULL;
2797 gboolean is_muxing_unit = FALSE;
2799 gst_buffer_map (buf, &map, GST_MAP_READ);
2804 gst_buffer_unmap (buf, &map);
2805 gst_buffer_unref (buf);
2809 /* Check if this buffer contains a picture or end-of-sequence packet */
2810 while (size >= 13) {
2811 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2812 gst_buffer_unmap (buf, &map);
2813 gst_buffer_unref (buf);
2817 parse_code = GST_READ_UINT8 (data + 4);
2818 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2819 if (ctx->dirac_unit) {
2820 gst_buffer_unref (ctx->dirac_unit);
2821 ctx->dirac_unit = NULL;
2823 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2824 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2825 is_muxing_unit = TRUE;
2829 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2831 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2834 data += next_parse_offset;
2835 size -= next_parse_offset;
2838 if (ctx->dirac_unit)
2839 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2841 ctx->dirac_unit = gst_buffer_ref (buf);
2843 gst_buffer_unmap (buf, &map);
2845 if (is_muxing_unit) {
2846 ret = gst_buffer_make_writable (ctx->dirac_unit);
2847 ctx->dirac_unit = NULL;
2848 gst_buffer_copy_into (ret, buf,
2849 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2850 gst_buffer_unref (buf);
2852 gst_buffer_unref (buf);
2860 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2864 GValue streamheader = { 0 };
2865 GValue bufval = { 0 };
2866 GstBuffer *streamheader_buffer;
2867 GstEbmlWrite *ebml = mux->ebml_write;
2869 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2870 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2871 caps = gst_caps_new_empty_simple ("video/webm");
2873 caps = gst_caps_new_empty_simple ("video/x-matroska");
2875 s = gst_caps_get_structure (caps, 0);
2876 g_value_init (&streamheader, GST_TYPE_ARRAY);
2877 g_value_init (&bufval, GST_TYPE_BUFFER);
2878 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
2879 gst_value_set_buffer (&bufval, streamheader_buffer);
2880 gst_value_array_append_value (&streamheader, &bufval);
2881 g_value_unset (&bufval);
2882 gst_structure_set_value (s, "streamheader", &streamheader);
2883 g_value_unset (&streamheader);
2884 gst_caps_replace (&ebml->caps, caps);
2885 gst_buffer_unref (streamheader_buffer);
2886 gst_caps_unref (caps);
2890 * gst_matroska_mux_write_data:
2891 * @mux: #GstMatroskaMux
2892 * @collect_pad: #GstMatroskaPad with the data
2894 * Write collected data (called from gst_matroska_mux_collected).
2896 * Returns: Result of the gst_pad_push issued to write the data.
2898 static GstFlowReturn
2899 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2902 GstEbmlWrite *ebml = mux->ebml_write;
2905 gboolean write_duration;
2906 gint16 relative_timestamp;
2907 gint64 relative_timestamp64;
2908 guint64 block_duration;
2909 gboolean is_video_keyframe = FALSE;
2910 GstMatroskamuxPad *pad;
2913 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2915 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2916 if (collect_pad->track->xiph_headers_to_skip > 0) {
2917 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2918 gst_buffer_unref (buf);
2919 --collect_pad->track->xiph_headers_to_skip;
2923 /* for dirac we have to queue up everything up to a picture unit */
2924 if (collect_pad->track->codec_id != NULL &&
2925 strcmp (collect_pad->track->codec_id,
2926 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2927 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2932 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2933 * this would wreak havoc with time stored in matroska file */
2934 /* TODO: maybe calculate a timestamp by using the previous timestamp
2935 * and default duration */
2936 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2937 GST_WARNING_OBJECT (collect_pad->collect.pad,
2938 "Invalid buffer timestamp; dropping buffer");
2939 gst_buffer_unref (buf);
2943 /* set the timestamp for outgoing buffers */
2944 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2946 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2947 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2948 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2949 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2950 is_video_keyframe = TRUE;
2954 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2955 * or when we may be reaching the limit of the relative timestamp */
2956 if (mux->cluster_time +
2957 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2958 || is_video_keyframe || mux->force_key_unit_event) {
2959 if (!mux->streamable)
2960 gst_ebml_write_master_finish (ebml, mux->cluster);
2962 /* Forward the GstForceKeyUnit event after finishing the cluster */
2963 if (mux->force_key_unit_event) {
2964 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2965 mux->force_key_unit_event = NULL;
2968 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2969 mux->cluster_pos = ebml->pos;
2970 gst_ebml_write_set_cache (ebml, 0x20);
2972 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2973 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2974 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2976 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2977 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2979 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2980 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2981 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2982 mux->prev_cluster_size);
2987 mux->cluster_pos = ebml->pos;
2988 gst_ebml_write_set_cache (ebml, 0x20);
2989 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2990 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2991 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2992 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2993 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2996 /* update duration of this track */
2997 if (GST_BUFFER_DURATION_IS_VALID (buf))
2998 collect_pad->duration += GST_BUFFER_DURATION (buf);
3000 /* We currently write index entries for all video tracks or for the audio
3001 * track in a single-track audio file. This could be improved by keeping the
3002 * index only for the *first* video track. */
3004 /* TODO: index is useful for every track, should contain the number of
3005 * the block in the cluster which contains the timestamp, should also work
3006 * for files with multiple audio tracks.
3008 if (!mux->streamable &&
3009 (is_video_keyframe ||
3010 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3011 (mux->num_streams == 1)))) {
3014 if (mux->min_index_interval != 0) {
3015 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3016 if (mux->index[last_idx].track == collect_pad->track->num)
3021 if (last_idx < 0 || mux->min_index_interval == 0 ||
3022 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3023 >= mux->min_index_interval)) {
3024 GstMatroskaIndex *idx;
3026 if (mux->num_indexes % 32 == 0) {
3027 mux->index = g_renew (GstMatroskaIndex, mux->index,
3028 mux->num_indexes + 32);
3030 idx = &mux->index[mux->num_indexes++];
3032 idx->pos = mux->cluster_pos;
3033 idx->time = GST_BUFFER_TIMESTAMP (buf);
3034 idx->track = collect_pad->track->num;
3038 /* Check if the duration differs from the default duration. */
3039 write_duration = FALSE;
3041 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3042 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3043 1, mux->time_scale);
3045 /* small difference should be ok. */
3046 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3047 block_duration < collect_pad->default_duration_scaled - 1) {
3048 write_duration = TRUE;
3052 /* write the block, for doctype v2 use SimpleBlock if possible
3053 * one slice (*breath*).
3054 * FIXME: Need to do correct lacing! */
3055 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3056 if (relative_timestamp64 >= 0) {
3057 /* round the timestamp */
3058 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3060 /* round the timestamp */
3061 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3063 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3065 if (mux->doctype_version > 1 && !write_duration) {
3067 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3070 gst_matroska_mux_create_buffer_header (collect_pad->track,
3071 relative_timestamp, flags);
3072 gst_ebml_write_set_cache (ebml, 0x40);
3073 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3074 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3075 gst_ebml_write_buffer (ebml, hdr);
3076 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3077 gst_ebml_write_buffer (ebml, buf);
3079 return gst_ebml_last_write_result (ebml);
3081 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3082 /* write and call order slightly unnatural,
3083 * but avoids seek and minizes pushing */
3084 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3086 gst_matroska_mux_create_buffer_header (collect_pad->track,
3087 relative_timestamp, 0);
3089 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3090 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3091 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3092 gst_ebml_write_buffer (ebml, hdr);
3093 gst_ebml_write_master_finish_full (ebml, blockgroup,
3094 gst_buffer_get_size (buf));
3095 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3096 gst_ebml_write_buffer (ebml, buf);
3098 return gst_ebml_last_write_result (ebml);
3103 * gst_matroska_mux_handle_buffer:
3104 * @pads: #GstCollectPads2
3105 * @uuser_data: #GstMatroskaMux
3107 * Collectpads callback.
3109 * Returns: #GstFlowReturn
3111 static GstFlowReturn
3112 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3113 GstBuffer * buf, gpointer user_data)
3115 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3116 GstEbmlWrite *ebml = mux->ebml_write;
3117 GstMatroskaPad *best;
3118 GstFlowReturn ret = GST_FLOW_OK;
3120 GST_DEBUG_OBJECT (mux, "Collected pads");
3122 /* start with a header */
3123 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3124 if (mux->collect->data == NULL) {
3125 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3126 ("No input streams configured"));
3127 return GST_FLOW_ERROR;
3129 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3130 gst_ebml_start_streamheader (ebml);
3131 gst_matroska_mux_start (mux);
3132 gst_matroska_mux_stop_streamheader (mux);
3133 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3136 /* provided with stream to write from */
3137 best = (GstMatroskaPad *) data;
3139 /* if there is no best pad, we have reached EOS */
3141 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3142 if (!mux->streamable) {
3143 gst_matroska_mux_finish (mux);
3145 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3147 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3152 /* if we have a best stream, should also have a buffer */
3155 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3156 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3157 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3158 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3160 /* make note of first and last encountered timestamps, so we can calculate
3161 * the actual duration later when we send an updated header on eos */
3162 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3163 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3164 GstClockTime end_ts = start_ts;
3166 if (GST_BUFFER_DURATION_IS_VALID (buf))
3167 end_ts += GST_BUFFER_DURATION (buf);
3168 else if (best->track->default_duration)
3169 end_ts += best->track->default_duration;
3171 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3172 best->end_ts = end_ts;
3174 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3175 start_ts < best->start_ts))
3176 best->start_ts = start_ts;
3179 /* write one buffer */
3180 ret = gst_matroska_mux_write_data (mux, best, buf);
3188 * gst_matroska_mux_change_state:
3189 * @element: #GstMatroskaMux
3190 * @transition: State change transition.
3192 * Change the muxer state.
3194 * Returns: #GstStateChangeReturn
3196 static GstStateChangeReturn
3197 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3199 GstStateChangeReturn ret;
3200 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3202 switch (transition) {
3203 case GST_STATE_CHANGE_NULL_TO_READY:
3205 case GST_STATE_CHANGE_READY_TO_PAUSED:
3206 gst_collect_pads2_start (mux->collect);
3208 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3210 case GST_STATE_CHANGE_PAUSED_TO_READY:
3211 gst_collect_pads2_stop (mux->collect);
3217 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3219 switch (transition) {
3220 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3222 case GST_STATE_CHANGE_PAUSED_TO_READY:
3223 gst_matroska_mux_reset (GST_ELEMENT (mux));
3225 case GST_STATE_CHANGE_READY_TO_NULL:
3235 gst_matroska_mux_set_property (GObject * object,
3236 guint prop_id, const GValue * value, GParamSpec * pspec)
3238 GstMatroskaMux *mux;
3240 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3241 mux = GST_MATROSKA_MUX (object);
3244 case ARG_WRITING_APP:
3245 if (!g_value_get_string (value)) {
3246 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3249 g_free (mux->writing_app);
3250 mux->writing_app = g_value_dup_string (value);
3252 case ARG_DOCTYPE_VERSION:
3253 mux->doctype_version = g_value_get_int (value);
3255 case ARG_MIN_INDEX_INTERVAL:
3256 mux->min_index_interval = g_value_get_int64 (value);
3258 case ARG_STREAMABLE:
3259 mux->streamable = g_value_get_boolean (value);
3262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3268 gst_matroska_mux_get_property (GObject * object,
3269 guint prop_id, GValue * value, GParamSpec * pspec)
3271 GstMatroskaMux *mux;
3273 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3274 mux = GST_MATROSKA_MUX (object);
3277 case ARG_WRITING_APP:
3278 g_value_set_string (value, mux->writing_app);
3280 case ARG_DOCTYPE_VERSION:
3281 g_value_set_int (value, mux->doctype_version);
3283 case ARG_MIN_INDEX_INTERVAL:
3284 g_value_set_int64 (value, mux->min_index_interval);
3286 case ARG_STREAMABLE:
3287 g_value_set_boolean (value, mux->streamable);
3290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);