1 /* GStreamer Matroska muxer/demuxer
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
7 * matroska-mux.c: matroska file/stream muxer
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
30 * SECTION:element-matroskamux
32 * matroskamux muxes different input streams into a Matroska file.
35 * <title>Example launch line</title>
37 * gst-launch -v filesrc location=/path/to/mp3 ! mp3parse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
40 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
57 #include "matroska-mux.h"
58 #include "matroska-ids.h"
60 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
61 #define GST_CAT_DEFAULT matroskamux_debug
68 ARG_MIN_INDEX_INTERVAL,
72 #define DEFAULT_DOCTYPE_VERSION 2
73 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
74 #define DEFAULT_MIN_INDEX_INTERVAL 0
75 #define DEFAULT_STREAMABLE FALSE
77 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
78 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
80 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
83 GST_STATIC_CAPS ("video/x-matroska")
86 #define COMMON_VIDEO_CAPS \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ], " \
89 "framerate = (fraction) [ 0, MAX ]"
91 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
92 "width = (int) [ 16, 4096 ], " \
93 "height = (int) [ 16, 4096 ] "
96 * * require codec data, etc as needed
99 static GstStaticPadTemplate videosink_templ =
100 GST_STATIC_PAD_TEMPLATE ("video_%u",
103 GST_STATIC_CAPS ("video/mpeg, "
104 "mpegversion = (int) { 1, 2, 4 }, "
105 "systemstream = (boolean) false, "
106 COMMON_VIDEO_CAPS "; "
107 "video/x-h264, stream-format=avc, alignment=au, "
108 COMMON_VIDEO_CAPS "; "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS "; "
114 COMMON_VIDEO_CAPS "; "
116 COMMON_VIDEO_CAPS "; "
118 COMMON_VIDEO_CAPS "; "
120 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
125 COMMON_VIDEO_CAPS "; "
126 "video/x-pn-realvideo, "
127 "rmversion = (int) [1, 4], "
128 COMMON_VIDEO_CAPS "; "
130 COMMON_VIDEO_CAPS "; "
132 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
133 COMMON_VIDEO_CAPS "; "
134 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
137 #define COMMON_AUDIO_CAPS \
138 "channels = (int) [ 1, MAX ], " \
139 "rate = (int) [ 1, MAX ]"
142 * * require codec data, etc as needed
144 static GstStaticPadTemplate audiosink_templ =
145 GST_STATIC_PAD_TEMPLATE ("audio_%u",
148 GST_STATIC_CAPS ("audio/mpeg, "
149 "mpegversion = (int) 1, "
150 "layer = (int) [ 1, 3 ], "
151 COMMON_AUDIO_CAPS "; "
153 "mpegversion = (int) { 2, 4 }, "
154 "stream-format = (string) raw, "
155 COMMON_AUDIO_CAPS "; "
157 COMMON_AUDIO_CAPS "; "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
167 COMMON_AUDIO_CAPS "; "
169 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
170 "layout = (string) interleaved, "
171 COMMON_AUDIO_CAPS ";"
173 "width = (int) { 8, 16, 24 }, "
174 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
175 "audio/x-pn-realaudio, "
176 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
177 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
178 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
179 COMMON_AUDIO_CAPS ";"
181 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
183 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
186 static GstStaticPadTemplate subtitlesink_templ =
187 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
190 GST_STATIC_CAPS ("subtitle/x-kate; "
191 "text/plain; application/x-ssa; application/x-ass; "
192 "application/x-usf; video/x-dvd-subpicture; "
193 "application/x-subtitle-unknown")
196 static GArray *used_uids;
197 G_LOCK_DEFINE_STATIC (used_uids);
199 #define parent_class gst_matroska_mux_parent_class
200 G_DEFINE_TYPE_WITH_CODE (GstMatroskaMux, gst_matroska_mux, GST_TYPE_ELEMENT,
201 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
203 /* Matroska muxer destructor */
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
207 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
208 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
209 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
210 GstCollectData2 * data, GstEvent * event, gpointer user_data);
213 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
214 GstObject * parent, GstEvent * event);
215 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
216 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
217 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
219 /* gst internal change state handler */
220 static GstStateChangeReturn
221 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
223 /* gobject bla bla */
224 static void gst_matroska_mux_set_property (GObject * object,
225 guint prop_id, const GValue * value, GParamSpec * pspec);
226 static void gst_matroska_mux_get_property (GObject * object,
227 guint prop_id, GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_reset (GstElement * element);
233 static guint64 gst_matroska_mux_create_uid ();
235 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
246 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
250 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
252 GObjectClass *gobject_class;
253 GstElementClass *gstelement_class;
255 gobject_class = (GObjectClass *) klass;
256 gstelement_class = (GstElementClass *) klass;
258 gst_element_class_add_pad_template (gstelement_class,
259 gst_static_pad_template_get (&videosink_templ));
260 gst_element_class_add_pad_template (gstelement_class,
261 gst_static_pad_template_get (&audiosink_templ));
262 gst_element_class_add_pad_template (gstelement_class,
263 gst_static_pad_template_get (&subtitlesink_templ));
264 gst_element_class_add_pad_template (gstelement_class,
265 gst_static_pad_template_get (&src_templ));
266 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
268 "Muxes video/audio/subtitle streams into a matroska stream",
269 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
271 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
274 gobject_class->finalize = gst_matroska_mux_finalize;
276 gobject_class->get_property = gst_matroska_mux_get_property;
277 gobject_class->set_property = gst_matroska_mux_set_property;
279 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
280 g_param_spec_string ("writing-app", "Writing application.",
281 "The name the application that creates the matroska file.",
282 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
284 g_param_spec_int ("version", "DocType version",
285 "This parameter determines what Matroska features can be used.",
286 1, 2, DEFAULT_DOCTYPE_VERSION,
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
289 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
290 "entries", "An index entry is created every so many nanoseconds.",
291 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
292 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
294 g_param_spec_boolean ("streamable", "Determines whether output should "
295 "be streamable", "If set to true, the output should be as if it is "
296 "to be streamed and hence no indexes written or duration written.",
298 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
300 gstelement_class->change_state =
301 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
302 gstelement_class->request_new_pad =
303 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
304 gstelement_class->release_pad =
305 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
309 * Start of pad option handler code
311 #define DEFAULT_PAD_FRAME_DURATION TRUE
312 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
317 PROP_PAD_FRAME_DURATION
323 gboolean frame_duration;
324 gboolean frame_duration_user;
327 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
330 gst_matroskamux_pad_get_type (void)
332 static GType type = 0;
334 if (G_UNLIKELY (type == 0)) {
335 type = g_type_register_static_simple (GST_TYPE_PAD,
336 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
337 (GClassInitFunc) gst_matroskamux_pad_class_init,
338 sizeof (GstMatroskamuxPad), NULL, 0);
343 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
344 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
345 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
346 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
349 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
350 GValue * value, GParamSpec * pspec)
352 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
355 case PROP_PAD_FRAME_DURATION:
356 g_value_set_boolean (value, pad->frame_duration);
359 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
366 const GValue * value, GParamSpec * pspec)
368 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
371 case PROP_PAD_FRAME_DURATION:
372 pad->frame_duration = g_value_get_boolean (value);
373 pad->frame_duration_user = TRUE;
376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382 gst_matroskamux_pad_class_init (GstPadClass * klass)
384 GObjectClass *gobject_class = (GObjectClass *) klass;
386 gobject_class->set_property = gst_matroskamux_pad_set_property;
387 gobject_class->get_property = gst_matroskamux_pad_get_property;
389 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
390 g_param_spec_boolean ("frame-duration", "Frame duration",
391 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
392 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
396 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
398 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
399 pad->frame_duration_user = FALSE;
403 * End of pad option handler code
407 * gst_matroska_mux_init:
408 * @mux: #GstMatroskaMux that should be initialized.
409 * @g_class: Class of the muxer.
411 * Matroska muxer constructor.
414 gst_matroska_mux_init (GstMatroskaMux * mux)
416 GstPadTemplate *templ;
419 gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mux), "src");
420 mux->srcpad = gst_pad_new_from_template (templ, "src");
422 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
423 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
425 mux->collect = gst_collect_pads2_new ();
426 gst_collect_pads2_set_clip_function (mux->collect,
427 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
428 gst_collect_pads2_set_buffer_function (mux->collect,
429 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
430 gst_collect_pads2_set_event_function (mux->collect,
431 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
433 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
434 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
436 /* property defaults */
437 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
438 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
439 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
440 mux->streamable = DEFAULT_STREAMABLE;
442 /* initialize internal variables */
444 mux->num_streams = 0;
445 mux->num_a_streams = 0;
446 mux->num_t_streams = 0;
447 mux->num_v_streams = 0;
449 /* initialize remaining variables */
450 gst_matroska_mux_reset (GST_ELEMENT (mux));
455 * gst_matroska_mux_finalize:
456 * @object: #GstMatroskaMux that should be finalized.
458 * Finalize matroska muxer.
461 gst_matroska_mux_finalize (GObject * object)
463 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
465 gst_event_replace (&mux->force_key_unit_event, NULL);
467 gst_object_unref (mux->collect);
468 gst_object_unref (mux->ebml_write);
469 if (mux->writing_app)
470 g_free (mux->writing_app);
472 G_OBJECT_CLASS (parent_class)->finalize (object);
477 * gst_matroska_mux_create_uid:
479 * Generate new unused track UID.
481 * Returns: New track UID.
484 gst_matroska_mux_create_uid (void)
491 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
496 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
497 for (i = 0; i < used_uids->len; i++) {
498 if (g_array_index (used_uids, guint64, i) == uid) {
503 g_array_append_val (used_uids, uid);
506 G_UNLOCK (used_uids);
512 * gst_matroska_pad_reset:
513 * @collect_pad: the #GstMatroskaPad
515 * Reset and/or release resources of a matroska collect pad.
518 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
521 GstMatroskaTrackType type = 0;
523 /* free track information */
524 if (collect_pad->track != NULL) {
525 /* retrieve for optional later use */
526 name = collect_pad->track->name;
527 type = collect_pad->track->type;
528 /* extra for video */
529 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
530 GstMatroskaTrackVideoContext *ctx =
531 (GstMatroskaTrackVideoContext *) collect_pad->track;
533 if (ctx->dirac_unit) {
534 gst_buffer_unref (ctx->dirac_unit);
535 ctx->dirac_unit = NULL;
538 g_free (collect_pad->track->codec_id);
539 g_free (collect_pad->track->codec_name);
541 g_free (collect_pad->track->name);
542 g_free (collect_pad->track->language);
543 g_free (collect_pad->track->codec_priv);
544 g_free (collect_pad->track);
545 collect_pad->track = NULL;
548 if (!full && type != 0) {
549 GstMatroskaTrackContext *context;
551 /* create a fresh context */
553 case GST_MATROSKA_TRACK_TYPE_VIDEO:
554 context = (GstMatroskaTrackContext *)
555 g_new0 (GstMatroskaTrackVideoContext, 1);
557 case GST_MATROSKA_TRACK_TYPE_AUDIO:
558 context = (GstMatroskaTrackContext *)
559 g_new0 (GstMatroskaTrackAudioContext, 1);
561 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
562 context = (GstMatroskaTrackContext *)
563 g_new0 (GstMatroskaTrackSubtitleContext, 1);
566 g_assert_not_reached ();
570 context->type = type;
571 context->name = name;
572 /* TODO: check default values for the context */
573 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
574 collect_pad->track = context;
575 collect_pad->duration = 0;
576 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
577 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
582 * gst_matroska_pad_free:
583 * @collect_pad: the #GstMatroskaPad
585 * Release resources of a matroska collect pad.
588 gst_matroska_pad_free (GstPad * collect_pad)
590 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
595 * gst_matroska_mux_reset:
596 * @element: #GstMatroskaMux that should be reseted.
598 * Reset matroska muxer back to initial state.
601 gst_matroska_mux_reset (GstElement * element)
603 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
606 /* reset EBML write */
607 gst_ebml_write_reset (mux->ebml_write);
610 mux->state = GST_MATROSKA_MUX_STATE_START;
612 /* clean up existing streams */
614 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
615 GstMatroskaPad *collect_pad;
617 collect_pad = (GstMatroskaPad *) walk->data;
619 /* reset collect pad to pristine state */
620 gst_matroska_pad_reset (collect_pad, FALSE);
624 mux->num_indexes = 0;
629 mux->time_scale = GST_MSECOND;
630 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
635 mux->cluster_time = 0;
636 mux->cluster_pos = 0;
637 mux->prev_cluster_size = 0;
640 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
644 * gst_matroska_mux_handle_src_event:
645 * @pad: Pad which received the event.
646 * @event: Received event.
648 * handle events - copied from oggmux without understanding
650 * Returns: #TRUE on success.
653 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
658 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
662 /* disable seeking for now */
668 return gst_pad_event_default (pad, parent, event);
673 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
675 if (context->codec_priv != NULL) {
676 g_free (context->codec_priv);
677 context->codec_priv = NULL;
678 context->codec_priv_size = 0;
683 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
693 /* produce comma-separated list in hex format */
694 for (i = 0; i < 16; ++i) {
696 /* replicate vobsub's slightly off RGB conversion calculation */
697 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
698 u = ((col >> 8) & 0xff) - 128;
699 v = (col & 0xff) - 128;
700 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
701 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
702 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
703 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
706 sclut = g_strjoinv (",", clutv);
708 /* build codec private; only palette for now */
709 gst_matroska_mux_free_codec_priv (context);
710 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
711 /* include terminating 0 */
712 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
714 for (i = 0; i < 16; ++i) {
721 * gst_matroska_mux_handle_sink_event:
722 * @pad: Pad which received the event.
723 * @event: Received event.
725 * handle events - informational ones like tags
727 * Returns: #TRUE on success.
730 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
731 GstCollectData2 * data, GstEvent * event, gpointer user_data)
733 GstMatroskaPad *collect_pad;
734 GstMatroskaTrackContext *context;
738 gboolean ret = FALSE;
740 mux = GST_MATROSKA_MUX (user_data);
741 collect_pad = (GstMatroskaPad *) data;
743 context = collect_pad->track;
746 switch (GST_EVENT_TYPE (event)) {
747 case GST_EVENT_CAPS:{
750 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
751 gst_event_parse_caps (event, &caps);
753 ret = collect_pad->capsfunc (pad, caps);
754 gst_event_unref (event);
761 GST_DEBUG_OBJECT (mux, "received tag event");
762 gst_event_parse_tag (event, &list);
764 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
765 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
766 const gchar *lang_code;
768 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
770 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
771 context->language = g_strdup (lang_code);
773 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
778 /* FIXME: what about stream-specific tags? */
779 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
780 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
782 gst_event_unref (event);
783 /* handled this, don't want collectpads to forward it downstream */
787 case GST_EVENT_SEGMENT:{
788 const GstSegment *segment;
790 gst_event_parse_segment (event, &segment);
791 if (segment->format != GST_FORMAT_TIME) {
792 gst_event_unref (event);
797 case GST_EVENT_CUSTOM_DOWNSTREAM:{
798 const GstStructure *structure;
800 structure = gst_event_get_structure (event);
801 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
802 gst_event_replace (&mux->force_key_unit_event, NULL);
803 mux->force_key_unit_event = event;
805 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
806 !strcmp ("dvd-spu-clut-change",
807 gst_structure_get_string (structure, "event"))) {
812 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
813 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
814 GST_DEBUG_OBJECT (pad, "... discarding");
817 /* first transform event data into table form */
818 for (i = 0; i < 16; i++) {
819 g_snprintf (name, sizeof (name), "clut%02d", i);
820 if (!gst_structure_get_int (structure, name, &value)) {
821 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
822 "contain %s field", name);
828 /* transform into private data for stream; text form */
829 gst_matroska_mux_build_vobsub_private (context, clut);
837 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
842 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
845 g_assert (context && id);
846 if (context->codec_id)
847 g_free (context->codec_id);
848 context->codec_id = g_strdup (id);
852 * gst_matroska_mux_video_pad_setcaps:
853 * @pad: Pad which got the caps.
856 * Setcaps function for video sink pad.
858 * Returns: #TRUE on success.
861 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
863 GstMatroskaTrackContext *context = NULL;
864 GstMatroskaTrackVideoContext *videocontext;
866 GstMatroskaPad *collect_pad;
867 GstStructure *structure;
868 const gchar *mimetype;
869 const GValue *value = NULL;
870 GstBuffer *codec_buf = NULL;
871 gint width, height, pixel_width, pixel_height;
873 gboolean interlaced = FALSE;
875 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
878 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
879 g_assert (collect_pad);
880 context = collect_pad->track;
882 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
883 videocontext = (GstMatroskaTrackVideoContext *) context;
885 /* gst -> matroska ID'ing */
886 structure = gst_caps_get_structure (caps, 0);
888 mimetype = gst_structure_get_name (structure);
890 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
892 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
894 if (!strcmp (mimetype, "video/x-theora")) {
895 /* we'll extract the details later from the theora identification header */
899 /* get general properties */
900 /* spec says it is mandatory */
901 if (!gst_structure_get_int (structure, "width", &width) ||
902 !gst_structure_get_int (structure, "height", &height))
905 videocontext->pixel_width = width;
906 videocontext->pixel_height = height;
908 /* set vp8 defaults or let user override it */
909 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
910 && (!strcmp (mimetype, "video/x-vp8")))
911 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
912 DEFAULT_PAD_FRAME_DURATION_VP8;
914 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
915 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
917 context->default_duration =
918 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
919 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
920 GST_TIME_ARGS (context->default_duration));
922 context->default_duration = 0;
924 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
925 &pixel_width, &pixel_height)) {
926 if (pixel_width > pixel_height) {
927 videocontext->display_width = width * pixel_width / pixel_height;
928 videocontext->display_height = height;
929 } else if (pixel_width < pixel_height) {
930 videocontext->display_width = width;
931 videocontext->display_height = height * pixel_height / pixel_width;
933 videocontext->display_width = 0;
934 videocontext->display_height = 0;
937 videocontext->display_width = 0;
938 videocontext->display_height = 0;
943 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
944 videocontext->fourcc = 0;
946 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
947 * data and other settings
951 /* extract codec_data, may turn out needed */
952 value = gst_structure_get_value (structure, "codec_data");
954 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
957 if (!strcmp (mimetype, "video/x-raw")) {
959 gst_matroska_mux_set_codec_id (context,
960 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
961 fstr = gst_structure_get_string (structure, "format");
962 if (fstr && strlen (fstr) == 4)
963 videocontext->fourcc = GST_STR_FOURCC (fstr);
964 } else if (!strcmp (mimetype, "image/jpeg")) {
965 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
966 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
967 ||!strcmp (mimetype, "video/x-huffyuv")
968 || !strcmp (mimetype, "video/x-divx")
969 || !strcmp (mimetype, "video/x-dv")
970 || !strcmp (mimetype, "video/x-h263")
971 || !strcmp (mimetype, "video/x-msmpeg")
972 || !strcmp (mimetype, "video/x-wmv")
973 || !strcmp (mimetype, "image/jpeg")) {
974 gst_riff_strf_vids *bih;
975 gint size = sizeof (gst_riff_strf_vids);
978 if (!strcmp (mimetype, "video/x-xvid"))
979 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
980 else if (!strcmp (mimetype, "video/x-huffyuv"))
981 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
982 else if (!strcmp (mimetype, "video/x-dv"))
983 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
984 else if (!strcmp (mimetype, "video/x-h263"))
985 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
986 else if (!strcmp (mimetype, "video/x-divx")) {
989 gst_structure_get_int (structure, "divxversion", &divxversion);
990 switch (divxversion) {
992 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
995 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
998 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1001 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1004 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1005 switch (msmpegversion) {
1007 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1010 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1016 } else if (!strcmp (mimetype, "video/x-wmv")) {
1020 fstr = gst_structure_get_string (structure, "format");
1021 if (fstr && strlen (fstr) == 4) {
1022 fourcc = GST_STR_FOURCC (fstr);
1023 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1024 if (wmvversion == 2) {
1025 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1026 } else if (wmvversion == 1) {
1027 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1028 } else if (wmvversion == 3) {
1029 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1032 } else if (!strcmp (mimetype, "image/jpeg")) {
1033 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1039 bih = g_new0 (gst_riff_strf_vids, 1);
1040 GST_WRITE_UINT32_LE (&bih->size, size);
1041 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1042 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1043 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1044 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1045 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1046 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1047 videocontext->pixel_height * 3);
1049 /* process codec private/initialization data, if any */
1051 size += gst_buffer_get_size (codec_buf);
1052 bih = g_realloc (bih, size);
1053 GST_WRITE_UINT32_LE (&bih->size, size);
1054 gst_buffer_extract (codec_buf, 0,
1055 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1058 gst_matroska_mux_set_codec_id (context,
1059 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1060 gst_matroska_mux_free_codec_priv (context);
1061 context->codec_priv = (gpointer) bih;
1062 context->codec_priv_size = size;
1063 } else if (!strcmp (mimetype, "video/x-h264")) {
1064 gst_matroska_mux_set_codec_id (context,
1065 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1066 gst_matroska_mux_free_codec_priv (context);
1067 /* Create avcC header */
1068 if (codec_buf != NULL) {
1069 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1070 context->codec_priv = g_malloc0 (context->codec_priv_size);
1071 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1073 } else if (!strcmp (mimetype, "video/x-theora")) {
1074 const GValue *streamheader;
1076 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1078 gst_matroska_mux_free_codec_priv (context);
1080 streamheader = gst_structure_get_value (structure, "streamheader");
1081 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1082 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1083 ("theora stream headers missing or malformed"));
1086 } else if (!strcmp (mimetype, "video/x-dirac")) {
1087 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1088 } else if (!strcmp (mimetype, "video/x-vp8")) {
1089 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1090 } else if (!strcmp (mimetype, "video/mpeg")) {
1093 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1094 switch (mpegversion) {
1096 gst_matroska_mux_set_codec_id (context,
1097 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1100 gst_matroska_mux_set_codec_id (context,
1101 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1104 gst_matroska_mux_set_codec_id (context,
1105 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1111 /* global headers may be in codec data */
1112 if (codec_buf != NULL) {
1113 gst_matroska_mux_free_codec_priv (context);
1114 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1115 context->codec_priv = g_malloc0 (context->codec_priv_size);
1116 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1118 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1120 /* can only make it here if preceding case verified it was version 3 */
1121 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1122 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1124 const GValue *mdpr_data;
1126 gst_structure_get_int (structure, "rmversion", &rmversion);
1127 switch (rmversion) {
1129 gst_matroska_mux_set_codec_id (context,
1130 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1133 gst_matroska_mux_set_codec_id (context,
1134 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1137 gst_matroska_mux_set_codec_id (context,
1138 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1141 gst_matroska_mux_set_codec_id (context,
1142 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1148 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1149 if (mdpr_data != NULL) {
1150 guint8 *priv_data = NULL;
1151 guint priv_data_size = 0;
1153 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1155 priv_data_size = gst_buffer_get_size (codec_data_buf);
1156 priv_data = g_malloc0 (priv_data_size);
1158 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1160 gst_matroska_mux_free_codec_priv (context);
1161 context->codec_priv = priv_data;
1162 context->codec_priv_size = priv_data_size;
1171 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1172 GST_PAD_NAME (pad), caps);
1177 /* N > 0 to expect a particular number of headers, negative if the
1178 number of headers is variable */
1180 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1181 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1183 GstBuffer **buf = NULL;
1186 guint bufi, i, offset, priv_data_size;
1188 if (streamheader == NULL)
1189 goto no_stream_headers;
1191 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1194 bufarr = g_value_peek_pointer (streamheader);
1195 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1197 if (N > 0 && bufarr->len != N)
1200 context->xiph_headers_to_skip = bufarr->len;
1202 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1203 for (i = 0; i < bufarr->len; i++) {
1204 GValue *bufval = &g_array_index (bufarr, GValue, i);
1206 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1208 goto wrong_content_type;
1211 buf[i] = g_value_peek_pointer (bufval);
1215 if (bufarr->len > 0) {
1216 for (i = 0; i < bufarr->len - 1; i++) {
1217 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1221 for (i = 0; i < bufarr->len; ++i) {
1222 priv_data_size += gst_buffer_get_size (buf[i]);
1225 priv_data = g_malloc0 (priv_data_size);
1227 priv_data[0] = bufarr->len - 1;
1230 if (bufarr->len > 0) {
1231 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1232 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1233 priv_data[offset++] = 0xff;
1235 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1239 for (i = 0; i < bufarr->len; ++i) {
1240 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1241 offset += gst_buffer_get_size (buf[i]);
1244 gst_matroska_mux_free_codec_priv (context);
1245 context->codec_priv = priv_data;
1246 context->codec_priv_size = priv_data_size;
1249 *p_buf0 = gst_buffer_ref (buf[0]);
1258 GST_WARNING ("required streamheaders missing in sink caps!");
1263 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1264 G_VALUE_TYPE_NAME (streamheader));
1269 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1274 GST_WARNING ("streamheaders array does not contain GstBuffers");
1280 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1281 GstMatroskaTrackContext * context)
1283 GstBuffer *buf0 = NULL;
1285 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1288 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1289 GST_WARNING ("First vorbis header too small, ignoring");
1291 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1292 GstMatroskaTrackAudioContext *audiocontext;
1296 gst_buffer_map (buf0, &map, GST_MAP_READ);
1297 hdr = map.data + 1 + 6 + 4;
1298 audiocontext = (GstMatroskaTrackAudioContext *) context;
1299 audiocontext->channels = GST_READ_UINT8 (hdr);
1300 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1301 gst_buffer_unmap (buf0, &map);
1306 gst_buffer_unref (buf0);
1312 theora_streamheader_to_codecdata (const GValue * streamheader,
1313 GstMatroskaTrackContext * context)
1315 GstBuffer *buf0 = NULL;
1317 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1320 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1321 GST_WARNING ("First theora header too small, ignoring");
1322 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1323 GST_WARNING ("First header not a theora identification header, ignoring");
1325 GstMatroskaTrackVideoContext *videocontext;
1326 guint fps_num, fps_denom, par_num, par_denom;
1330 gst_buffer_map (buf0, &map, GST_MAP_READ);
1331 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1333 videocontext = (GstMatroskaTrackVideoContext *) context;
1334 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1335 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1336 hdr += 3 + 3 + 1 + 1;
1337 fps_num = GST_READ_UINT32_BE (hdr);
1338 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1339 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1340 fps_denom, fps_num);
1342 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1343 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1344 if (par_num > 0 && par_num > 0) {
1345 if (par_num > par_denom) {
1346 videocontext->display_width =
1347 videocontext->pixel_width * par_num / par_denom;
1348 videocontext->display_height = videocontext->pixel_height;
1349 } else if (par_num < par_denom) {
1350 videocontext->display_width = videocontext->pixel_width;
1351 videocontext->display_height =
1352 videocontext->pixel_height * par_denom / par_num;
1354 videocontext->display_width = 0;
1355 videocontext->display_height = 0;
1358 videocontext->display_width = 0;
1359 videocontext->display_height = 0;
1363 gst_buffer_unmap (buf0, &map);
1367 gst_buffer_unref (buf0);
1373 kate_streamheader_to_codecdata (const GValue * streamheader,
1374 GstMatroskaTrackContext * context)
1376 GstBuffer *buf0 = NULL;
1378 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1381 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1382 GST_WARNING ("First kate header too small, ignoring");
1383 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1384 GST_WARNING ("First header not a kate identification header, ignoring");
1388 gst_buffer_unref (buf0);
1394 flac_streamheader_to_codecdata (const GValue * streamheader,
1395 GstMatroskaTrackContext * context)
1402 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1403 GST_WARNING ("No or invalid streamheader field in the caps");
1407 bufarr = g_value_peek_pointer (streamheader);
1408 if (bufarr->len < 2) {
1409 GST_WARNING ("Too few headers in streamheader field");
1413 context->xiph_headers_to_skip = bufarr->len + 1;
1415 bufval = &g_array_index (bufarr, GValue, 0);
1416 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1417 GST_WARNING ("streamheaders array does not contain GstBuffers");
1421 buffer = g_value_peek_pointer (bufval);
1423 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1424 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1425 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1426 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1427 GST_WARNING ("Invalid streamheader for FLAC");
1431 gst_matroska_mux_free_codec_priv (context);
1432 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1433 context->codec_priv = g_malloc (context->codec_priv_size);
1434 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1436 for (i = 1; i < bufarr->len; i++) {
1438 bufval = &g_array_index (bufarr, GValue, i);
1440 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1441 gst_matroska_mux_free_codec_priv (context);
1442 GST_WARNING ("streamheaders array does not contain GstBuffers");
1446 buffer = g_value_peek_pointer (bufval);
1448 old_size = context->codec_priv_size;
1449 context->codec_priv_size += gst_buffer_get_size (buffer);
1451 context->codec_priv = g_realloc (context->codec_priv,
1452 context->codec_priv_size);
1453 gst_buffer_extract (buffer, 0,
1454 (guint8 *) context->codec_priv + old_size, -1);
1461 speex_streamheader_to_codecdata (const GValue * streamheader,
1462 GstMatroskaTrackContext * context)
1469 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1470 GST_WARNING ("No or invalid streamheader field in the caps");
1474 bufarr = g_value_peek_pointer (streamheader);
1475 if (bufarr->len != 2) {
1476 GST_WARNING ("Too few headers in streamheader field");
1480 context->xiph_headers_to_skip = bufarr->len + 1;
1482 bufval = &g_array_index (bufarr, GValue, 0);
1483 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1484 GST_WARNING ("streamheaders array does not contain GstBuffers");
1488 buffer = g_value_peek_pointer (bufval);
1490 if (gst_buffer_get_size (buffer) < 80
1491 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1492 GST_WARNING ("Invalid streamheader for Speex");
1496 gst_matroska_mux_free_codec_priv (context);
1497 context->codec_priv_size = gst_buffer_get_size (buffer);
1498 context->codec_priv = g_malloc (context->codec_priv_size);
1499 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1501 bufval = &g_array_index (bufarr, GValue, 1);
1503 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1504 gst_matroska_mux_free_codec_priv (context);
1505 GST_WARNING ("streamheaders array does not contain GstBuffers");
1509 buffer = g_value_peek_pointer (bufval);
1511 old_size = context->codec_priv_size;
1512 context->codec_priv_size += gst_buffer_get_size (buffer);
1513 context->codec_priv = g_realloc (context->codec_priv,
1514 context->codec_priv_size);
1515 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1520 static const gchar *
1521 aac_codec_data_to_codec_id (GstBuffer * buf)
1523 const gchar *result;
1526 /* default to MAIN */
1529 if (gst_buffer_get_size (buf) >= 2) {
1530 gst_buffer_extract (buf, 0, &profile, 1);
1548 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1557 * gst_matroska_mux_audio_pad_setcaps:
1558 * @pad: Pad which got the caps.
1561 * Setcaps function for audio sink pad.
1563 * Returns: #TRUE on success.
1566 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1568 GstMatroskaTrackContext *context = NULL;
1569 GstMatroskaTrackAudioContext *audiocontext;
1570 GstMatroskaMux *mux;
1571 GstMatroskaPad *collect_pad;
1572 const gchar *mimetype;
1573 gint samplerate = 0, channels = 0;
1574 GstStructure *structure;
1575 const GValue *codec_data = NULL;
1576 GstBuffer *buf = NULL;
1577 const gchar *stream_format = NULL;
1579 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1582 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1583 g_assert (collect_pad);
1584 context = collect_pad->track;
1586 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1587 audiocontext = (GstMatroskaTrackAudioContext *) context;
1589 structure = gst_caps_get_structure (caps, 0);
1590 mimetype = gst_structure_get_name (structure);
1593 gst_structure_get_int (structure, "rate", &samplerate);
1594 gst_structure_get_int (structure, "channels", &channels);
1596 audiocontext->samplerate = samplerate;
1597 audiocontext->channels = channels;
1598 audiocontext->bitdepth = 0;
1599 context->default_duration = 0;
1601 codec_data = gst_structure_get_value (structure, "codec_data");
1603 buf = gst_value_get_buffer (codec_data);
1605 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1606 * data and other settings
1610 if (!strcmp (mimetype, "audio/mpeg")) {
1611 gint mpegversion = 0;
1613 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1614 switch (mpegversion) {
1620 gst_structure_get_int (structure, "layer", &layer);
1622 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1623 GST_WARNING_OBJECT (mux,
1624 "Unable to determine MPEG audio version, assuming 1");
1630 else if (layer == 2)
1632 else if (version == 2)
1637 context->default_duration =
1638 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1642 gst_matroska_mux_set_codec_id (context,
1643 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1646 gst_matroska_mux_set_codec_id (context,
1647 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1650 gst_matroska_mux_set_codec_id (context,
1651 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1660 stream_format = gst_structure_get_string (structure, "stream-format");
1661 /* check this is raw aac */
1662 if (stream_format) {
1663 if (strcmp (stream_format, "raw") != 0) {
1664 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1668 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1673 if (mpegversion == 2)
1675 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1676 aac_codec_data_to_codec_id (buf));
1677 else if (mpegversion == 4)
1679 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1680 aac_codec_data_to_codec_id (buf));
1682 g_assert_not_reached ();
1684 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1691 } else if (!strcmp (mimetype, "audio/x-raw")) {
1694 gst_audio_info_init (&info);
1695 if (!gst_audio_info_from_caps (&info, caps)) {
1696 GST_DEBUG_OBJECT (mux,
1697 "broken caps, rejected by gst_audio_info_from_caps");
1701 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1702 case GST_AUDIO_FORMAT_U8:
1703 case GST_AUDIO_FORMAT_S16BE:
1704 case GST_AUDIO_FORMAT_S16LE:
1705 case GST_AUDIO_FORMAT_S24BE:
1706 case GST_AUDIO_FORMAT_S24LE:
1707 case GST_AUDIO_FORMAT_S32BE:
1708 case GST_AUDIO_FORMAT_S32LE:
1709 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1710 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1713 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1714 gst_matroska_mux_set_codec_id (context,
1715 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1717 gst_matroska_mux_set_codec_id (context,
1718 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1720 case GST_AUDIO_FORMAT_F32LE:
1721 case GST_AUDIO_FORMAT_F64LE:
1722 gst_matroska_mux_set_codec_id (context,
1723 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1727 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1731 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1732 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1733 const GValue *streamheader;
1735 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1737 gst_matroska_mux_free_codec_priv (context);
1739 streamheader = gst_structure_get_value (structure, "streamheader");
1740 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1741 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1742 ("vorbis stream headers missing or malformed"));
1745 } else if (!strcmp (mimetype, "audio/x-flac")) {
1746 const GValue *streamheader;
1748 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1750 gst_matroska_mux_free_codec_priv (context);
1752 streamheader = gst_structure_get_value (structure, "streamheader");
1753 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1754 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1755 ("flac stream headers missing or malformed"));
1758 } else if (!strcmp (mimetype, "audio/x-speex")) {
1759 const GValue *streamheader;
1761 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1762 gst_matroska_mux_free_codec_priv (context);
1764 streamheader = gst_structure_get_value (structure, "streamheader");
1765 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1766 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1767 ("speex stream headers missing or malformed"));
1770 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1771 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1772 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1773 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1774 } else if (!strcmp (mimetype, "audio/x-dts")) {
1775 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1776 } else if (!strcmp (mimetype, "audio/x-tta")) {
1779 /* TTA frame duration */
1780 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1782 gst_structure_get_int (structure, "width", &width);
1783 audiocontext->bitdepth = width;
1784 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1786 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1788 const GValue *mdpr_data;
1790 gst_structure_get_int (structure, "raversion", &raversion);
1791 switch (raversion) {
1793 gst_matroska_mux_set_codec_id (context,
1794 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1797 gst_matroska_mux_set_codec_id (context,
1798 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1801 gst_matroska_mux_set_codec_id (context,
1802 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1808 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1809 if (mdpr_data != NULL) {
1810 guint8 *priv_data = NULL;
1811 guint priv_data_size = 0;
1813 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1815 priv_data_size = gst_buffer_get_size (codec_data_buf);
1816 priv_data = g_malloc0 (priv_data_size);
1818 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1820 gst_matroska_mux_free_codec_priv (context);
1822 context->codec_priv = priv_data;
1823 context->codec_priv_size = priv_data_size;
1826 } else if (!strcmp (mimetype, "audio/x-wma")
1827 || !strcmp (mimetype, "audio/x-alaw")
1828 || !strcmp (mimetype, "audio/x-mulaw")) {
1830 guint codec_priv_size;
1835 if (samplerate == 0 || channels == 0) {
1836 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1840 if (!strcmp (mimetype, "audio/x-wma")) {
1844 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1845 || !gst_structure_get_int (structure, "block_align", &block_align)
1846 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1847 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1852 switch (wmaversion) {
1854 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1857 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1860 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1863 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1867 if (gst_structure_get_int (structure, "depth", &depth))
1868 audiocontext->bitdepth = depth;
1869 } else if (!strcmp (mimetype, "audio/x-alaw")
1870 || !strcmp (mimetype, "audio/x-mulaw")) {
1871 audiocontext->bitdepth = 8;
1872 if (!strcmp (mimetype, "audio/x-alaw"))
1873 format = GST_RIFF_WAVE_FORMAT_ALAW;
1875 format = GST_RIFF_WAVE_FORMAT_MULAW;
1877 block_align = channels;
1878 bitrate = block_align * samplerate;
1880 g_assert (format != 0);
1882 codec_priv_size = WAVEFORMATEX_SIZE;
1884 codec_priv_size += gst_buffer_get_size (buf);
1886 /* serialize waveformatex structure */
1887 codec_priv = g_malloc0 (codec_priv_size);
1888 GST_WRITE_UINT16_LE (codec_priv, format);
1889 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1890 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1891 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1892 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1893 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1895 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1897 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1899 /* process codec private/initialization data, if any */
1901 gst_buffer_extract (buf, 0,
1902 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1905 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1906 gst_matroska_mux_free_codec_priv (context);
1907 context->codec_priv = (gpointer) codec_priv;
1908 context->codec_priv_size = codec_priv_size;
1916 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1917 GST_PAD_NAME (pad), caps);
1922 /* we probably don't have the data at start,
1923 * so have to reserve (a maximum) space to write this at the end.
1924 * bit spacy, but some formats can hold quite some */
1925 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1928 * gst_matroska_mux_subtitle_pad_setcaps:
1929 * @pad: Pad which got the caps.
1932 * Setcaps function for subtitle sink pad.
1934 * Returns: #TRUE on success.
1937 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1939 /* There is now (at least) one such alement (kateenc), and I'm going
1940 to handle it here and claim it works when it can be piped back
1941 through GStreamer and VLC */
1943 GstMatroskaTrackContext *context = NULL;
1944 GstMatroskaTrackSubtitleContext *scontext;
1945 GstMatroskaMux *mux;
1946 GstMatroskaPad *collect_pad;
1947 const gchar *mimetype;
1948 GstStructure *structure;
1949 const GValue *value = NULL;
1950 GstBuffer *buf = NULL;
1952 gboolean ret = TRUE;
1954 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1957 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1958 g_assert (collect_pad);
1959 context = collect_pad->track;
1961 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1962 scontext = (GstMatroskaTrackSubtitleContext *) context;
1964 structure = gst_caps_get_structure (caps, 0);
1965 mimetype = gst_structure_get_name (structure);
1967 /* keep track of default set in request_pad */
1968 id = context->codec_id;
1971 scontext->check_utf8 = 1;
1972 scontext->invalid_utf8 = 0;
1973 context->default_duration = 0;
1975 if (!strcmp (mimetype, "subtitle/x-kate")) {
1976 const GValue *streamheader;
1978 gst_matroska_mux_set_codec_id (context,
1979 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1981 gst_matroska_mux_free_codec_priv (context);
1983 streamheader = gst_structure_get_value (structure, "streamheader");
1984 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1985 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1986 ("kate stream headers missing or malformed"));
1990 } else if (!strcmp (mimetype, "text/plain")) {
1991 gst_matroska_mux_set_codec_id (context,
1992 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
1993 } else if (!strcmp (mimetype, "application/x-ssa")) {
1994 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
1995 } else if (!strcmp (mimetype, "application/x-ass")) {
1996 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
1997 } else if (!strcmp (mimetype, "application/x-usf")) {
1998 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
1999 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2000 gst_matroska_mux_set_codec_id (context,
2001 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2008 /* maybe some private data, e.g. vobsub */
2009 value = gst_structure_get_value (structure, "codec_data");
2011 buf = gst_value_get_buffer (value);
2014 guint8 *priv_data = NULL;
2016 gst_buffer_map (buf, &map, GST_MAP_READ);
2018 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2019 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2020 " exceeded maximum (%d); discarding", pad,
2021 SUBTITLE_MAX_CODEC_PRIVATE);
2022 gst_buffer_unmap (buf, &map);
2026 gst_matroska_mux_free_codec_priv (context);
2028 priv_data = g_malloc0 (map.size);
2029 memcpy (priv_data, map.data, map.size);
2030 context->codec_priv = priv_data;
2031 context->codec_priv_size = map.size;
2032 gst_buffer_unmap (buf, &map);
2035 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2036 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2039 /* free default if modified */
2048 * gst_matroska_mux_request_new_pad:
2049 * @element: #GstMatroskaMux.
2050 * @templ: #GstPadTemplate.
2051 * @pad_name: New pad name.
2053 * Request pad function for sink templates.
2055 * Returns: New #GstPad.
2058 gst_matroska_mux_request_new_pad (GstElement * element,
2059 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2061 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2062 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2063 GstMatroskaPad *collect_pad;
2064 GstMatroskamuxPad *newpad;
2066 const gchar *pad_name = NULL;
2067 GstMatroskaCapsFunc capsfunc = NULL;
2068 GstMatroskaTrackContext *context = NULL;
2070 gboolean locked = TRUE;
2073 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2074 /* don't mix named and unnamed pads, if the pad already exists we fail when
2075 * trying to add it */
2076 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2077 pad_name = req_name;
2079 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2082 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2083 context = (GstMatroskaTrackContext *)
2084 g_new0 (GstMatroskaTrackAudioContext, 1);
2085 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2086 context->name = g_strdup ("Audio");
2087 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2088 /* don't mix named and unnamed pads, if the pad already exists we fail when
2089 * trying to add it */
2090 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2091 pad_name = req_name;
2093 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2096 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2097 context = (GstMatroskaTrackContext *)
2098 g_new0 (GstMatroskaTrackVideoContext, 1);
2099 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2100 context->name = g_strdup ("Video");
2101 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2102 /* don't mix named and unnamed pads, if the pad already exists we fail when
2103 * trying to add it */
2104 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2105 pad_name = req_name;
2107 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2110 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2111 context = (GstMatroskaTrackContext *)
2112 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2113 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2114 context->name = g_strdup ("Subtitle");
2115 /* setcaps may only provide proper one a lot later */
2116 id = g_strdup ("S_SUB_UNKNOWN");
2119 GST_WARNING_OBJECT (mux, "This is not our template!");
2123 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2124 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2127 gst_matroskamux_pad_init (newpad);
2128 collect_pad = (GstMatroskaPad *)
2129 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2130 sizeof (GstMatroskamuxPad),
2131 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2133 collect_pad->track = context;
2134 gst_matroska_pad_reset (collect_pad, FALSE);
2135 collect_pad->track->codec_id = id;
2137 collect_pad->capsfunc = capsfunc;
2138 gst_pad_set_active (GST_PAD (newpad), TRUE);
2139 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2140 goto pad_add_failed;
2144 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2146 return GST_PAD (newpad);
2151 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2152 gst_object_unref (newpad);
2158 * gst_matroska_mux_release_pad:
2159 * @element: #GstMatroskaMux.
2160 * @pad: Pad to release.
2162 * Release a previously requested pad.
2165 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2167 GstMatroskaMux *mux;
2170 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2172 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2173 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2174 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2176 if (cdata->pad == pad) {
2177 GstClockTime min_dur; /* observed minimum duration */
2179 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2180 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2181 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2182 if (collect_pad->duration < min_dur)
2183 collect_pad->duration = min_dur;
2186 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2187 mux->duration < collect_pad->duration)
2188 mux->duration = collect_pad->duration;
2194 gst_collect_pads2_remove_pad (mux->collect, pad);
2195 if (gst_element_remove_pad (element, pad))
2201 * gst_matroska_mux_track_header:
2202 * @mux: #GstMatroskaMux
2203 * @context: Tack context.
2205 * Write a track header.
2208 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2209 GstMatroskaTrackContext * context)
2211 GstEbmlWrite *ebml = mux->ebml_write;
2214 /* TODO: check if everything necessary is written and check default values */
2216 /* track type goes before the type-specific stuff */
2217 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2218 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2220 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2221 gst_matroska_mux_create_uid ());
2222 if (context->default_duration) {
2223 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2224 context->default_duration);
2226 if (context->language) {
2227 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2231 /* FIXME: until we have a nice way of getting the codecname
2232 * out of the caps, I'm not going to enable this. Too much
2233 * (useless, double, boring) work... */
2234 /* TODO: Use value from tags if any */
2235 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2236 context->codec_name); */
2237 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2239 /* type-specific stuff */
2240 switch (context->type) {
2241 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2242 GstMatroskaTrackVideoContext *videocontext =
2243 (GstMatroskaTrackVideoContext *) context;
2245 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2246 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2247 videocontext->pixel_width);
2248 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2249 videocontext->pixel_height);
2250 if (videocontext->display_width && videocontext->display_height) {
2251 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2252 videocontext->display_width);
2253 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2254 videocontext->display_height);
2256 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2257 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2258 if (videocontext->fourcc) {
2259 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2261 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2262 (gpointer) & fcc_le, 4);
2264 gst_ebml_write_master_finish (ebml, master);
2269 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2270 GstMatroskaTrackAudioContext *audiocontext =
2271 (GstMatroskaTrackAudioContext *) context;
2273 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2274 if (audiocontext->samplerate != 8000)
2275 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2276 audiocontext->samplerate);
2277 if (audiocontext->channels != 1)
2278 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2279 audiocontext->channels);
2280 if (audiocontext->bitdepth) {
2281 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2282 audiocontext->bitdepth);
2284 gst_ebml_write_master_finish (ebml, master);
2289 /* this is what we write for now and must be filled
2290 * and remainder void'ed later on */
2291 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2293 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2296 context->pos = ebml->pos;
2297 /* CodecID is mandatory ... */
2298 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2300 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2301 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2302 SUBTITLE_MAX_CODEC_PRIVATE);
2304 /* real data has to be written at finish */
2308 /* doesn't need type-specific data */
2312 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2313 if (context->codec_priv)
2314 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2315 context->codec_priv, context->codec_priv_size);
2320 * gst_matroska_mux_start:
2321 * @mux: #GstMatroskaMux
2323 * Start a new matroska file (write headers etc...)
2326 gst_matroska_mux_start (GstMatroskaMux * mux)
2328 GstEbmlWrite *ebml = mux->ebml_write;
2329 const gchar *doctype;
2330 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2331 GST_MATROSKA_ID_TRACKS,
2332 GST_MATROSKA_ID_CUES,
2333 GST_MATROSKA_ID_TAGS,
2336 guint64 master, child;
2340 GstClockTime duration = 0;
2341 guint32 segment_uid[4];
2342 GTimeVal time = { 0, 0 };
2344 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2345 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2347 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2349 /* we start with a EBML header */
2350 doctype = mux->doctype;
2351 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2352 doctype, mux->doctype_version);
2353 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2355 /* the rest of the header is cached */
2356 gst_ebml_write_set_cache (ebml, 0x1000);
2358 /* start a segment */
2360 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2361 mux->segment_master = ebml->pos;
2363 if (!mux->streamable) {
2364 /* seekhead (table of contents) - we set the positions later */
2365 mux->seekhead_pos = ebml->pos;
2366 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2367 for (i = 0; seekhead_id[i] != 0; i++) {
2368 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2369 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2370 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2371 gst_ebml_write_master_finish (ebml, child);
2373 gst_ebml_write_master_finish (ebml, master);
2376 if (mux->streamable) {
2377 const GstTagList *tags;
2380 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2382 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2383 guint64 master_tags, master_tag;
2385 GST_DEBUG_OBJECT (mux, "Writing tags");
2387 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2388 mux->tags_pos = ebml->pos;
2389 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2390 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2391 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2392 gst_ebml_write_master_finish (ebml, master_tag);
2393 gst_ebml_write_master_finish (ebml, master_tags);
2398 mux->info_pos = ebml->pos;
2399 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2400 for (i = 0; i < 4; i++) {
2401 segment_uid[i] = g_random_int ();
2403 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2404 (guint8 *) segment_uid, 16);
2405 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2406 mux->duration_pos = ebml->pos;
2408 if (!mux->streamable) {
2409 for (collected = mux->collect->data; collected;
2410 collected = g_slist_next (collected)) {
2411 GstMatroskaPad *collect_pad;
2413 gint64 trackduration;
2415 collect_pad = (GstMatroskaPad *) collected->data;
2416 thepad = collect_pad->collect.pad;
2418 /* Query the total length of the track. */
2419 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2420 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2421 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2422 GST_TIME_ARGS (trackduration));
2423 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2424 duration = (GstClockTime) trackduration;
2428 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2429 gst_guint64_to_gdouble (duration) /
2430 gst_guint64_to_gdouble (mux->time_scale));
2432 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2433 "GStreamer plugin version " PACKAGE_VERSION);
2434 if (mux->writing_app && mux->writing_app[0]) {
2435 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2437 g_get_current_time (&time);
2438 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2439 gst_ebml_write_master_finish (ebml, master);
2442 mux->tracks_pos = ebml->pos;
2443 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2445 for (collected = mux->collect->data; collected;
2446 collected = g_slist_next (collected)) {
2447 GstMatroskaPad *collect_pad;
2450 collect_pad = (GstMatroskaPad *) collected->data;
2451 thepad = collect_pad->collect.pad;
2453 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2454 collect_pad->track->codec_id != 0) {
2455 collect_pad->track->num = tracknum++;
2456 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2457 gst_matroska_mux_track_header (mux, collect_pad->track);
2458 gst_ebml_write_master_finish (ebml, child);
2459 /* some remaining pad/track setup */
2460 collect_pad->default_duration_scaled =
2461 gst_util_uint64_scale (collect_pad->track->default_duration,
2462 1, mux->time_scale);
2465 gst_ebml_write_master_finish (ebml, master);
2467 /* lastly, flush the cache */
2468 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2472 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2475 /* TODO: more sensible tag mappings */
2478 const gchar *matroska_tagname;
2479 const gchar *gstreamer_tagname;
2483 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2484 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2485 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2486 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2487 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2488 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2489 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2490 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2491 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2492 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2493 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2494 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2495 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2496 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2497 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2499 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2501 guint64 simpletag_master;
2503 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2504 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2505 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2507 if (strcmp (tagname_gst, tag) == 0) {
2508 GValue src = { 0, };
2511 if (!gst_tag_list_copy_value (&src, list, tag))
2513 if ((dest = gst_value_serialize (&src))) {
2515 simpletag_master = gst_ebml_write_master_start (ebml,
2516 GST_MATROSKA_ID_SIMPLETAG);
2517 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2518 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2519 gst_ebml_write_master_finish (ebml, simpletag_master);
2522 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2524 g_value_unset (&src);
2532 * gst_matroska_mux_finish:
2533 * @mux: #GstMatroskaMux
2535 * Finish a new matroska file (write index etc...)
2538 gst_matroska_mux_finish (GstMatroskaMux * mux)
2540 GstEbmlWrite *ebml = mux->ebml_write;
2542 guint64 duration = 0;
2544 const GstTagList *tags;
2546 /* finish last cluster */
2548 gst_ebml_write_master_finish (ebml, mux->cluster);
2552 if (mux->index != NULL) {
2554 guint64 master, pointentry_master, trackpos_master;
2556 mux->cues_pos = ebml->pos;
2557 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2558 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2560 for (n = 0; n < mux->num_indexes; n++) {
2561 GstMatroskaIndex *idx = &mux->index[n];
2563 pointentry_master = gst_ebml_write_master_start (ebml,
2564 GST_MATROSKA_ID_POINTENTRY);
2565 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2566 idx->time / mux->time_scale);
2567 trackpos_master = gst_ebml_write_master_start (ebml,
2568 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2569 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2570 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2571 idx->pos - mux->segment_master);
2572 gst_ebml_write_master_finish (ebml, trackpos_master);
2573 gst_ebml_write_master_finish (ebml, pointentry_master);
2576 gst_ebml_write_master_finish (ebml, master);
2577 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2581 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2583 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2584 guint64 master_tags, master_tag;
2586 GST_DEBUG_OBJECT (mux, "Writing tags");
2588 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2589 mux->tags_pos = ebml->pos;
2590 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2591 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2592 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2593 gst_ebml_write_master_finish (ebml, master_tag);
2594 gst_ebml_write_master_finish (ebml, master_tags);
2597 /* update seekhead. We know that:
2598 * - a seekhead contains 4 entries.
2599 * - order of entries is as above.
2600 * - a seekhead has a 4-byte header + 8-byte length
2601 * - each entry is 2-byte master, 2-byte ID pointer,
2602 * 2-byte length pointer, all 8/1-byte length, 4-
2603 * byte ID and 8-byte length pointer, where the
2604 * length pointer starts at 20.
2605 * - all entries are local to the segment (so pos - segment_master).
2606 * - so each entry is at 12 + 20 + num * 28. */
2607 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2608 mux->info_pos - mux->segment_master);
2609 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2610 mux->tracks_pos - mux->segment_master);
2611 if (mux->index != NULL) {
2612 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2613 mux->cues_pos - mux->segment_master);
2616 guint64 my_pos = ebml->pos;
2618 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2619 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2620 gst_ebml_write_seek (ebml, my_pos);
2623 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2624 mux->tags_pos - mux->segment_master);
2627 guint64 my_pos = ebml->pos;
2629 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2630 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2631 gst_ebml_write_seek (ebml, my_pos);
2635 * - first get the overall duration
2636 * (a released track may have left a duration in here)
2637 * - write some track header data for subtitles
2639 duration = mux->duration;
2641 for (collected = mux->collect->data; collected;
2642 collected = g_slist_next (collected)) {
2643 GstMatroskaPad *collect_pad;
2644 GstClockTime min_duration; /* observed minimum duration */
2645 GstMatroskaTrackContext *context;
2646 gint voidleft = 0, fill = 0;
2649 collect_pad = (GstMatroskaPad *) collected->data;
2650 context = collect_pad->track;
2652 GST_DEBUG_OBJECT (mux,
2653 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2654 " end ts %" GST_TIME_FORMAT, collect_pad,
2655 GST_TIME_ARGS (collect_pad->start_ts),
2656 GST_TIME_ARGS (collect_pad->end_ts));
2658 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2659 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2661 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2662 if (collect_pad->duration < min_duration)
2663 collect_pad->duration = min_duration;
2664 GST_DEBUG_OBJECT (collect_pad,
2665 "final track duration: %" GST_TIME_FORMAT,
2666 GST_TIME_ARGS (collect_pad->duration));
2669 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2670 duration < collect_pad->duration)
2671 duration = collect_pad->duration;
2673 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2677 /* write subtitle type and possible private data */
2678 gst_ebml_write_seek (ebml, context->pos);
2679 /* complex way to write ascii to account for extra filling */
2680 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2681 strcpy (codec_id, context->codec_id);
2682 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2683 codec_id, strlen (context->codec_id) + 1 + fill);
2685 if (context->codec_priv)
2686 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2687 context->codec_priv, context->codec_priv_size);
2688 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2689 /* void'ify; sigh, variable sized length field */
2690 if (voidleft == 1) {
2693 } else if (voidleft && voidleft <= 128)
2694 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2695 else if (voidleft >= 130)
2696 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2697 else if (voidleft == 129) {
2698 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2699 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2703 /* seek back (optional, but do anyway) */
2704 gst_ebml_write_seek (ebml, pos);
2706 /* update duration */
2707 if (duration != 0) {
2708 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2709 GST_TIME_ARGS (duration));
2710 pos = mux->ebml_write->pos;
2711 gst_ebml_write_seek (ebml, mux->duration_pos);
2712 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2713 gst_guint64_to_gdouble (duration) /
2714 gst_guint64_to_gdouble (mux->time_scale));
2715 gst_ebml_write_seek (ebml, pos);
2718 guint64 my_pos = ebml->pos;
2720 gst_ebml_write_seek (ebml, mux->duration_pos);
2721 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2722 gst_ebml_write_seek (ebml, my_pos);
2724 GST_DEBUG_OBJECT (mux, "finishing segment");
2725 /* finish segment - this also writes element length */
2726 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2730 * gst_matroska_mux_buffer_header:
2731 * @track: Track context.
2732 * @relative_timestamp: relative timestamp of the buffer
2733 * @flags: Buffer flags.
2735 * Create a buffer containing buffer header.
2737 * Returns: New buffer.
2740 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2741 gint16 relative_timestamp, int flags)
2744 guint8 *data = g_malloc (4);
2746 hdr = gst_buffer_new_wrapped (data, 4);
2747 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2748 data[0] = track->num | 0x80;
2749 /* time relative to clustertime */
2750 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
2758 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2759 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2760 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2763 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2764 GstMatroskaPad * collect_pad, GstBuffer * buf)
2766 GstMatroskaTrackVideoContext *ctx =
2767 (GstMatroskaTrackVideoContext *) collect_pad->track;
2772 guint32 next_parse_offset;
2773 GstBuffer *ret = NULL;
2774 gboolean is_muxing_unit = FALSE;
2776 gst_buffer_map (buf, &map, GST_MAP_READ);
2781 gst_buffer_unmap (buf, &map);
2782 gst_buffer_unref (buf);
2786 /* Check if this buffer contains a picture or end-of-sequence packet */
2787 while (size >= 13) {
2788 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2789 gst_buffer_unmap (buf, &map);
2790 gst_buffer_unref (buf);
2794 parse_code = GST_READ_UINT8 (data + 4);
2795 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2796 if (ctx->dirac_unit) {
2797 gst_buffer_unref (ctx->dirac_unit);
2798 ctx->dirac_unit = NULL;
2800 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2801 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2802 is_muxing_unit = TRUE;
2806 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2808 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2811 data += next_parse_offset;
2812 size -= next_parse_offset;
2815 if (ctx->dirac_unit)
2816 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2818 ctx->dirac_unit = gst_buffer_ref (buf);
2820 gst_buffer_unmap (buf, &map);
2822 if (is_muxing_unit) {
2823 ret = gst_buffer_make_writable (ctx->dirac_unit);
2824 ctx->dirac_unit = NULL;
2825 gst_buffer_copy_into (ret, buf,
2826 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2827 gst_buffer_unref (buf);
2829 gst_buffer_unref (buf);
2837 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2841 GValue streamheader = { 0 };
2842 GValue bufval = { 0 };
2843 GstBuffer *streamheader_buffer;
2844 GstEbmlWrite *ebml = mux->ebml_write;
2846 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2847 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2848 caps = gst_caps_new_empty_simple ("video/webm");
2850 caps = gst_caps_new_empty_simple ("video/x-matroska");
2852 s = gst_caps_get_structure (caps, 0);
2853 g_value_init (&streamheader, GST_TYPE_ARRAY);
2854 g_value_init (&bufval, GST_TYPE_BUFFER);
2855 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2856 gst_value_set_buffer (&bufval, streamheader_buffer);
2857 gst_value_array_append_value (&streamheader, &bufval);
2858 g_value_unset (&bufval);
2859 gst_structure_set_value (s, "streamheader", &streamheader);
2860 g_value_unset (&streamheader);
2861 gst_caps_replace (&ebml->caps, caps);
2862 gst_buffer_unref (streamheader_buffer);
2863 gst_caps_unref (caps);
2867 * gst_matroska_mux_write_data:
2868 * @mux: #GstMatroskaMux
2869 * @collect_pad: #GstMatroskaPad with the data
2871 * Write collected data (called from gst_matroska_mux_collected).
2873 * Returns: Result of the gst_pad_push issued to write the data.
2875 static GstFlowReturn
2876 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2879 GstEbmlWrite *ebml = mux->ebml_write;
2882 gboolean write_duration;
2883 gint16 relative_timestamp;
2884 gint64 relative_timestamp64;
2885 guint64 block_duration;
2886 gboolean is_video_keyframe = FALSE;
2887 GstMatroskamuxPad *pad;
2890 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2892 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2893 if (collect_pad->track->xiph_headers_to_skip > 0) {
2894 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2895 gst_buffer_unref (buf);
2896 --collect_pad->track->xiph_headers_to_skip;
2900 /* for dirac we have to queue up everything up to a picture unit */
2901 if (collect_pad->track->codec_id != NULL &&
2902 strcmp (collect_pad->track->codec_id,
2903 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2904 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2909 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2910 * this would wreak havoc with time stored in matroska file */
2911 /* TODO: maybe calculate a timestamp by using the previous timestamp
2912 * and default duration */
2913 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2914 GST_WARNING_OBJECT (collect_pad->collect.pad,
2915 "Invalid buffer timestamp; dropping buffer");
2916 gst_buffer_unref (buf);
2920 /* set the timestamp for outgoing buffers */
2921 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2923 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2924 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2925 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2926 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2927 is_video_keyframe = TRUE;
2931 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2932 * or when we may be reaching the limit of the relative timestamp */
2933 if (mux->cluster_time +
2934 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2935 || is_video_keyframe || mux->force_key_unit_event) {
2936 if (!mux->streamable)
2937 gst_ebml_write_master_finish (ebml, mux->cluster);
2939 /* Forward the GstForceKeyUnit event after finishing the cluster */
2940 if (mux->force_key_unit_event) {
2941 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2942 mux->force_key_unit_event = NULL;
2945 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2946 mux->cluster_pos = ebml->pos;
2947 gst_ebml_write_set_cache (ebml, 0x20);
2949 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2950 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2951 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2953 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2954 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2956 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2957 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2958 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2959 mux->prev_cluster_size);
2964 mux->cluster_pos = ebml->pos;
2965 gst_ebml_write_set_cache (ebml, 0x20);
2966 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2967 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2968 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2969 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2970 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2973 /* update duration of this track */
2974 if (GST_BUFFER_DURATION_IS_VALID (buf))
2975 collect_pad->duration += GST_BUFFER_DURATION (buf);
2977 /* We currently write index entries for all video tracks or for the audio
2978 * track in a single-track audio file. This could be improved by keeping the
2979 * index only for the *first* video track. */
2981 /* TODO: index is useful for every track, should contain the number of
2982 * the block in the cluster which contains the timestamp, should also work
2983 * for files with multiple audio tracks.
2985 if (!mux->streamable &&
2986 (is_video_keyframe ||
2987 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2988 (mux->num_streams == 1)))) {
2991 if (mux->min_index_interval != 0) {
2992 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2993 if (mux->index[last_idx].track == collect_pad->track->num)
2998 if (last_idx < 0 || mux->min_index_interval == 0 ||
2999 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3000 >= mux->min_index_interval)) {
3001 GstMatroskaIndex *idx;
3003 if (mux->num_indexes % 32 == 0) {
3004 mux->index = g_renew (GstMatroskaIndex, mux->index,
3005 mux->num_indexes + 32);
3007 idx = &mux->index[mux->num_indexes++];
3009 idx->pos = mux->cluster_pos;
3010 idx->time = GST_BUFFER_TIMESTAMP (buf);
3011 idx->track = collect_pad->track->num;
3015 /* Check if the duration differs from the default duration. */
3016 write_duration = FALSE;
3018 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3019 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3020 1, mux->time_scale);
3022 /* small difference should be ok. */
3023 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3024 block_duration < collect_pad->default_duration_scaled - 1) {
3025 write_duration = TRUE;
3029 /* write the block, for doctype v2 use SimpleBlock if possible
3030 * one slice (*breath*).
3031 * FIXME: Need to do correct lacing! */
3032 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3033 if (relative_timestamp64 >= 0) {
3034 /* round the timestamp */
3035 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3037 /* round the timestamp */
3038 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3040 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3042 if (mux->doctype_version > 1 && !write_duration) {
3044 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3047 gst_matroska_mux_create_buffer_header (collect_pad->track,
3048 relative_timestamp, flags);
3049 gst_ebml_write_set_cache (ebml, 0x40);
3050 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3051 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3052 gst_ebml_write_buffer (ebml, hdr);
3053 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3054 gst_ebml_write_buffer (ebml, buf);
3056 return gst_ebml_last_write_result (ebml);
3058 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3059 /* write and call order slightly unnatural,
3060 * but avoids seek and minizes pushing */
3061 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3063 gst_matroska_mux_create_buffer_header (collect_pad->track,
3064 relative_timestamp, 0);
3066 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3067 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3068 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3069 gst_ebml_write_buffer (ebml, hdr);
3070 gst_ebml_write_master_finish_full (ebml, blockgroup,
3071 gst_buffer_get_size (buf));
3072 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3073 gst_ebml_write_buffer (ebml, buf);
3075 return gst_ebml_last_write_result (ebml);
3080 * gst_matroska_mux_handle_buffer:
3081 * @pads: #GstCollectPads2
3082 * @uuser_data: #GstMatroskaMux
3084 * Collectpads callback.
3086 * Returns: #GstFlowReturn
3088 static GstFlowReturn
3089 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3090 GstBuffer * buf, gpointer user_data)
3092 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3093 GstEbmlWrite *ebml = mux->ebml_write;
3094 GstMatroskaPad *best;
3095 GstFlowReturn ret = GST_FLOW_OK;
3097 GST_DEBUG_OBJECT (mux, "Collected pads");
3099 /* start with a header */
3100 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3101 if (mux->collect->data == NULL) {
3102 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3103 ("No input streams configured"));
3104 return GST_FLOW_ERROR;
3106 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3107 gst_ebml_start_streamheader (ebml);
3108 gst_matroska_mux_start (mux);
3109 gst_matroska_mux_stop_streamheader (mux);
3110 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3113 /* provided with stream to write from */
3114 best = (GstMatroskaPad *) data;
3116 /* if there is no best pad, we have reached EOS */
3118 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3119 if (!mux->streamable) {
3120 gst_matroska_mux_finish (mux);
3122 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3124 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3129 /* if we have a best stream, should also have a buffer */
3132 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3133 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3134 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3135 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3137 /* make note of first and last encountered timestamps, so we can calculate
3138 * the actual duration later when we send an updated header on eos */
3139 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3140 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3141 GstClockTime end_ts = start_ts;
3143 if (GST_BUFFER_DURATION_IS_VALID (buf))
3144 end_ts += GST_BUFFER_DURATION (buf);
3145 else if (best->track->default_duration)
3146 end_ts += best->track->default_duration;
3148 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3149 best->end_ts = end_ts;
3151 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3152 start_ts < best->start_ts))
3153 best->start_ts = start_ts;
3156 /* write one buffer */
3157 ret = gst_matroska_mux_write_data (mux, best, buf);
3165 * gst_matroska_mux_change_state:
3166 * @element: #GstMatroskaMux
3167 * @transition: State change transition.
3169 * Change the muxer state.
3171 * Returns: #GstStateChangeReturn
3173 static GstStateChangeReturn
3174 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3176 GstStateChangeReturn ret;
3177 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3179 switch (transition) {
3180 case GST_STATE_CHANGE_NULL_TO_READY:
3182 case GST_STATE_CHANGE_READY_TO_PAUSED:
3183 gst_collect_pads2_start (mux->collect);
3185 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3187 case GST_STATE_CHANGE_PAUSED_TO_READY:
3188 gst_collect_pads2_stop (mux->collect);
3194 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3196 switch (transition) {
3197 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3199 case GST_STATE_CHANGE_PAUSED_TO_READY:
3200 gst_matroska_mux_reset (GST_ELEMENT (mux));
3202 case GST_STATE_CHANGE_READY_TO_NULL:
3212 gst_matroska_mux_set_property (GObject * object,
3213 guint prop_id, const GValue * value, GParamSpec * pspec)
3215 GstMatroskaMux *mux;
3217 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3218 mux = GST_MATROSKA_MUX (object);
3221 case ARG_WRITING_APP:
3222 if (!g_value_get_string (value)) {
3223 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3226 g_free (mux->writing_app);
3227 mux->writing_app = g_value_dup_string (value);
3229 case ARG_DOCTYPE_VERSION:
3230 mux->doctype_version = g_value_get_int (value);
3232 case ARG_MIN_INDEX_INTERVAL:
3233 mux->min_index_interval = g_value_get_int64 (value);
3235 case ARG_STREAMABLE:
3236 mux->streamable = g_value_get_boolean (value);
3239 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3245 gst_matroska_mux_get_property (GObject * object,
3246 guint prop_id, GValue * value, GParamSpec * pspec)
3248 GstMatroskaMux *mux;
3250 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3251 mux = GST_MATROSKA_MUX (object);
3254 case ARG_WRITING_APP:
3255 g_value_set_string (value, mux->writing_app);
3257 case ARG_DOCTYPE_VERSION:
3258 g_value_set_int (value, mux->doctype_version);
3260 case ARG_MIN_INDEX_INTERVAL:
3261 g_value_set_int64 (value, mux->min_index_interval);
3263 case ARG_STREAMABLE:
3264 g_value_set_boolean (value, mux->streamable);
3267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);