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;
739 mux = GST_MATROSKA_MUX (user_data);
740 collect_pad = (GstMatroskaPad *) data;
742 context = collect_pad->track;
745 switch (GST_EVENT_TYPE (event)) {
746 case GST_EVENT_CAPS:{
749 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
750 gst_event_parse_caps (event, &caps);
752 collect_pad->capsfunc (pad, caps);
753 gst_event_unref (event);
760 GST_DEBUG_OBJECT (mux, "received tag event");
761 gst_event_parse_tag (event, &list);
763 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
764 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
765 const gchar *lang_code;
767 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
769 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
770 context->language = g_strdup (lang_code);
772 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
777 /* FIXME: what about stream-specific tags? */
778 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
779 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
781 gst_event_unref (event);
782 /* handled this, don't want collectpads to forward it downstream */
786 case GST_EVENT_SEGMENT:{
787 const GstSegment *segment;
789 gst_event_parse_segment (event, &segment);
790 if (segment->format != GST_FORMAT_TIME) {
791 gst_event_unref (event);
796 case GST_EVENT_CUSTOM_DOWNSTREAM:{
797 const GstStructure *structure;
799 structure = gst_event_get_structure (event);
800 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
801 gst_event_replace (&mux->force_key_unit_event, NULL);
802 mux->force_key_unit_event = event;
804 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
805 !strcmp ("dvd-spu-clut-change",
806 gst_structure_get_string (structure, "event"))) {
811 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
812 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
813 GST_DEBUG_OBJECT (pad, "... discarding");
816 /* first transform event data into table form */
817 for (i = 0; i < 16; i++) {
818 g_snprintf (name, sizeof (name), "clut%02d", i);
819 if (!gst_structure_get_int (structure, name, &value)) {
820 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
821 "contain %s field", name);
827 /* transform into private data for stream; text form */
828 gst_matroska_mux_build_vobsub_private (context, clut);
836 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
837 return (event == NULL);
841 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
844 g_assert (context && id);
845 if (context->codec_id)
846 g_free (context->codec_id);
847 context->codec_id = g_strdup (id);
851 * gst_matroska_mux_video_pad_setcaps:
852 * @pad: Pad which got the caps.
855 * Setcaps function for video sink pad.
857 * Returns: #TRUE on success.
860 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
862 GstMatroskaTrackContext *context = NULL;
863 GstMatroskaTrackVideoContext *videocontext;
865 GstMatroskaPad *collect_pad;
866 GstStructure *structure;
867 const gchar *mimetype;
868 const GValue *value = NULL;
869 GstBuffer *codec_buf = NULL;
870 gint width, height, pixel_width, pixel_height;
872 gboolean interlaced = FALSE;
874 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
877 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
878 g_assert (collect_pad);
879 context = collect_pad->track;
881 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
882 videocontext = (GstMatroskaTrackVideoContext *) context;
884 /* gst -> matroska ID'ing */
885 structure = gst_caps_get_structure (caps, 0);
887 mimetype = gst_structure_get_name (structure);
889 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
891 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
893 if (!strcmp (mimetype, "video/x-theora")) {
894 /* we'll extract the details later from the theora identification header */
898 /* get general properties */
899 /* spec says it is mandatory */
900 if (!gst_structure_get_int (structure, "width", &width) ||
901 !gst_structure_get_int (structure, "height", &height))
904 videocontext->pixel_width = width;
905 videocontext->pixel_height = height;
907 /* set vp8 defaults or let user override it */
908 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
909 && (!strcmp (mimetype, "video/x-vp8")))
910 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
911 DEFAULT_PAD_FRAME_DURATION_VP8;
913 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
914 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
916 context->default_duration =
917 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
918 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
919 GST_TIME_ARGS (context->default_duration));
921 context->default_duration = 0;
923 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
924 &pixel_width, &pixel_height)) {
925 if (pixel_width > pixel_height) {
926 videocontext->display_width = width * pixel_width / pixel_height;
927 videocontext->display_height = height;
928 } else if (pixel_width < pixel_height) {
929 videocontext->display_width = width;
930 videocontext->display_height = height * pixel_height / pixel_width;
932 videocontext->display_width = 0;
933 videocontext->display_height = 0;
936 videocontext->display_width = 0;
937 videocontext->display_height = 0;
942 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
943 videocontext->fourcc = 0;
945 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
946 * data and other settings
950 /* extract codec_data, may turn out needed */
951 value = gst_structure_get_value (structure, "codec_data");
953 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
956 if (!strcmp (mimetype, "video/x-raw")) {
958 gst_matroska_mux_set_codec_id (context,
959 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
960 fstr = gst_structure_get_string (structure, "format");
961 if (fstr && strlen (fstr) == 4)
962 videocontext->fourcc = GST_STR_FOURCC (fstr);
963 } else if (!strcmp (mimetype, "image/jpeg")) {
964 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
965 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
966 ||!strcmp (mimetype, "video/x-huffyuv")
967 || !strcmp (mimetype, "video/x-divx")
968 || !strcmp (mimetype, "video/x-dv")
969 || !strcmp (mimetype, "video/x-h263")
970 || !strcmp (mimetype, "video/x-msmpeg")
971 || !strcmp (mimetype, "video/x-wmv")
972 || !strcmp (mimetype, "image/jpeg")) {
973 gst_riff_strf_vids *bih;
974 gint size = sizeof (gst_riff_strf_vids);
977 if (!strcmp (mimetype, "video/x-xvid"))
978 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
979 else if (!strcmp (mimetype, "video/x-huffyuv"))
980 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
981 else if (!strcmp (mimetype, "video/x-dv"))
982 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
983 else if (!strcmp (mimetype, "video/x-h263"))
984 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
985 else if (!strcmp (mimetype, "video/x-divx")) {
988 gst_structure_get_int (structure, "divxversion", &divxversion);
989 switch (divxversion) {
991 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
994 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
997 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1000 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1003 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1004 switch (msmpegversion) {
1006 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1009 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1015 } else if (!strcmp (mimetype, "video/x-wmv")) {
1019 fstr = gst_structure_get_string (structure, "format");
1020 if (fstr && strlen (fstr) == 4) {
1021 fourcc = GST_STR_FOURCC (fstr);
1022 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1023 if (wmvversion == 2) {
1024 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1025 } else if (wmvversion == 1) {
1026 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1027 } else if (wmvversion == 3) {
1028 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1031 } else if (!strcmp (mimetype, "image/jpeg")) {
1032 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1038 bih = g_new0 (gst_riff_strf_vids, 1);
1039 GST_WRITE_UINT32_LE (&bih->size, size);
1040 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1041 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1042 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1043 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1044 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1045 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1046 videocontext->pixel_height * 3);
1048 /* process codec private/initialization data, if any */
1050 size += gst_buffer_get_size (codec_buf);
1051 bih = g_realloc (bih, size);
1052 GST_WRITE_UINT32_LE (&bih->size, size);
1053 gst_buffer_extract (codec_buf, 0,
1054 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1057 gst_matroska_mux_set_codec_id (context,
1058 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1059 gst_matroska_mux_free_codec_priv (context);
1060 context->codec_priv = (gpointer) bih;
1061 context->codec_priv_size = size;
1062 } else if (!strcmp (mimetype, "video/x-h264")) {
1063 gst_matroska_mux_set_codec_id (context,
1064 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1065 gst_matroska_mux_free_codec_priv (context);
1066 /* Create avcC header */
1067 if (codec_buf != NULL) {
1068 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1069 context->codec_priv = g_malloc0 (context->codec_priv_size);
1070 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1072 } else if (!strcmp (mimetype, "video/x-theora")) {
1073 const GValue *streamheader;
1075 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1077 gst_matroska_mux_free_codec_priv (context);
1079 streamheader = gst_structure_get_value (structure, "streamheader");
1080 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1081 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1082 ("theora stream headers missing or malformed"));
1085 } else if (!strcmp (mimetype, "video/x-dirac")) {
1086 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1087 } else if (!strcmp (mimetype, "video/x-vp8")) {
1088 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1089 } else if (!strcmp (mimetype, "video/mpeg")) {
1092 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1093 switch (mpegversion) {
1095 gst_matroska_mux_set_codec_id (context,
1096 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1099 gst_matroska_mux_set_codec_id (context,
1100 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1103 gst_matroska_mux_set_codec_id (context,
1104 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1110 /* global headers may be in codec data */
1111 if (codec_buf != NULL) {
1112 gst_matroska_mux_free_codec_priv (context);
1113 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1114 context->codec_priv = g_malloc0 (context->codec_priv_size);
1115 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1117 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1119 /* can only make it here if preceding case verified it was version 3 */
1120 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1121 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1123 const GValue *mdpr_data;
1125 gst_structure_get_int (structure, "rmversion", &rmversion);
1126 switch (rmversion) {
1128 gst_matroska_mux_set_codec_id (context,
1129 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1132 gst_matroska_mux_set_codec_id (context,
1133 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1136 gst_matroska_mux_set_codec_id (context,
1137 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1140 gst_matroska_mux_set_codec_id (context,
1141 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1147 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1148 if (mdpr_data != NULL) {
1149 guint8 *priv_data = NULL;
1150 guint priv_data_size = 0;
1152 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1154 priv_data_size = gst_buffer_get_size (codec_data_buf);
1155 priv_data = g_malloc0 (priv_data_size);
1157 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1159 gst_matroska_mux_free_codec_priv (context);
1160 context->codec_priv = priv_data;
1161 context->codec_priv_size = priv_data_size;
1170 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1171 GST_PAD_NAME (pad), caps);
1176 /* N > 0 to expect a particular number of headers, negative if the
1177 number of headers is variable */
1179 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1180 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1182 GstBuffer **buf = NULL;
1185 guint bufi, i, offset, priv_data_size;
1187 if (streamheader == NULL)
1188 goto no_stream_headers;
1190 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1193 bufarr = g_value_peek_pointer (streamheader);
1194 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1196 if (N > 0 && bufarr->len != N)
1199 context->xiph_headers_to_skip = bufarr->len;
1201 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1202 for (i = 0; i < bufarr->len; i++) {
1203 GValue *bufval = &g_array_index (bufarr, GValue, i);
1205 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1207 goto wrong_content_type;
1210 buf[i] = g_value_peek_pointer (bufval);
1214 if (bufarr->len > 0) {
1215 for (i = 0; i < bufarr->len - 1; i++) {
1216 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1220 for (i = 0; i < bufarr->len; ++i) {
1221 priv_data_size += gst_buffer_get_size (buf[i]);
1224 priv_data = g_malloc0 (priv_data_size);
1226 priv_data[0] = bufarr->len - 1;
1229 if (bufarr->len > 0) {
1230 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1231 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1232 priv_data[offset++] = 0xff;
1234 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1238 for (i = 0; i < bufarr->len; ++i) {
1239 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1240 offset += gst_buffer_get_size (buf[i]);
1243 gst_matroska_mux_free_codec_priv (context);
1244 context->codec_priv = priv_data;
1245 context->codec_priv_size = priv_data_size;
1248 *p_buf0 = gst_buffer_ref (buf[0]);
1257 GST_WARNING ("required streamheaders missing in sink caps!");
1262 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1263 G_VALUE_TYPE_NAME (streamheader));
1268 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1273 GST_WARNING ("streamheaders array does not contain GstBuffers");
1279 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1280 GstMatroskaTrackContext * context)
1282 GstBuffer *buf0 = NULL;
1284 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1287 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1288 GST_WARNING ("First vorbis header too small, ignoring");
1290 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1291 GstMatroskaTrackAudioContext *audiocontext;
1295 gst_buffer_map (buf0, &map, GST_MAP_READ);
1296 hdr = map.data + 1 + 6 + 4;
1297 audiocontext = (GstMatroskaTrackAudioContext *) context;
1298 audiocontext->channels = GST_READ_UINT8 (hdr);
1299 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1300 gst_buffer_unmap (buf0, &map);
1305 gst_buffer_unref (buf0);
1311 theora_streamheader_to_codecdata (const GValue * streamheader,
1312 GstMatroskaTrackContext * context)
1314 GstBuffer *buf0 = NULL;
1316 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1319 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1320 GST_WARNING ("First theora header too small, ignoring");
1321 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1322 GST_WARNING ("First header not a theora identification header, ignoring");
1324 GstMatroskaTrackVideoContext *videocontext;
1325 guint fps_num, fps_denom, par_num, par_denom;
1329 gst_buffer_map (buf0, &map, GST_MAP_READ);
1330 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1332 videocontext = (GstMatroskaTrackVideoContext *) context;
1333 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1334 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1335 hdr += 3 + 3 + 1 + 1;
1336 fps_num = GST_READ_UINT32_BE (hdr);
1337 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1338 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1339 fps_denom, fps_num);
1341 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1342 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1343 if (par_num > 0 && par_num > 0) {
1344 if (par_num > par_denom) {
1345 videocontext->display_width =
1346 videocontext->pixel_width * par_num / par_denom;
1347 videocontext->display_height = videocontext->pixel_height;
1348 } else if (par_num < par_denom) {
1349 videocontext->display_width = videocontext->pixel_width;
1350 videocontext->display_height =
1351 videocontext->pixel_height * par_denom / par_num;
1353 videocontext->display_width = 0;
1354 videocontext->display_height = 0;
1357 videocontext->display_width = 0;
1358 videocontext->display_height = 0;
1362 gst_buffer_unmap (buf0, &map);
1366 gst_buffer_unref (buf0);
1372 kate_streamheader_to_codecdata (const GValue * streamheader,
1373 GstMatroskaTrackContext * context)
1375 GstBuffer *buf0 = NULL;
1377 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1380 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1381 GST_WARNING ("First kate header too small, ignoring");
1382 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1383 GST_WARNING ("First header not a kate identification header, ignoring");
1387 gst_buffer_unref (buf0);
1393 flac_streamheader_to_codecdata (const GValue * streamheader,
1394 GstMatroskaTrackContext * context)
1401 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1402 GST_WARNING ("No or invalid streamheader field in the caps");
1406 bufarr = g_value_peek_pointer (streamheader);
1407 if (bufarr->len < 2) {
1408 GST_WARNING ("Too few headers in streamheader field");
1412 context->xiph_headers_to_skip = bufarr->len + 1;
1414 bufval = &g_array_index (bufarr, GValue, 0);
1415 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1416 GST_WARNING ("streamheaders array does not contain GstBuffers");
1420 buffer = g_value_peek_pointer (bufval);
1422 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1423 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1424 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1425 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1426 GST_WARNING ("Invalid streamheader for FLAC");
1430 gst_matroska_mux_free_codec_priv (context);
1431 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1432 context->codec_priv = g_malloc (context->codec_priv_size);
1433 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1435 for (i = 1; i < bufarr->len; i++) {
1437 bufval = &g_array_index (bufarr, GValue, i);
1439 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1440 gst_matroska_mux_free_codec_priv (context);
1441 GST_WARNING ("streamheaders array does not contain GstBuffers");
1445 buffer = g_value_peek_pointer (bufval);
1447 old_size = context->codec_priv_size;
1448 context->codec_priv_size += gst_buffer_get_size (buffer);
1450 context->codec_priv = g_realloc (context->codec_priv,
1451 context->codec_priv_size);
1452 gst_buffer_extract (buffer, 0,
1453 (guint8 *) context->codec_priv + old_size, -1);
1460 speex_streamheader_to_codecdata (const GValue * streamheader,
1461 GstMatroskaTrackContext * context)
1468 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1469 GST_WARNING ("No or invalid streamheader field in the caps");
1473 bufarr = g_value_peek_pointer (streamheader);
1474 if (bufarr->len != 2) {
1475 GST_WARNING ("Too few headers in streamheader field");
1479 context->xiph_headers_to_skip = bufarr->len + 1;
1481 bufval = &g_array_index (bufarr, GValue, 0);
1482 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1483 GST_WARNING ("streamheaders array does not contain GstBuffers");
1487 buffer = g_value_peek_pointer (bufval);
1489 if (gst_buffer_get_size (buffer) < 80
1490 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1491 GST_WARNING ("Invalid streamheader for Speex");
1495 gst_matroska_mux_free_codec_priv (context);
1496 context->codec_priv_size = gst_buffer_get_size (buffer);
1497 context->codec_priv = g_malloc (context->codec_priv_size);
1498 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1500 bufval = &g_array_index (bufarr, GValue, 1);
1502 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1503 gst_matroska_mux_free_codec_priv (context);
1504 GST_WARNING ("streamheaders array does not contain GstBuffers");
1508 buffer = g_value_peek_pointer (bufval);
1510 old_size = context->codec_priv_size;
1511 context->codec_priv_size += gst_buffer_get_size (buffer);
1512 context->codec_priv = g_realloc (context->codec_priv,
1513 context->codec_priv_size);
1514 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1519 static const gchar *
1520 aac_codec_data_to_codec_id (GstBuffer * buf)
1522 const gchar *result;
1525 /* default to MAIN */
1528 if (gst_buffer_get_size (buf) >= 2) {
1529 gst_buffer_extract (buf, 0, &profile, 1);
1547 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1556 * gst_matroska_mux_audio_pad_setcaps:
1557 * @pad: Pad which got the caps.
1560 * Setcaps function for audio sink pad.
1562 * Returns: #TRUE on success.
1565 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1567 GstMatroskaTrackContext *context = NULL;
1568 GstMatroskaTrackAudioContext *audiocontext;
1569 GstMatroskaMux *mux;
1570 GstMatroskaPad *collect_pad;
1571 const gchar *mimetype;
1572 gint samplerate = 0, channels = 0;
1573 GstStructure *structure;
1574 const GValue *codec_data = NULL;
1575 GstBuffer *buf = NULL;
1576 const gchar *stream_format = NULL;
1578 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1581 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1582 g_assert (collect_pad);
1583 context = collect_pad->track;
1585 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1586 audiocontext = (GstMatroskaTrackAudioContext *) context;
1588 structure = gst_caps_get_structure (caps, 0);
1589 mimetype = gst_structure_get_name (structure);
1592 gst_structure_get_int (structure, "rate", &samplerate);
1593 gst_structure_get_int (structure, "channels", &channels);
1595 audiocontext->samplerate = samplerate;
1596 audiocontext->channels = channels;
1597 audiocontext->bitdepth = 0;
1598 context->default_duration = 0;
1600 codec_data = gst_structure_get_value (structure, "codec_data");
1602 buf = gst_value_get_buffer (codec_data);
1604 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1605 * data and other settings
1609 if (!strcmp (mimetype, "audio/mpeg")) {
1610 gint mpegversion = 0;
1612 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1613 switch (mpegversion) {
1619 gst_structure_get_int (structure, "layer", &layer);
1621 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1622 GST_WARNING_OBJECT (mux,
1623 "Unable to determine MPEG audio version, assuming 1");
1629 else if (layer == 2)
1631 else if (version == 2)
1636 context->default_duration =
1637 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1641 gst_matroska_mux_set_codec_id (context,
1642 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1645 gst_matroska_mux_set_codec_id (context,
1646 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1649 gst_matroska_mux_set_codec_id (context,
1650 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1659 stream_format = gst_structure_get_string (structure, "stream-format");
1660 /* check this is raw aac */
1661 if (stream_format) {
1662 if (strcmp (stream_format, "raw") != 0) {
1663 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1667 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1672 if (mpegversion == 2)
1674 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1675 aac_codec_data_to_codec_id (buf));
1676 else if (mpegversion == 4)
1678 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1679 aac_codec_data_to_codec_id (buf));
1681 g_assert_not_reached ();
1683 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1690 } else if (!strcmp (mimetype, "audio/x-raw")) {
1693 gst_audio_info_init (&info);
1694 if (!gst_audio_info_from_caps (&info, caps)) {
1695 GST_DEBUG_OBJECT (mux,
1696 "broken caps, rejected by gst_audio_info_from_caps");
1700 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1701 case GST_AUDIO_FORMAT_U8:
1702 case GST_AUDIO_FORMAT_S16BE:
1703 case GST_AUDIO_FORMAT_S16LE:
1704 case GST_AUDIO_FORMAT_S24BE:
1705 case GST_AUDIO_FORMAT_S24LE:
1706 case GST_AUDIO_FORMAT_S32BE:
1707 case GST_AUDIO_FORMAT_S32LE:
1708 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1709 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1712 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1713 gst_matroska_mux_set_codec_id (context,
1714 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1716 gst_matroska_mux_set_codec_id (context,
1717 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1719 case GST_AUDIO_FORMAT_F32LE:
1720 case GST_AUDIO_FORMAT_F64LE:
1721 gst_matroska_mux_set_codec_id (context,
1722 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1726 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1730 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1731 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1732 const GValue *streamheader;
1734 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1736 gst_matroska_mux_free_codec_priv (context);
1738 streamheader = gst_structure_get_value (structure, "streamheader");
1739 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1740 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1741 ("vorbis stream headers missing or malformed"));
1744 } else if (!strcmp (mimetype, "audio/x-flac")) {
1745 const GValue *streamheader;
1747 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1749 gst_matroska_mux_free_codec_priv (context);
1751 streamheader = gst_structure_get_value (structure, "streamheader");
1752 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1753 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1754 ("flac stream headers missing or malformed"));
1757 } else if (!strcmp (mimetype, "audio/x-speex")) {
1758 const GValue *streamheader;
1760 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1761 gst_matroska_mux_free_codec_priv (context);
1763 streamheader = gst_structure_get_value (structure, "streamheader");
1764 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1765 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1766 ("speex stream headers missing or malformed"));
1769 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1770 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1771 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1772 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1773 } else if (!strcmp (mimetype, "audio/x-dts")) {
1774 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1775 } else if (!strcmp (mimetype, "audio/x-tta")) {
1778 /* TTA frame duration */
1779 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1781 gst_structure_get_int (structure, "width", &width);
1782 audiocontext->bitdepth = width;
1783 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1785 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1787 const GValue *mdpr_data;
1789 gst_structure_get_int (structure, "raversion", &raversion);
1790 switch (raversion) {
1792 gst_matroska_mux_set_codec_id (context,
1793 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1796 gst_matroska_mux_set_codec_id (context,
1797 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1800 gst_matroska_mux_set_codec_id (context,
1801 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1807 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1808 if (mdpr_data != NULL) {
1809 guint8 *priv_data = NULL;
1810 guint priv_data_size = 0;
1812 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1814 priv_data_size = gst_buffer_get_size (codec_data_buf);
1815 priv_data = g_malloc0 (priv_data_size);
1817 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1819 gst_matroska_mux_free_codec_priv (context);
1821 context->codec_priv = priv_data;
1822 context->codec_priv_size = priv_data_size;
1825 } else if (!strcmp (mimetype, "audio/x-wma")
1826 || !strcmp (mimetype, "audio/x-alaw")
1827 || !strcmp (mimetype, "audio/x-mulaw")) {
1829 guint codec_priv_size;
1834 if (samplerate == 0 || channels == 0) {
1835 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1839 if (!strcmp (mimetype, "audio/x-wma")) {
1843 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1844 || !gst_structure_get_int (structure, "block_align", &block_align)
1845 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1846 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1851 switch (wmaversion) {
1853 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1856 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1859 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1862 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1866 if (gst_structure_get_int (structure, "depth", &depth))
1867 audiocontext->bitdepth = depth;
1868 } else if (!strcmp (mimetype, "audio/x-alaw")
1869 || !strcmp (mimetype, "audio/x-mulaw")) {
1870 audiocontext->bitdepth = 8;
1871 if (!strcmp (mimetype, "audio/x-alaw"))
1872 format = GST_RIFF_WAVE_FORMAT_ALAW;
1874 format = GST_RIFF_WAVE_FORMAT_MULAW;
1876 block_align = channels;
1877 bitrate = block_align * samplerate;
1879 g_assert (format != 0);
1881 codec_priv_size = WAVEFORMATEX_SIZE;
1883 codec_priv_size += gst_buffer_get_size (buf);
1885 /* serialize waveformatex structure */
1886 codec_priv = g_malloc0 (codec_priv_size);
1887 GST_WRITE_UINT16_LE (codec_priv, format);
1888 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1889 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1890 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1891 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1892 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1894 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1896 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1898 /* process codec private/initialization data, if any */
1900 gst_buffer_extract (buf, 0,
1901 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1904 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1905 gst_matroska_mux_free_codec_priv (context);
1906 context->codec_priv = (gpointer) codec_priv;
1907 context->codec_priv_size = codec_priv_size;
1915 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1916 GST_PAD_NAME (pad), caps);
1921 /* we probably don't have the data at start,
1922 * so have to reserve (a maximum) space to write this at the end.
1923 * bit spacy, but some formats can hold quite some */
1924 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1927 * gst_matroska_mux_subtitle_pad_setcaps:
1928 * @pad: Pad which got the caps.
1931 * Setcaps function for subtitle sink pad.
1933 * Returns: #TRUE on success.
1936 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1938 /* There is now (at least) one such alement (kateenc), and I'm going
1939 to handle it here and claim it works when it can be piped back
1940 through GStreamer and VLC */
1942 GstMatroskaTrackContext *context = NULL;
1943 GstMatroskaTrackSubtitleContext *scontext;
1944 GstMatroskaMux *mux;
1945 GstMatroskaPad *collect_pad;
1946 const gchar *mimetype;
1947 GstStructure *structure;
1948 const GValue *value = NULL;
1949 GstBuffer *buf = NULL;
1951 gboolean ret = TRUE;
1953 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1956 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1957 g_assert (collect_pad);
1958 context = collect_pad->track;
1960 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1961 scontext = (GstMatroskaTrackSubtitleContext *) context;
1963 structure = gst_caps_get_structure (caps, 0);
1964 mimetype = gst_structure_get_name (structure);
1966 /* keep track of default set in request_pad */
1967 id = context->codec_id;
1970 scontext->check_utf8 = 1;
1971 scontext->invalid_utf8 = 0;
1972 context->default_duration = 0;
1974 if (!strcmp (mimetype, "subtitle/x-kate")) {
1975 const GValue *streamheader;
1977 gst_matroska_mux_set_codec_id (context,
1978 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1980 gst_matroska_mux_free_codec_priv (context);
1982 streamheader = gst_structure_get_value (structure, "streamheader");
1983 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1984 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1985 ("kate stream headers missing or malformed"));
1989 } else if (!strcmp (mimetype, "text/plain")) {
1990 gst_matroska_mux_set_codec_id (context,
1991 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
1992 } else if (!strcmp (mimetype, "application/x-ssa")) {
1993 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
1994 } else if (!strcmp (mimetype, "application/x-ass")) {
1995 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
1996 } else if (!strcmp (mimetype, "application/x-usf")) {
1997 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
1998 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
1999 gst_matroska_mux_set_codec_id (context,
2000 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2007 /* maybe some private data, e.g. vobsub */
2008 value = gst_structure_get_value (structure, "codec_data");
2010 buf = gst_value_get_buffer (value);
2013 guint8 *priv_data = NULL;
2015 gst_buffer_map (buf, &map, GST_MAP_READ);
2017 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2018 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2019 " exceeded maximum (%d); discarding", pad,
2020 SUBTITLE_MAX_CODEC_PRIVATE);
2021 gst_buffer_unmap (buf, &map);
2025 gst_matroska_mux_free_codec_priv (context);
2027 priv_data = g_malloc0 (map.size);
2028 memcpy (priv_data, map.data, map.size);
2029 context->codec_priv = priv_data;
2030 context->codec_priv_size = map.size;
2031 gst_buffer_unmap (buf, &map);
2034 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2035 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2038 /* free default if modified */
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_IN_CAPS);
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);