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 (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2344 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2346 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2348 /* we start with a EBML header */
2349 doctype = mux->doctype;
2350 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2351 doctype, mux->doctype_version);
2352 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2354 /* the rest of the header is cached */
2355 gst_ebml_write_set_cache (ebml, 0x1000);
2357 /* start a segment */
2359 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2360 mux->segment_master = ebml->pos;
2362 if (!mux->streamable) {
2363 /* seekhead (table of contents) - we set the positions later */
2364 mux->seekhead_pos = ebml->pos;
2365 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2366 for (i = 0; seekhead_id[i] != 0; i++) {
2367 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2368 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2369 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2370 gst_ebml_write_master_finish (ebml, child);
2372 gst_ebml_write_master_finish (ebml, master);
2375 if (mux->streamable) {
2376 const GstTagList *tags;
2379 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2381 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2382 guint64 master_tags, master_tag;
2384 GST_DEBUG_OBJECT (mux, "Writing tags");
2386 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2387 mux->tags_pos = ebml->pos;
2388 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2389 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2390 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2391 gst_ebml_write_master_finish (ebml, master_tag);
2392 gst_ebml_write_master_finish (ebml, master_tags);
2397 mux->info_pos = ebml->pos;
2398 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2399 for (i = 0; i < 4; i++) {
2400 segment_uid[i] = g_random_int ();
2402 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2403 (guint8 *) segment_uid, 16);
2404 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2405 mux->duration_pos = ebml->pos;
2407 if (!mux->streamable) {
2408 for (collected = mux->collect->data; collected;
2409 collected = g_slist_next (collected)) {
2410 GstMatroskaPad *collect_pad;
2412 gint64 trackduration;
2414 collect_pad = (GstMatroskaPad *) collected->data;
2415 thepad = collect_pad->collect.pad;
2417 /* Query the total length of the track. */
2418 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2419 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2420 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2421 GST_TIME_ARGS (trackduration));
2422 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2423 duration = (GstClockTime) trackduration;
2427 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2428 gst_guint64_to_gdouble (duration) /
2429 gst_guint64_to_gdouble (mux->time_scale));
2431 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2432 "GStreamer plugin version " PACKAGE_VERSION);
2433 if (mux->writing_app && mux->writing_app[0]) {
2434 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2436 g_get_current_time (&time);
2437 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2438 gst_ebml_write_master_finish (ebml, master);
2441 mux->tracks_pos = ebml->pos;
2442 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2444 for (collected = mux->collect->data; collected;
2445 collected = g_slist_next (collected)) {
2446 GstMatroskaPad *collect_pad;
2449 collect_pad = (GstMatroskaPad *) collected->data;
2450 thepad = collect_pad->collect.pad;
2452 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2453 collect_pad->track->codec_id != 0) {
2454 collect_pad->track->num = tracknum++;
2455 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2456 gst_matroska_mux_track_header (mux, collect_pad->track);
2457 gst_ebml_write_master_finish (ebml, child);
2458 /* some remaining pad/track setup */
2459 collect_pad->default_duration_scaled =
2460 gst_util_uint64_scale (collect_pad->track->default_duration,
2461 1, mux->time_scale);
2464 gst_ebml_write_master_finish (ebml, master);
2466 /* lastly, flush the cache */
2467 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2471 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2474 /* TODO: more sensible tag mappings */
2477 const gchar *matroska_tagname;
2478 const gchar *gstreamer_tagname;
2482 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2483 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2484 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2485 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2486 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2487 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2488 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2489 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2490 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2491 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2492 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2493 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2494 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2495 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2496 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2498 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2500 guint64 simpletag_master;
2502 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2503 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2504 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2506 if (strcmp (tagname_gst, tag) == 0) {
2507 GValue src = { 0, };
2510 if (!gst_tag_list_copy_value (&src, list, tag))
2512 if ((dest = gst_value_serialize (&src))) {
2514 simpletag_master = gst_ebml_write_master_start (ebml,
2515 GST_MATROSKA_ID_SIMPLETAG);
2516 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2517 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2518 gst_ebml_write_master_finish (ebml, simpletag_master);
2521 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2523 g_value_unset (&src);
2531 * gst_matroska_mux_finish:
2532 * @mux: #GstMatroskaMux
2534 * Finish a new matroska file (write index etc...)
2537 gst_matroska_mux_finish (GstMatroskaMux * mux)
2539 GstEbmlWrite *ebml = mux->ebml_write;
2541 guint64 duration = 0;
2543 const GstTagList *tags;
2545 /* finish last cluster */
2547 gst_ebml_write_master_finish (ebml, mux->cluster);
2551 if (mux->index != NULL) {
2553 guint64 master, pointentry_master, trackpos_master;
2555 mux->cues_pos = ebml->pos;
2556 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2557 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2559 for (n = 0; n < mux->num_indexes; n++) {
2560 GstMatroskaIndex *idx = &mux->index[n];
2562 pointentry_master = gst_ebml_write_master_start (ebml,
2563 GST_MATROSKA_ID_POINTENTRY);
2564 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2565 idx->time / mux->time_scale);
2566 trackpos_master = gst_ebml_write_master_start (ebml,
2567 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2568 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2569 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2570 idx->pos - mux->segment_master);
2571 gst_ebml_write_master_finish (ebml, trackpos_master);
2572 gst_ebml_write_master_finish (ebml, pointentry_master);
2575 gst_ebml_write_master_finish (ebml, master);
2576 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2580 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2582 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2583 guint64 master_tags, master_tag;
2585 GST_DEBUG_OBJECT (mux, "Writing tags");
2587 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2588 mux->tags_pos = ebml->pos;
2589 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2590 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2591 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2592 gst_ebml_write_master_finish (ebml, master_tag);
2593 gst_ebml_write_master_finish (ebml, master_tags);
2596 /* update seekhead. We know that:
2597 * - a seekhead contains 4 entries.
2598 * - order of entries is as above.
2599 * - a seekhead has a 4-byte header + 8-byte length
2600 * - each entry is 2-byte master, 2-byte ID pointer,
2601 * 2-byte length pointer, all 8/1-byte length, 4-
2602 * byte ID and 8-byte length pointer, where the
2603 * length pointer starts at 20.
2604 * - all entries are local to the segment (so pos - segment_master).
2605 * - so each entry is at 12 + 20 + num * 28. */
2606 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2607 mux->info_pos - mux->segment_master);
2608 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2609 mux->tracks_pos - mux->segment_master);
2610 if (mux->index != NULL) {
2611 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2612 mux->cues_pos - mux->segment_master);
2615 guint64 my_pos = ebml->pos;
2617 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2618 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2619 gst_ebml_write_seek (ebml, my_pos);
2622 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2623 mux->tags_pos - mux->segment_master);
2626 guint64 my_pos = ebml->pos;
2628 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2629 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2630 gst_ebml_write_seek (ebml, my_pos);
2634 * - first get the overall duration
2635 * (a released track may have left a duration in here)
2636 * - write some track header data for subtitles
2638 duration = mux->duration;
2640 for (collected = mux->collect->data; collected;
2641 collected = g_slist_next (collected)) {
2642 GstMatroskaPad *collect_pad;
2643 GstClockTime min_duration; /* observed minimum duration */
2644 GstMatroskaTrackContext *context;
2645 gint voidleft = 0, fill = 0;
2648 collect_pad = (GstMatroskaPad *) collected->data;
2649 context = collect_pad->track;
2651 GST_DEBUG_OBJECT (mux,
2652 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2653 " end ts %" GST_TIME_FORMAT, collect_pad,
2654 GST_TIME_ARGS (collect_pad->start_ts),
2655 GST_TIME_ARGS (collect_pad->end_ts));
2657 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2658 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2660 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2661 if (collect_pad->duration < min_duration)
2662 collect_pad->duration = min_duration;
2663 GST_DEBUG_OBJECT (collect_pad,
2664 "final track duration: %" GST_TIME_FORMAT,
2665 GST_TIME_ARGS (collect_pad->duration));
2668 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2669 duration < collect_pad->duration)
2670 duration = collect_pad->duration;
2672 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2676 /* write subtitle type and possible private data */
2677 gst_ebml_write_seek (ebml, context->pos);
2678 /* complex way to write ascii to account for extra filling */
2679 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2680 strcpy (codec_id, context->codec_id);
2681 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2682 codec_id, strlen (context->codec_id) + 1 + fill);
2684 if (context->codec_priv)
2685 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2686 context->codec_priv, context->codec_priv_size);
2687 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2688 /* void'ify; sigh, variable sized length field */
2689 if (voidleft == 1) {
2692 } else if (voidleft && voidleft <= 128)
2693 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2694 else if (voidleft >= 130)
2695 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2696 else if (voidleft == 129) {
2697 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2698 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2702 /* seek back (optional, but do anyway) */
2703 gst_ebml_write_seek (ebml, pos);
2705 /* update duration */
2706 if (duration != 0) {
2707 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2708 GST_TIME_ARGS (duration));
2709 pos = mux->ebml_write->pos;
2710 gst_ebml_write_seek (ebml, mux->duration_pos);
2711 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2712 gst_guint64_to_gdouble (duration) /
2713 gst_guint64_to_gdouble (mux->time_scale));
2714 gst_ebml_write_seek (ebml, pos);
2717 guint64 my_pos = ebml->pos;
2719 gst_ebml_write_seek (ebml, mux->duration_pos);
2720 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2721 gst_ebml_write_seek (ebml, my_pos);
2723 GST_DEBUG_OBJECT (mux, "finishing segment");
2724 /* finish segment - this also writes element length */
2725 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2729 * gst_matroska_mux_buffer_header:
2730 * @track: Track context.
2731 * @relative_timestamp: relative timestamp of the buffer
2732 * @flags: Buffer flags.
2734 * Create a buffer containing buffer header.
2736 * Returns: New buffer.
2739 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2740 gint16 relative_timestamp, int flags)
2743 guint8 *data = g_malloc (4);
2745 hdr = gst_buffer_new_wrapped (data, 4);
2746 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2747 data[0] = track->num | 0x80;
2748 /* time relative to clustertime */
2749 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
2757 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2758 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2759 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2762 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2763 GstMatroskaPad * collect_pad, GstBuffer * buf)
2765 GstMatroskaTrackVideoContext *ctx =
2766 (GstMatroskaTrackVideoContext *) collect_pad->track;
2771 guint32 next_parse_offset;
2772 GstBuffer *ret = NULL;
2773 gboolean is_muxing_unit = FALSE;
2775 gst_buffer_map (buf, &map, GST_MAP_READ);
2780 gst_buffer_unmap (buf, &map);
2781 gst_buffer_unref (buf);
2785 /* Check if this buffer contains a picture or end-of-sequence packet */
2786 while (size >= 13) {
2787 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2788 gst_buffer_unmap (buf, &map);
2789 gst_buffer_unref (buf);
2793 parse_code = GST_READ_UINT8 (data + 4);
2794 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2795 if (ctx->dirac_unit) {
2796 gst_buffer_unref (ctx->dirac_unit);
2797 ctx->dirac_unit = NULL;
2799 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2800 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2801 is_muxing_unit = TRUE;
2805 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2807 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2810 data += next_parse_offset;
2811 size -= next_parse_offset;
2814 if (ctx->dirac_unit)
2815 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2817 ctx->dirac_unit = gst_buffer_ref (buf);
2819 gst_buffer_unmap (buf, &map);
2821 if (is_muxing_unit) {
2822 ret = gst_buffer_make_writable (ctx->dirac_unit);
2823 ctx->dirac_unit = NULL;
2824 gst_buffer_copy_into (ret, buf,
2825 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2826 gst_buffer_unref (buf);
2828 gst_buffer_unref (buf);
2836 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2840 GValue streamheader = { 0 };
2841 GValue bufval = { 0 };
2842 GstBuffer *streamheader_buffer;
2843 GstEbmlWrite *ebml = mux->ebml_write;
2845 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2846 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2847 caps = gst_caps_new_empty_simple ("video/webm");
2849 caps = gst_caps_new_empty_simple ("video/x-matroska");
2851 s = gst_caps_get_structure (caps, 0);
2852 g_value_init (&streamheader, GST_TYPE_ARRAY);
2853 g_value_init (&bufval, GST_TYPE_BUFFER);
2854 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
2855 gst_value_set_buffer (&bufval, streamheader_buffer);
2856 gst_value_array_append_value (&streamheader, &bufval);
2857 g_value_unset (&bufval);
2858 gst_structure_set_value (s, "streamheader", &streamheader);
2859 g_value_unset (&streamheader);
2860 gst_caps_replace (&ebml->caps, caps);
2861 gst_buffer_unref (streamheader_buffer);
2862 gst_caps_unref (caps);
2866 * gst_matroska_mux_write_data:
2867 * @mux: #GstMatroskaMux
2868 * @collect_pad: #GstMatroskaPad with the data
2870 * Write collected data (called from gst_matroska_mux_collected).
2872 * Returns: Result of the gst_pad_push issued to write the data.
2874 static GstFlowReturn
2875 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2878 GstEbmlWrite *ebml = mux->ebml_write;
2881 gboolean write_duration;
2882 gint16 relative_timestamp;
2883 gint64 relative_timestamp64;
2884 guint64 block_duration;
2885 gboolean is_video_keyframe = FALSE;
2886 GstMatroskamuxPad *pad;
2889 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2891 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2892 if (collect_pad->track->xiph_headers_to_skip > 0) {
2893 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2894 gst_buffer_unref (buf);
2895 --collect_pad->track->xiph_headers_to_skip;
2899 /* for dirac we have to queue up everything up to a picture unit */
2900 if (collect_pad->track->codec_id != NULL &&
2901 strcmp (collect_pad->track->codec_id,
2902 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2903 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2908 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2909 * this would wreak havoc with time stored in matroska file */
2910 /* TODO: maybe calculate a timestamp by using the previous timestamp
2911 * and default duration */
2912 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2913 GST_WARNING_OBJECT (collect_pad->collect.pad,
2914 "Invalid buffer timestamp; dropping buffer");
2915 gst_buffer_unref (buf);
2919 /* set the timestamp for outgoing buffers */
2920 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2922 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2923 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2924 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2925 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2926 is_video_keyframe = TRUE;
2930 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2931 * or when we may be reaching the limit of the relative timestamp */
2932 if (mux->cluster_time +
2933 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2934 || is_video_keyframe || mux->force_key_unit_event) {
2935 if (!mux->streamable)
2936 gst_ebml_write_master_finish (ebml, mux->cluster);
2938 /* Forward the GstForceKeyUnit event after finishing the cluster */
2939 if (mux->force_key_unit_event) {
2940 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2941 mux->force_key_unit_event = NULL;
2944 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2945 mux->cluster_pos = ebml->pos;
2946 gst_ebml_write_set_cache (ebml, 0x20);
2948 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2949 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2950 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2952 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2953 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2955 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2956 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2957 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2958 mux->prev_cluster_size);
2963 mux->cluster_pos = ebml->pos;
2964 gst_ebml_write_set_cache (ebml, 0x20);
2965 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2966 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2967 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2968 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2969 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2972 /* update duration of this track */
2973 if (GST_BUFFER_DURATION_IS_VALID (buf))
2974 collect_pad->duration += GST_BUFFER_DURATION (buf);
2976 /* We currently write index entries for all video tracks or for the audio
2977 * track in a single-track audio file. This could be improved by keeping the
2978 * index only for the *first* video track. */
2980 /* TODO: index is useful for every track, should contain the number of
2981 * the block in the cluster which contains the timestamp, should also work
2982 * for files with multiple audio tracks.
2984 if (!mux->streamable &&
2985 (is_video_keyframe ||
2986 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2987 (mux->num_streams == 1)))) {
2990 if (mux->min_index_interval != 0) {
2991 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2992 if (mux->index[last_idx].track == collect_pad->track->num)
2997 if (last_idx < 0 || mux->min_index_interval == 0 ||
2998 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2999 >= mux->min_index_interval)) {
3000 GstMatroskaIndex *idx;
3002 if (mux->num_indexes % 32 == 0) {
3003 mux->index = g_renew (GstMatroskaIndex, mux->index,
3004 mux->num_indexes + 32);
3006 idx = &mux->index[mux->num_indexes++];
3008 idx->pos = mux->cluster_pos;
3009 idx->time = GST_BUFFER_TIMESTAMP (buf);
3010 idx->track = collect_pad->track->num;
3014 /* Check if the duration differs from the default duration. */
3015 write_duration = FALSE;
3017 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3018 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3019 1, mux->time_scale);
3021 /* small difference should be ok. */
3022 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3023 block_duration < collect_pad->default_duration_scaled - 1) {
3024 write_duration = TRUE;
3028 /* write the block, for doctype v2 use SimpleBlock if possible
3029 * one slice (*breath*).
3030 * FIXME: Need to do correct lacing! */
3031 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3032 if (relative_timestamp64 >= 0) {
3033 /* round the timestamp */
3034 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3036 /* round the timestamp */
3037 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3039 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3041 if (mux->doctype_version > 1 && !write_duration) {
3043 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3046 gst_matroska_mux_create_buffer_header (collect_pad->track,
3047 relative_timestamp, flags);
3048 gst_ebml_write_set_cache (ebml, 0x40);
3049 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3050 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3051 gst_ebml_write_buffer (ebml, hdr);
3052 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3053 gst_ebml_write_buffer (ebml, buf);
3055 return gst_ebml_last_write_result (ebml);
3057 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3058 /* write and call order slightly unnatural,
3059 * but avoids seek and minizes pushing */
3060 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3062 gst_matroska_mux_create_buffer_header (collect_pad->track,
3063 relative_timestamp, 0);
3065 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3066 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3067 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3068 gst_ebml_write_buffer (ebml, hdr);
3069 gst_ebml_write_master_finish_full (ebml, blockgroup,
3070 gst_buffer_get_size (buf));
3071 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3072 gst_ebml_write_buffer (ebml, buf);
3074 return gst_ebml_last_write_result (ebml);
3079 * gst_matroska_mux_handle_buffer:
3080 * @pads: #GstCollectPads2
3081 * @uuser_data: #GstMatroskaMux
3083 * Collectpads callback.
3085 * Returns: #GstFlowReturn
3087 static GstFlowReturn
3088 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3089 GstBuffer * buf, gpointer user_data)
3091 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3092 GstEbmlWrite *ebml = mux->ebml_write;
3093 GstMatroskaPad *best;
3094 GstFlowReturn ret = GST_FLOW_OK;
3096 GST_DEBUG_OBJECT (mux, "Collected pads");
3098 /* start with a header */
3099 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3100 if (mux->collect->data == NULL) {
3101 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3102 ("No input streams configured"));
3103 return GST_FLOW_ERROR;
3105 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3106 gst_ebml_start_streamheader (ebml);
3107 gst_matroska_mux_start (mux);
3108 gst_matroska_mux_stop_streamheader (mux);
3109 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3112 /* provided with stream to write from */
3113 best = (GstMatroskaPad *) data;
3115 /* if there is no best pad, we have reached EOS */
3117 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3118 if (!mux->streamable) {
3119 gst_matroska_mux_finish (mux);
3121 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3123 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3128 /* if we have a best stream, should also have a buffer */
3131 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3132 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3133 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3134 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3136 /* make note of first and last encountered timestamps, so we can calculate
3137 * the actual duration later when we send an updated header on eos */
3138 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3139 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3140 GstClockTime end_ts = start_ts;
3142 if (GST_BUFFER_DURATION_IS_VALID (buf))
3143 end_ts += GST_BUFFER_DURATION (buf);
3144 else if (best->track->default_duration)
3145 end_ts += best->track->default_duration;
3147 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3148 best->end_ts = end_ts;
3150 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3151 start_ts < best->start_ts))
3152 best->start_ts = start_ts;
3155 /* write one buffer */
3156 ret = gst_matroska_mux_write_data (mux, best, buf);
3164 * gst_matroska_mux_change_state:
3165 * @element: #GstMatroskaMux
3166 * @transition: State change transition.
3168 * Change the muxer state.
3170 * Returns: #GstStateChangeReturn
3172 static GstStateChangeReturn
3173 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3175 GstStateChangeReturn ret;
3176 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3178 switch (transition) {
3179 case GST_STATE_CHANGE_NULL_TO_READY:
3181 case GST_STATE_CHANGE_READY_TO_PAUSED:
3182 gst_collect_pads2_start (mux->collect);
3184 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3186 case GST_STATE_CHANGE_PAUSED_TO_READY:
3187 gst_collect_pads2_stop (mux->collect);
3193 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3195 switch (transition) {
3196 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3198 case GST_STATE_CHANGE_PAUSED_TO_READY:
3199 gst_matroska_mux_reset (GST_ELEMENT (mux));
3201 case GST_STATE_CHANGE_READY_TO_NULL:
3211 gst_matroska_mux_set_property (GObject * object,
3212 guint prop_id, const GValue * value, GParamSpec * pspec)
3214 GstMatroskaMux *mux;
3216 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3217 mux = GST_MATROSKA_MUX (object);
3220 case ARG_WRITING_APP:
3221 if (!g_value_get_string (value)) {
3222 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3225 g_free (mux->writing_app);
3226 mux->writing_app = g_value_dup_string (value);
3228 case ARG_DOCTYPE_VERSION:
3229 mux->doctype_version = g_value_get_int (value);
3231 case ARG_MIN_INDEX_INTERVAL:
3232 mux->min_index_interval = g_value_get_int64 (value);
3234 case ARG_STREAMABLE:
3235 mux->streamable = g_value_get_boolean (value);
3238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3244 gst_matroska_mux_get_property (GObject * object,
3245 guint prop_id, GValue * value, GParamSpec * pspec)
3247 GstMatroskaMux *mux;
3249 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3250 mux = GST_MATROSKA_MUX (object);
3253 case ARG_WRITING_APP:
3254 g_value_set_string (value, mux->writing_app);
3256 case ARG_DOCTYPE_VERSION:
3257 g_value_set_int (value, mux->doctype_version);
3259 case ARG_MIN_INDEX_INTERVAL:
3260 g_value_set_int64 (value, mux->min_index_interval);
3262 case ARG_STREAMABLE:
3263 g_value_set_boolean (value, mux->streamable);
3266 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);