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);
672 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
682 /* produce comma-separated list in hex format */
683 for (i = 0; i < 16; ++i) {
685 /* replicate vobsub's slightly off RGB conversion calculation */
686 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
687 u = ((col >> 8) & 0xff) - 128;
688 v = (col & 0xff) - 128;
689 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
690 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
691 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
692 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
695 sclut = g_strjoinv (",", clutv);
697 /* build codec private; only palette for now */
698 g_free (context->codec_priv);
699 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
700 /* include terminating 0 */
701 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
703 for (i = 0; i < 16; ++i) {
710 * gst_matroska_mux_handle_sink_event:
711 * @pad: Pad which received the event.
712 * @event: Received event.
714 * handle events - informational ones like tags
716 * Returns: #TRUE on success.
719 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
720 GstCollectData2 * data, GstEvent * event, gpointer user_data)
722 GstMatroskaPad *collect_pad;
723 GstMatroskaTrackContext *context;
727 gboolean ret = FALSE;
729 mux = GST_MATROSKA_MUX (user_data);
730 collect_pad = (GstMatroskaPad *) data;
732 context = collect_pad->track;
735 switch (GST_EVENT_TYPE (event)) {
736 case GST_EVENT_CAPS:{
739 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
740 gst_event_parse_caps (event, &caps);
742 ret = collect_pad->capsfunc (pad, caps);
743 gst_event_unref (event);
750 GST_DEBUG_OBJECT (mux, "received tag event");
751 gst_event_parse_tag (event, &list);
753 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
754 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
755 const gchar *lang_code;
757 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
759 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
760 context->language = g_strdup (lang_code);
762 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
767 /* FIXME: what about stream-specific tags? */
768 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
769 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
771 gst_event_unref (event);
772 /* handled this, don't want collectpads to forward it downstream */
776 case GST_EVENT_SEGMENT:{
777 const GstSegment *segment;
779 gst_event_parse_segment (event, &segment);
780 if (segment->format != GST_FORMAT_TIME) {
781 gst_event_unref (event);
786 case GST_EVENT_CUSTOM_DOWNSTREAM:{
787 const GstStructure *structure;
789 structure = gst_event_get_structure (event);
790 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
791 gst_event_replace (&mux->force_key_unit_event, NULL);
792 mux->force_key_unit_event = event;
794 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
795 !strcmp ("dvd-spu-clut-change",
796 gst_structure_get_string (structure, "event"))) {
801 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
802 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
803 GST_DEBUG_OBJECT (pad, "... discarding");
806 /* first transform event data into table form */
807 for (i = 0; i < 16; i++) {
808 g_snprintf (name, sizeof (name), "clut%02d", i);
809 if (!gst_structure_get_int (structure, name, &value)) {
810 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
811 "contain %s field", name);
817 /* transform into private data for stream; text form */
818 gst_matroska_mux_build_vobsub_private (context, clut);
826 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
831 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
834 g_assert (context && id);
835 if (context->codec_id)
836 g_free (context->codec_id);
837 context->codec_id = g_strdup (id);
841 * gst_matroska_mux_video_pad_setcaps:
842 * @pad: Pad which got the caps.
845 * Setcaps function for video sink pad.
847 * Returns: #TRUE on success.
850 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
852 GstMatroskaTrackContext *context = NULL;
853 GstMatroskaTrackVideoContext *videocontext;
855 GstMatroskaPad *collect_pad;
856 GstStructure *structure;
857 const gchar *mimetype;
858 const GValue *value = NULL;
859 GstBuffer *codec_buf = NULL;
860 gint width, height, pixel_width, pixel_height;
862 gboolean interlaced = FALSE;
864 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
867 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
868 g_assert (collect_pad);
869 context = collect_pad->track;
871 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
872 videocontext = (GstMatroskaTrackVideoContext *) context;
874 /* gst -> matroska ID'ing */
875 structure = gst_caps_get_structure (caps, 0);
877 mimetype = gst_structure_get_name (structure);
879 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
881 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
883 if (!strcmp (mimetype, "video/x-theora")) {
884 /* we'll extract the details later from the theora identification header */
888 /* get general properties */
889 /* spec says it is mandatory */
890 if (!gst_structure_get_int (structure, "width", &width) ||
891 !gst_structure_get_int (structure, "height", &height))
894 videocontext->pixel_width = width;
895 videocontext->pixel_height = height;
897 /* set vp8 defaults or let user override it */
898 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
899 && (!strcmp (mimetype, "video/x-vp8")))
900 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
901 DEFAULT_PAD_FRAME_DURATION_VP8;
903 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
904 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
906 context->default_duration =
907 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
908 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
909 GST_TIME_ARGS (context->default_duration));
911 context->default_duration = 0;
913 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
914 &pixel_width, &pixel_height)) {
915 if (pixel_width > pixel_height) {
916 videocontext->display_width = width * pixel_width / pixel_height;
917 videocontext->display_height = height;
918 } else if (pixel_width < pixel_height) {
919 videocontext->display_width = width;
920 videocontext->display_height = height * pixel_height / pixel_width;
922 videocontext->display_width = 0;
923 videocontext->display_height = 0;
926 videocontext->display_width = 0;
927 videocontext->display_height = 0;
932 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
933 videocontext->fourcc = 0;
935 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
936 * data and other settings
940 /* extract codec_data, may turn out needed */
941 value = gst_structure_get_value (structure, "codec_data");
943 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
946 if (!strcmp (mimetype, "video/x-raw")) {
948 gst_matroska_mux_set_codec_id (context,
949 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
950 fstr = gst_structure_get_string (structure, "format");
951 if (fstr && strlen (fstr) == 4)
952 videocontext->fourcc = GST_STR_FOURCC (fstr);
953 } else if (!strcmp (mimetype, "image/jpeg")) {
954 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
955 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
956 ||!strcmp (mimetype, "video/x-huffyuv")
957 || !strcmp (mimetype, "video/x-divx")
958 || !strcmp (mimetype, "video/x-dv")
959 || !strcmp (mimetype, "video/x-h263")
960 || !strcmp (mimetype, "video/x-msmpeg")
961 || !strcmp (mimetype, "video/x-wmv")
962 || !strcmp (mimetype, "image/jpeg")) {
963 gst_riff_strf_vids *bih;
964 gint size = sizeof (gst_riff_strf_vids);
967 if (!strcmp (mimetype, "video/x-xvid"))
968 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
969 else if (!strcmp (mimetype, "video/x-huffyuv"))
970 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
971 else if (!strcmp (mimetype, "video/x-dv"))
972 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
973 else if (!strcmp (mimetype, "video/x-h263"))
974 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
975 else if (!strcmp (mimetype, "video/x-divx")) {
978 gst_structure_get_int (structure, "divxversion", &divxversion);
979 switch (divxversion) {
981 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
984 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
987 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
990 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
993 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
994 switch (msmpegversion) {
996 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
999 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1005 } else if (!strcmp (mimetype, "video/x-wmv")) {
1009 fstr = gst_structure_get_string (structure, "format");
1010 if (fstr && strlen (fstr) == 4) {
1011 fourcc = GST_STR_FOURCC (fstr);
1012 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1013 if (wmvversion == 2) {
1014 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1015 } else if (wmvversion == 1) {
1016 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1017 } else if (wmvversion == 3) {
1018 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1021 } else if (!strcmp (mimetype, "image/jpeg")) {
1022 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1028 bih = g_new0 (gst_riff_strf_vids, 1);
1029 GST_WRITE_UINT32_LE (&bih->size, size);
1030 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1031 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1032 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1033 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1034 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1035 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1036 videocontext->pixel_height * 3);
1038 /* process codec private/initialization data, if any */
1040 size += gst_buffer_get_size (codec_buf);
1041 bih = g_realloc (bih, size);
1042 GST_WRITE_UINT32_LE (&bih->size, size);
1043 gst_buffer_extract (codec_buf, 0,
1044 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1047 gst_matroska_mux_set_codec_id (context,
1048 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1049 context->codec_priv = (gpointer) bih;
1050 context->codec_priv_size = size;
1051 } else if (!strcmp (mimetype, "video/x-h264")) {
1052 gst_matroska_mux_set_codec_id (context,
1053 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1055 if (context->codec_priv != NULL) {
1056 g_free (context->codec_priv);
1057 context->codec_priv = NULL;
1058 context->codec_priv_size = 0;
1061 /* Create avcC header */
1062 if (codec_buf != NULL) {
1063 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1064 context->codec_priv = g_malloc0 (context->codec_priv_size);
1065 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1067 } else if (!strcmp (mimetype, "video/x-theora")) {
1068 const GValue *streamheader;
1070 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1072 if (context->codec_priv != NULL) {
1073 g_free (context->codec_priv);
1074 context->codec_priv = NULL;
1075 context->codec_priv_size = 0;
1078 streamheader = gst_structure_get_value (structure, "streamheader");
1079 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1080 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1081 ("theora stream headers missing or malformed"));
1084 } else if (!strcmp (mimetype, "video/x-dirac")) {
1085 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1086 } else if (!strcmp (mimetype, "video/x-vp8")) {
1087 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1088 } else if (!strcmp (mimetype, "video/mpeg")) {
1091 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1092 switch (mpegversion) {
1094 gst_matroska_mux_set_codec_id (context,
1095 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1098 gst_matroska_mux_set_codec_id (context,
1099 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1102 gst_matroska_mux_set_codec_id (context,
1103 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1109 /* global headers may be in codec data */
1110 if (codec_buf != NULL) {
1111 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1112 context->codec_priv = g_malloc0 (context->codec_priv_size);
1113 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1115 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1117 /* can only make it here if preceding case verified it was version 3 */
1118 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1119 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1121 const GValue *mdpr_data;
1123 gst_structure_get_int (structure, "rmversion", &rmversion);
1124 switch (rmversion) {
1126 gst_matroska_mux_set_codec_id (context,
1127 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1130 gst_matroska_mux_set_codec_id (context,
1131 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1134 gst_matroska_mux_set_codec_id (context,
1135 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1138 gst_matroska_mux_set_codec_id (context,
1139 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1145 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1146 if (mdpr_data != NULL) {
1147 guint8 *priv_data = NULL;
1148 guint priv_data_size = 0;
1150 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1152 priv_data_size = gst_buffer_get_size (codec_data_buf);
1153 priv_data = g_malloc0 (priv_data_size);
1155 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1157 context->codec_priv = priv_data;
1158 context->codec_priv_size = priv_data_size;
1167 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1168 GST_PAD_NAME (pad), caps);
1173 /* N > 0 to expect a particular number of headers, negative if the
1174 number of headers is variable */
1176 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1177 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1179 GstBuffer **buf = NULL;
1182 guint bufi, i, offset, priv_data_size;
1184 if (streamheader == NULL)
1185 goto no_stream_headers;
1187 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1190 bufarr = g_value_peek_pointer (streamheader);
1191 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1193 if (N > 0 && bufarr->len != N)
1196 context->xiph_headers_to_skip = bufarr->len;
1198 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1199 for (i = 0; i < bufarr->len; i++) {
1200 GValue *bufval = &g_array_index (bufarr, GValue, i);
1202 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1204 goto wrong_content_type;
1207 buf[i] = g_value_peek_pointer (bufval);
1211 if (bufarr->len > 0) {
1212 for (i = 0; i < bufarr->len - 1; i++) {
1213 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1217 for (i = 0; i < bufarr->len; ++i) {
1218 priv_data_size += gst_buffer_get_size (buf[i]);
1221 priv_data = g_malloc0 (priv_data_size);
1223 priv_data[0] = bufarr->len - 1;
1226 if (bufarr->len > 0) {
1227 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1228 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1229 priv_data[offset++] = 0xff;
1231 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1235 for (i = 0; i < bufarr->len; ++i) {
1236 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1237 offset += gst_buffer_get_size (buf[i]);
1240 context->codec_priv = priv_data;
1241 context->codec_priv_size = priv_data_size;
1244 *p_buf0 = gst_buffer_ref (buf[0]);
1253 GST_WARNING ("required streamheaders missing in sink caps!");
1258 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1259 G_VALUE_TYPE_NAME (streamheader));
1264 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1269 GST_WARNING ("streamheaders array does not contain GstBuffers");
1275 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1276 GstMatroskaTrackContext * context)
1278 GstBuffer *buf0 = NULL;
1280 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1283 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1284 GST_WARNING ("First vorbis header too small, ignoring");
1286 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1287 GstMatroskaTrackAudioContext *audiocontext;
1290 data = gst_buffer_map (buf0, NULL, NULL, GST_MAP_READ);
1291 hdr = data + 1 + 6 + 4;
1292 audiocontext = (GstMatroskaTrackAudioContext *) context;
1293 audiocontext->channels = GST_READ_UINT8 (hdr);
1294 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1295 gst_buffer_unmap (buf0, data, -1);
1300 gst_buffer_unref (buf0);
1306 theora_streamheader_to_codecdata (const GValue * streamheader,
1307 GstMatroskaTrackContext * context)
1309 GstBuffer *buf0 = NULL;
1311 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1314 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1315 GST_WARNING ("First theora header too small, ignoring");
1316 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1317 GST_WARNING ("First header not a theora identification header, ignoring");
1319 GstMatroskaTrackVideoContext *videocontext;
1320 guint fps_num, fps_denom, par_num, par_denom;
1323 data = gst_buffer_map (buf0, NULL, NULL, GST_MAP_READ);
1324 hdr = data + 1 + 6 + 3 + 2 + 2;
1326 videocontext = (GstMatroskaTrackVideoContext *) context;
1327 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1328 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1329 hdr += 3 + 3 + 1 + 1;
1330 fps_num = GST_READ_UINT32_BE (hdr);
1331 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1332 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1333 fps_denom, fps_num);
1335 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1336 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1337 if (par_num > 0 && par_num > 0) {
1338 if (par_num > par_denom) {
1339 videocontext->display_width =
1340 videocontext->pixel_width * par_num / par_denom;
1341 videocontext->display_height = videocontext->pixel_height;
1342 } else if (par_num < par_denom) {
1343 videocontext->display_width = videocontext->pixel_width;
1344 videocontext->display_height =
1345 videocontext->pixel_height * par_denom / par_num;
1347 videocontext->display_width = 0;
1348 videocontext->display_height = 0;
1351 videocontext->display_width = 0;
1352 videocontext->display_height = 0;
1356 gst_buffer_unmap (buf0, data, -1);
1360 gst_buffer_unref (buf0);
1366 kate_streamheader_to_codecdata (const GValue * streamheader,
1367 GstMatroskaTrackContext * context)
1369 GstBuffer *buf0 = NULL;
1371 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1374 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1375 GST_WARNING ("First kate header too small, ignoring");
1376 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1377 GST_WARNING ("First header not a kate identification header, ignoring");
1381 gst_buffer_unref (buf0);
1387 flac_streamheader_to_codecdata (const GValue * streamheader,
1388 GstMatroskaTrackContext * context)
1395 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1396 GST_WARNING ("No or invalid streamheader field in the caps");
1400 bufarr = g_value_peek_pointer (streamheader);
1401 if (bufarr->len < 2) {
1402 GST_WARNING ("Too few headers in streamheader field");
1406 context->xiph_headers_to_skip = bufarr->len + 1;
1408 bufval = &g_array_index (bufarr, GValue, 0);
1409 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1410 GST_WARNING ("streamheaders array does not contain GstBuffers");
1414 buffer = g_value_peek_pointer (bufval);
1416 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1417 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1418 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1419 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1420 GST_WARNING ("Invalid streamheader for FLAC");
1424 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1425 context->codec_priv = g_malloc (context->codec_priv_size);
1426 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1428 for (i = 1; i < bufarr->len; i++) {
1430 bufval = &g_array_index (bufarr, GValue, i);
1432 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1433 g_free (context->codec_priv);
1434 context->codec_priv = NULL;
1435 context->codec_priv_size = 0;
1436 GST_WARNING ("streamheaders array does not contain GstBuffers");
1440 buffer = g_value_peek_pointer (bufval);
1442 old_size = context->codec_priv_size;
1443 context->codec_priv_size += gst_buffer_get_size (buffer);
1445 context->codec_priv = g_realloc (context->codec_priv,
1446 context->codec_priv_size);
1447 gst_buffer_extract (buffer, 0,
1448 (guint8 *) context->codec_priv + old_size, -1);
1455 speex_streamheader_to_codecdata (const GValue * streamheader,
1456 GstMatroskaTrackContext * context)
1463 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1464 GST_WARNING ("No or invalid streamheader field in the caps");
1468 bufarr = g_value_peek_pointer (streamheader);
1469 if (bufarr->len != 2) {
1470 GST_WARNING ("Too few headers in streamheader field");
1474 context->xiph_headers_to_skip = bufarr->len + 1;
1476 bufval = &g_array_index (bufarr, GValue, 0);
1477 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1478 GST_WARNING ("streamheaders array does not contain GstBuffers");
1482 buffer = g_value_peek_pointer (bufval);
1484 if (gst_buffer_get_size (buffer) < 80
1485 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1486 GST_WARNING ("Invalid streamheader for Speex");
1490 context->codec_priv_size = gst_buffer_get_size (buffer);
1491 context->codec_priv = g_malloc (context->codec_priv_size);
1492 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1494 bufval = &g_array_index (bufarr, GValue, 1);
1496 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1497 g_free (context->codec_priv);
1498 context->codec_priv = NULL;
1499 context->codec_priv_size = 0;
1500 GST_WARNING ("streamheaders array does not contain GstBuffers");
1504 buffer = g_value_peek_pointer (bufval);
1506 old_size = context->codec_priv_size;
1507 context->codec_priv_size += gst_buffer_get_size (buffer);
1508 context->codec_priv = g_realloc (context->codec_priv,
1509 context->codec_priv_size);
1510 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1515 static const gchar *
1516 aac_codec_data_to_codec_id (GstBuffer * buf)
1518 const gchar *result;
1521 /* default to MAIN */
1524 if (gst_buffer_get_size (buf) >= 2) {
1525 gst_buffer_extract (buf, 0, &profile, 1);
1543 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1552 * gst_matroska_mux_audio_pad_setcaps:
1553 * @pad: Pad which got the caps.
1556 * Setcaps function for audio sink pad.
1558 * Returns: #TRUE on success.
1561 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1563 GstMatroskaTrackContext *context = NULL;
1564 GstMatroskaTrackAudioContext *audiocontext;
1565 GstMatroskaMux *mux;
1566 GstMatroskaPad *collect_pad;
1567 const gchar *mimetype;
1568 gint samplerate = 0, channels = 0;
1569 GstStructure *structure;
1570 const GValue *codec_data = NULL;
1571 GstBuffer *buf = NULL;
1572 const gchar *stream_format = NULL;
1574 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1577 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1578 g_assert (collect_pad);
1579 context = collect_pad->track;
1581 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1582 audiocontext = (GstMatroskaTrackAudioContext *) context;
1584 structure = gst_caps_get_structure (caps, 0);
1585 mimetype = gst_structure_get_name (structure);
1588 gst_structure_get_int (structure, "rate", &samplerate);
1589 gst_structure_get_int (structure, "channels", &channels);
1591 audiocontext->samplerate = samplerate;
1592 audiocontext->channels = channels;
1593 audiocontext->bitdepth = 0;
1594 context->default_duration = 0;
1596 codec_data = gst_structure_get_value (structure, "codec_data");
1598 buf = gst_value_get_buffer (codec_data);
1600 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1601 * data and other settings
1605 if (!strcmp (mimetype, "audio/mpeg")) {
1606 gint mpegversion = 0;
1608 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1609 switch (mpegversion) {
1615 gst_structure_get_int (structure, "layer", &layer);
1617 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1618 GST_WARNING_OBJECT (mux,
1619 "Unable to determine MPEG audio version, assuming 1");
1625 else if (layer == 2)
1627 else if (version == 2)
1632 context->default_duration =
1633 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1637 gst_matroska_mux_set_codec_id (context,
1638 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1641 gst_matroska_mux_set_codec_id (context,
1642 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1645 gst_matroska_mux_set_codec_id (context,
1646 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1655 stream_format = gst_structure_get_string (structure, "stream-format");
1656 /* check this is raw aac */
1657 if (stream_format) {
1658 if (strcmp (stream_format, "raw") != 0) {
1659 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1663 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1668 if (mpegversion == 2)
1670 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1671 aac_codec_data_to_codec_id (buf));
1672 else if (mpegversion == 4)
1674 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1675 aac_codec_data_to_codec_id (buf));
1677 g_assert_not_reached ();
1679 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1686 } else if (!strcmp (mimetype, "audio/x-raw")) {
1689 gst_audio_info_init (&info);
1690 if (!gst_audio_info_from_caps (&info, caps)) {
1691 GST_DEBUG_OBJECT (mux,
1692 "broken caps, rejected by gst_audio_info_from_caps");
1696 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1697 case GST_AUDIO_FORMAT_U8:
1698 case GST_AUDIO_FORMAT_S16BE:
1699 case GST_AUDIO_FORMAT_S16LE:
1700 case GST_AUDIO_FORMAT_S24BE:
1701 case GST_AUDIO_FORMAT_S24LE:
1702 case GST_AUDIO_FORMAT_S32BE:
1703 case GST_AUDIO_FORMAT_S32LE:
1704 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1705 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1708 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1709 gst_matroska_mux_set_codec_id (context,
1710 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1712 gst_matroska_mux_set_codec_id (context,
1713 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1715 case GST_AUDIO_FORMAT_F32LE:
1716 case GST_AUDIO_FORMAT_F64LE:
1717 gst_matroska_mux_set_codec_id (context,
1718 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1722 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1726 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1727 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1728 const GValue *streamheader;
1730 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1732 if (context->codec_priv != NULL) {
1733 g_free (context->codec_priv);
1734 context->codec_priv = NULL;
1735 context->codec_priv_size = 0;
1738 streamheader = gst_structure_get_value (structure, "streamheader");
1739 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1740 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1741 ("vorbis stream headers missing or malformed"));
1744 } else if (!strcmp (mimetype, "audio/x-flac")) {
1745 const GValue *streamheader;
1747 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1748 if (context->codec_priv != NULL) {
1749 g_free (context->codec_priv);
1750 context->codec_priv = NULL;
1751 context->codec_priv_size = 0;
1754 streamheader = gst_structure_get_value (structure, "streamheader");
1755 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1756 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1757 ("flac stream headers missing or malformed"));
1760 } else if (!strcmp (mimetype, "audio/x-speex")) {
1761 const GValue *streamheader;
1763 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1764 if (context->codec_priv != NULL) {
1765 g_free (context->codec_priv);
1766 context->codec_priv = NULL;
1767 context->codec_priv_size = 0;
1770 streamheader = gst_structure_get_value (structure, "streamheader");
1771 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1772 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1773 ("speex stream headers missing or malformed"));
1776 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1777 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1778 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1779 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1780 } else if (!strcmp (mimetype, "audio/x-dts")) {
1781 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1782 } else if (!strcmp (mimetype, "audio/x-tta")) {
1785 /* TTA frame duration */
1786 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1788 gst_structure_get_int (structure, "width", &width);
1789 audiocontext->bitdepth = width;
1790 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1792 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1794 const GValue *mdpr_data;
1796 gst_structure_get_int (structure, "raversion", &raversion);
1797 switch (raversion) {
1799 gst_matroska_mux_set_codec_id (context,
1800 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1803 gst_matroska_mux_set_codec_id (context,
1804 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1807 gst_matroska_mux_set_codec_id (context,
1808 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1814 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1815 if (mdpr_data != NULL) {
1816 guint8 *priv_data = NULL;
1817 guint priv_data_size = 0;
1819 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1821 priv_data_size = gst_buffer_get_size (codec_data_buf);
1822 priv_data = g_malloc0 (priv_data_size);
1824 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1826 context->codec_priv = priv_data;
1827 context->codec_priv_size = priv_data_size;
1830 } else if (!strcmp (mimetype, "audio/x-wma")
1831 || !strcmp (mimetype, "audio/x-alaw")
1832 || !strcmp (mimetype, "audio/x-mulaw")) {
1834 guint codec_priv_size;
1839 if (samplerate == 0 || channels == 0) {
1840 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1844 if (!strcmp (mimetype, "audio/x-wma")) {
1848 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1849 || !gst_structure_get_int (structure, "block_align", &block_align)
1850 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1851 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1856 switch (wmaversion) {
1858 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1861 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1864 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1867 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1871 if (gst_structure_get_int (structure, "depth", &depth))
1872 audiocontext->bitdepth = depth;
1873 } else if (!strcmp (mimetype, "audio/x-alaw")
1874 || !strcmp (mimetype, "audio/x-mulaw")) {
1875 audiocontext->bitdepth = 8;
1876 if (!strcmp (mimetype, "audio/x-alaw"))
1877 format = GST_RIFF_WAVE_FORMAT_ALAW;
1879 format = GST_RIFF_WAVE_FORMAT_MULAW;
1881 block_align = channels;
1882 bitrate = block_align * samplerate;
1884 g_assert (format != 0);
1886 codec_priv_size = WAVEFORMATEX_SIZE;
1888 codec_priv_size += gst_buffer_get_size (buf);
1890 /* serialize waveformatex structure */
1891 codec_priv = g_malloc0 (codec_priv_size);
1892 GST_WRITE_UINT16_LE (codec_priv, format);
1893 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1894 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1895 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1896 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1897 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1899 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1901 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1903 /* process codec private/initialization data, if any */
1905 gst_buffer_extract (buf, 0,
1906 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1909 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1910 context->codec_priv = (gpointer) codec_priv;
1911 context->codec_priv_size = codec_priv_size;
1919 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1920 GST_PAD_NAME (pad), caps);
1925 /* we probably don't have the data at start,
1926 * so have to reserve (a maximum) space to write this at the end.
1927 * bit spacy, but some formats can hold quite some */
1928 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1931 * gst_matroska_mux_subtitle_pad_setcaps:
1932 * @pad: Pad which got the caps.
1935 * Setcaps function for subtitle sink pad.
1937 * Returns: #TRUE on success.
1940 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1942 /* There is now (at least) one such alement (kateenc), and I'm going
1943 to handle it here and claim it works when it can be piped back
1944 through GStreamer and VLC */
1946 GstMatroskaTrackContext *context = NULL;
1947 GstMatroskaTrackSubtitleContext *scontext;
1948 GstMatroskaMux *mux;
1949 GstMatroskaPad *collect_pad;
1950 const gchar *mimetype;
1951 GstStructure *structure;
1952 const GValue *value = NULL;
1953 GstBuffer *buf = NULL;
1955 gboolean ret = TRUE;
1957 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1960 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1961 g_assert (collect_pad);
1962 context = collect_pad->track;
1964 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1965 scontext = (GstMatroskaTrackSubtitleContext *) context;
1967 structure = gst_caps_get_structure (caps, 0);
1968 mimetype = gst_structure_get_name (structure);
1970 /* keep track of default set in request_pad */
1971 id = context->codec_id;
1974 scontext->check_utf8 = 1;
1975 scontext->invalid_utf8 = 0;
1976 context->default_duration = 0;
1978 if (!strcmp (mimetype, "subtitle/x-kate")) {
1979 const GValue *streamheader;
1981 gst_matroska_mux_set_codec_id (context,
1982 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1984 if (context->codec_priv != NULL) {
1985 g_free (context->codec_priv);
1986 context->codec_priv = NULL;
1987 context->codec_priv_size = 0;
1990 streamheader = gst_structure_get_value (structure, "streamheader");
1991 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1992 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1993 ("kate stream headers missing or malformed"));
1997 } else if (!strcmp (mimetype, "text/plain")) {
1998 gst_matroska_mux_set_codec_id (context,
1999 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2000 } else if (!strcmp (mimetype, "application/x-ssa")) {
2001 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2002 } else if (!strcmp (mimetype, "application/x-ass")) {
2003 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2004 } else if (!strcmp (mimetype, "application/x-usf")) {
2005 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2006 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2007 gst_matroska_mux_set_codec_id (context,
2008 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2015 /* maybe some private data, e.g. vobsub */
2016 value = gst_structure_get_value (structure, "codec_data");
2018 buf = gst_value_get_buffer (value);
2020 guint8 *priv_data = NULL, *priv_buffer_data;
2021 gsize priv_data_size = 0;
2024 gst_buffer_map (buf, &priv_data_size, NULL, GST_MAP_READ);
2025 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2026 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2027 " exceeded maximum (%d); discarding", pad,
2028 SUBTITLE_MAX_CODEC_PRIVATE);
2029 gst_buffer_unmap (buf, priv_data, priv_data_size);
2033 if (context->codec_priv != NULL)
2034 g_free (context->codec_priv);
2036 priv_data = g_malloc0 (priv_data_size);
2037 memcpy (priv_data, priv_buffer_data, priv_data_size);
2038 context->codec_priv = priv_data;
2039 context->codec_priv_size = priv_data_size;
2040 gst_buffer_unmap (buf, priv_buffer_data, priv_data_size);
2043 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2044 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2047 /* free default if modified */
2056 * gst_matroska_mux_request_new_pad:
2057 * @element: #GstMatroskaMux.
2058 * @templ: #GstPadTemplate.
2059 * @pad_name: New pad name.
2061 * Request pad function for sink templates.
2063 * Returns: New #GstPad.
2066 gst_matroska_mux_request_new_pad (GstElement * element,
2067 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2069 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2070 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2071 GstMatroskaPad *collect_pad;
2072 GstMatroskamuxPad *newpad;
2074 const gchar *pad_name = NULL;
2075 GstMatroskaCapsFunc capsfunc = NULL;
2076 GstMatroskaTrackContext *context = NULL;
2078 gboolean locked = TRUE;
2081 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2082 /* don't mix named and unnamed pads, if the pad already exists we fail when
2083 * trying to add it */
2084 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2085 pad_name = req_name;
2087 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2090 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2091 context = (GstMatroskaTrackContext *)
2092 g_new0 (GstMatroskaTrackAudioContext, 1);
2093 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2094 context->name = g_strdup ("Audio");
2095 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2096 /* don't mix named and unnamed pads, if the pad already exists we fail when
2097 * trying to add it */
2098 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2099 pad_name = req_name;
2101 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2104 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2105 context = (GstMatroskaTrackContext *)
2106 g_new0 (GstMatroskaTrackVideoContext, 1);
2107 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2108 context->name = g_strdup ("Video");
2109 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2110 /* don't mix named and unnamed pads, if the pad already exists we fail when
2111 * trying to add it */
2112 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2113 pad_name = req_name;
2115 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2118 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2119 context = (GstMatroskaTrackContext *)
2120 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2121 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2122 context->name = g_strdup ("Subtitle");
2123 /* setcaps may only provide proper one a lot later */
2124 id = g_strdup ("S_SUB_UNKNOWN");
2127 GST_WARNING_OBJECT (mux, "This is not our template!");
2131 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2132 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2135 gst_matroskamux_pad_init (newpad);
2136 collect_pad = (GstMatroskaPad *)
2137 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2138 sizeof (GstMatroskamuxPad),
2139 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2141 collect_pad->track = context;
2142 gst_matroska_pad_reset (collect_pad, FALSE);
2143 collect_pad->track->codec_id = id;
2145 collect_pad->capsfunc = capsfunc;
2146 gst_pad_set_active (GST_PAD (newpad), TRUE);
2147 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2148 goto pad_add_failed;
2152 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2154 return GST_PAD (newpad);
2159 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2160 gst_object_unref (newpad);
2166 * gst_matroska_mux_release_pad:
2167 * @element: #GstMatroskaMux.
2168 * @pad: Pad to release.
2170 * Release a previously requested pad.
2173 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2175 GstMatroskaMux *mux;
2178 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2180 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2181 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2182 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2184 if (cdata->pad == pad) {
2185 GstClockTime min_dur; /* observed minimum duration */
2187 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2188 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2189 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2190 if (collect_pad->duration < min_dur)
2191 collect_pad->duration = min_dur;
2194 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2195 mux->duration < collect_pad->duration)
2196 mux->duration = collect_pad->duration;
2202 gst_collect_pads2_remove_pad (mux->collect, pad);
2203 if (gst_element_remove_pad (element, pad))
2209 * gst_matroska_mux_track_header:
2210 * @mux: #GstMatroskaMux
2211 * @context: Tack context.
2213 * Write a track header.
2216 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2217 GstMatroskaTrackContext * context)
2219 GstEbmlWrite *ebml = mux->ebml_write;
2222 /* TODO: check if everything necessary is written and check default values */
2224 /* track type goes before the type-specific stuff */
2225 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2226 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2228 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2229 gst_matroska_mux_create_uid ());
2230 if (context->default_duration) {
2231 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2232 context->default_duration);
2234 if (context->language) {
2235 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2239 /* FIXME: until we have a nice way of getting the codecname
2240 * out of the caps, I'm not going to enable this. Too much
2241 * (useless, double, boring) work... */
2242 /* TODO: Use value from tags if any */
2243 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2244 context->codec_name); */
2245 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2247 /* type-specific stuff */
2248 switch (context->type) {
2249 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2250 GstMatroskaTrackVideoContext *videocontext =
2251 (GstMatroskaTrackVideoContext *) context;
2253 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2254 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2255 videocontext->pixel_width);
2256 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2257 videocontext->pixel_height);
2258 if (videocontext->display_width && videocontext->display_height) {
2259 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2260 videocontext->display_width);
2261 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2262 videocontext->display_height);
2264 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2265 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2266 if (videocontext->fourcc) {
2267 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2269 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2270 (gpointer) & fcc_le, 4);
2272 gst_ebml_write_master_finish (ebml, master);
2277 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2278 GstMatroskaTrackAudioContext *audiocontext =
2279 (GstMatroskaTrackAudioContext *) context;
2281 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2282 if (audiocontext->samplerate != 8000)
2283 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2284 audiocontext->samplerate);
2285 if (audiocontext->channels != 1)
2286 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2287 audiocontext->channels);
2288 if (audiocontext->bitdepth) {
2289 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2290 audiocontext->bitdepth);
2292 gst_ebml_write_master_finish (ebml, master);
2297 /* this is what we write for now and must be filled
2298 * and remainder void'ed later on */
2299 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2301 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2304 context->pos = ebml->pos;
2305 /* CodecID is mandatory ... */
2306 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2308 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2309 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2310 SUBTITLE_MAX_CODEC_PRIVATE);
2312 /* real data has to be written at finish */
2316 /* doesn't need type-specific data */
2320 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2321 if (context->codec_priv)
2322 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2323 context->codec_priv, context->codec_priv_size);
2328 * gst_matroska_mux_start:
2329 * @mux: #GstMatroskaMux
2331 * Start a new matroska file (write headers etc...)
2334 gst_matroska_mux_start (GstMatroskaMux * mux)
2336 GstEbmlWrite *ebml = mux->ebml_write;
2337 const gchar *doctype;
2338 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2339 GST_MATROSKA_ID_TRACKS,
2340 GST_MATROSKA_ID_CUES,
2341 GST_MATROSKA_ID_TAGS,
2344 guint64 master, child;
2348 GstClockTime duration = 0;
2349 guint32 segment_uid[4];
2350 GTimeVal time = { 0, 0 };
2352 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2353 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2355 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2357 /* we start with a EBML header */
2358 doctype = mux->doctype;
2359 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2360 doctype, mux->doctype_version);
2361 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2363 /* the rest of the header is cached */
2364 gst_ebml_write_set_cache (ebml, 0x1000);
2366 /* start a segment */
2368 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2369 mux->segment_master = ebml->pos;
2371 if (!mux->streamable) {
2372 /* seekhead (table of contents) - we set the positions later */
2373 mux->seekhead_pos = ebml->pos;
2374 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2375 for (i = 0; seekhead_id[i] != 0; i++) {
2376 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2377 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2378 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2379 gst_ebml_write_master_finish (ebml, child);
2381 gst_ebml_write_master_finish (ebml, master);
2384 if (mux->streamable) {
2385 const GstTagList *tags;
2388 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2390 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2391 guint64 master_tags, master_tag;
2393 GST_DEBUG_OBJECT (mux, "Writing tags");
2395 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2396 mux->tags_pos = ebml->pos;
2397 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2398 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2399 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2400 gst_ebml_write_master_finish (ebml, master_tag);
2401 gst_ebml_write_master_finish (ebml, master_tags);
2406 mux->info_pos = ebml->pos;
2407 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2408 for (i = 0; i < 4; i++) {
2409 segment_uid[i] = g_random_int ();
2411 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2412 (guint8 *) segment_uid, 16);
2413 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2414 mux->duration_pos = ebml->pos;
2416 if (!mux->streamable) {
2417 for (collected = mux->collect->data; collected;
2418 collected = g_slist_next (collected)) {
2419 GstMatroskaPad *collect_pad;
2421 gint64 trackduration;
2423 collect_pad = (GstMatroskaPad *) collected->data;
2424 thepad = collect_pad->collect.pad;
2426 /* Query the total length of the track. */
2427 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2428 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2429 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2430 GST_TIME_ARGS (trackduration));
2431 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2432 duration = (GstClockTime) trackduration;
2436 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2437 gst_guint64_to_gdouble (duration) /
2438 gst_guint64_to_gdouble (mux->time_scale));
2440 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2441 "GStreamer plugin version " PACKAGE_VERSION);
2442 if (mux->writing_app && mux->writing_app[0]) {
2443 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2445 g_get_current_time (&time);
2446 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2447 gst_ebml_write_master_finish (ebml, master);
2450 mux->tracks_pos = ebml->pos;
2451 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2453 for (collected = mux->collect->data; collected;
2454 collected = g_slist_next (collected)) {
2455 GstMatroskaPad *collect_pad;
2458 collect_pad = (GstMatroskaPad *) collected->data;
2459 thepad = collect_pad->collect.pad;
2461 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2462 collect_pad->track->codec_id != 0) {
2463 collect_pad->track->num = tracknum++;
2464 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2465 gst_matroska_mux_track_header (mux, collect_pad->track);
2466 gst_ebml_write_master_finish (ebml, child);
2467 /* some remaining pad/track setup */
2468 collect_pad->default_duration_scaled =
2469 gst_util_uint64_scale (collect_pad->track->default_duration,
2470 1, mux->time_scale);
2473 gst_ebml_write_master_finish (ebml, master);
2475 /* lastly, flush the cache */
2476 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2480 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2483 /* TODO: more sensible tag mappings */
2486 const gchar *matroska_tagname;
2487 const gchar *gstreamer_tagname;
2491 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2492 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2493 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2494 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2495 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2496 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2497 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2498 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2499 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2500 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2501 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2502 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2503 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2504 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2505 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2507 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2509 guint64 simpletag_master;
2511 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2512 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2513 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2515 if (strcmp (tagname_gst, tag) == 0) {
2516 GValue src = { 0, };
2519 if (!gst_tag_list_copy_value (&src, list, tag))
2521 if ((dest = gst_value_serialize (&src))) {
2523 simpletag_master = gst_ebml_write_master_start (ebml,
2524 GST_MATROSKA_ID_SIMPLETAG);
2525 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2526 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2527 gst_ebml_write_master_finish (ebml, simpletag_master);
2530 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2532 g_value_unset (&src);
2540 * gst_matroska_mux_finish:
2541 * @mux: #GstMatroskaMux
2543 * Finish a new matroska file (write index etc...)
2546 gst_matroska_mux_finish (GstMatroskaMux * mux)
2548 GstEbmlWrite *ebml = mux->ebml_write;
2550 guint64 duration = 0;
2552 const GstTagList *tags;
2554 /* finish last cluster */
2556 gst_ebml_write_master_finish (ebml, mux->cluster);
2560 if (mux->index != NULL) {
2562 guint64 master, pointentry_master, trackpos_master;
2564 mux->cues_pos = ebml->pos;
2565 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2566 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2568 for (n = 0; n < mux->num_indexes; n++) {
2569 GstMatroskaIndex *idx = &mux->index[n];
2571 pointentry_master = gst_ebml_write_master_start (ebml,
2572 GST_MATROSKA_ID_POINTENTRY);
2573 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2574 idx->time / mux->time_scale);
2575 trackpos_master = gst_ebml_write_master_start (ebml,
2576 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2577 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2578 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2579 idx->pos - mux->segment_master);
2580 gst_ebml_write_master_finish (ebml, trackpos_master);
2581 gst_ebml_write_master_finish (ebml, pointentry_master);
2584 gst_ebml_write_master_finish (ebml, master);
2585 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2589 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2591 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2592 guint64 master_tags, master_tag;
2594 GST_DEBUG_OBJECT (mux, "Writing tags");
2596 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2597 mux->tags_pos = ebml->pos;
2598 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2599 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2600 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2601 gst_ebml_write_master_finish (ebml, master_tag);
2602 gst_ebml_write_master_finish (ebml, master_tags);
2605 /* update seekhead. We know that:
2606 * - a seekhead contains 4 entries.
2607 * - order of entries is as above.
2608 * - a seekhead has a 4-byte header + 8-byte length
2609 * - each entry is 2-byte master, 2-byte ID pointer,
2610 * 2-byte length pointer, all 8/1-byte length, 4-
2611 * byte ID and 8-byte length pointer, where the
2612 * length pointer starts at 20.
2613 * - all entries are local to the segment (so pos - segment_master).
2614 * - so each entry is at 12 + 20 + num * 28. */
2615 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2616 mux->info_pos - mux->segment_master);
2617 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2618 mux->tracks_pos - mux->segment_master);
2619 if (mux->index != NULL) {
2620 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2621 mux->cues_pos - mux->segment_master);
2624 guint64 my_pos = ebml->pos;
2626 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2627 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2628 gst_ebml_write_seek (ebml, my_pos);
2631 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2632 mux->tags_pos - mux->segment_master);
2635 guint64 my_pos = ebml->pos;
2637 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2638 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2639 gst_ebml_write_seek (ebml, my_pos);
2643 * - first get the overall duration
2644 * (a released track may have left a duration in here)
2645 * - write some track header data for subtitles
2647 duration = mux->duration;
2649 for (collected = mux->collect->data; collected;
2650 collected = g_slist_next (collected)) {
2651 GstMatroskaPad *collect_pad;
2652 GstClockTime min_duration; /* observed minimum duration */
2653 GstMatroskaTrackContext *context;
2654 gint voidleft = 0, fill = 0;
2657 collect_pad = (GstMatroskaPad *) collected->data;
2658 context = collect_pad->track;
2660 GST_DEBUG_OBJECT (mux,
2661 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2662 " end ts %" GST_TIME_FORMAT, collect_pad,
2663 GST_TIME_ARGS (collect_pad->start_ts),
2664 GST_TIME_ARGS (collect_pad->end_ts));
2666 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2667 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2669 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2670 if (collect_pad->duration < min_duration)
2671 collect_pad->duration = min_duration;
2672 GST_DEBUG_OBJECT (collect_pad,
2673 "final track duration: %" GST_TIME_FORMAT,
2674 GST_TIME_ARGS (collect_pad->duration));
2677 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2678 duration < collect_pad->duration)
2679 duration = collect_pad->duration;
2681 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2685 /* write subtitle type and possible private data */
2686 gst_ebml_write_seek (ebml, context->pos);
2687 /* complex way to write ascii to account for extra filling */
2688 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2689 strcpy (codec_id, context->codec_id);
2690 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2691 codec_id, strlen (context->codec_id) + 1 + fill);
2693 if (context->codec_priv)
2694 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2695 context->codec_priv, context->codec_priv_size);
2696 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2697 /* void'ify; sigh, variable sized length field */
2698 if (voidleft == 1) {
2701 } else if (voidleft && voidleft <= 128)
2702 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2703 else if (voidleft >= 130)
2704 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2705 else if (voidleft == 129) {
2706 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2707 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2711 /* seek back (optional, but do anyway) */
2712 gst_ebml_write_seek (ebml, pos);
2714 /* update duration */
2715 if (duration != 0) {
2716 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2717 GST_TIME_ARGS (duration));
2718 pos = mux->ebml_write->pos;
2719 gst_ebml_write_seek (ebml, mux->duration_pos);
2720 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2721 gst_guint64_to_gdouble (duration) /
2722 gst_guint64_to_gdouble (mux->time_scale));
2723 gst_ebml_write_seek (ebml, pos);
2726 guint64 my_pos = ebml->pos;
2728 gst_ebml_write_seek (ebml, mux->duration_pos);
2729 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2730 gst_ebml_write_seek (ebml, my_pos);
2732 GST_DEBUG_OBJECT (mux, "finishing segment");
2733 /* finish segment - this also writes element length */
2734 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2738 * gst_matroska_mux_buffer_header:
2739 * @track: Track context.
2740 * @relative_timestamp: relative timestamp of the buffer
2741 * @flags: Buffer flags.
2743 * Create a buffer containing buffer header.
2745 * Returns: New buffer.
2748 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2749 gint16 relative_timestamp, int flags)
2752 guint8 *data = g_malloc (4);
2754 hdr = gst_buffer_new_wrapped (data, 4);
2755 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2756 data[0] = track->num | 0x80;
2757 /* time relative to clustertime */
2758 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
2766 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2767 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2768 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2771 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2772 GstMatroskaPad * collect_pad, GstBuffer * buf)
2774 GstMatroskaTrackVideoContext *ctx =
2775 (GstMatroskaTrackVideoContext *) collect_pad->track;
2776 guint8 *buf_data, *data;
2779 guint32 next_parse_offset;
2780 GstBuffer *ret = NULL;
2781 gboolean is_muxing_unit = FALSE;
2783 buf_data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
2787 gst_buffer_unmap (buf, buf_data, -1);
2788 gst_buffer_unref (buf);
2792 /* Check if this buffer contains a picture or end-of-sequence packet */
2793 while (size >= 13) {
2794 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2795 gst_buffer_unmap (buf, buf_data, -1);
2796 gst_buffer_unref (buf);
2800 parse_code = GST_READ_UINT8 (data + 4);
2801 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2802 if (ctx->dirac_unit) {
2803 gst_buffer_unref (ctx->dirac_unit);
2804 ctx->dirac_unit = NULL;
2806 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2807 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2808 is_muxing_unit = TRUE;
2812 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2814 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2817 data += next_parse_offset;
2818 size -= next_parse_offset;
2821 if (ctx->dirac_unit)
2822 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2824 ctx->dirac_unit = gst_buffer_ref (buf);
2826 gst_buffer_unmap (buf, buf_data, -1);
2828 if (is_muxing_unit) {
2829 ret = gst_buffer_make_writable (ctx->dirac_unit);
2830 ctx->dirac_unit = NULL;
2831 gst_buffer_copy_into (ret, buf,
2832 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2833 gst_buffer_unref (buf);
2835 gst_buffer_unref (buf);
2843 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2847 GValue streamheader = { 0 };
2848 GValue bufval = { 0 };
2849 GstBuffer *streamheader_buffer;
2850 GstEbmlWrite *ebml = mux->ebml_write;
2852 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2853 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2854 caps = gst_caps_new_empty_simple ("video/webm");
2856 caps = gst_caps_new_empty_simple ("video/x-matroska");
2858 s = gst_caps_get_structure (caps, 0);
2859 g_value_init (&streamheader, GST_TYPE_ARRAY);
2860 g_value_init (&bufval, GST_TYPE_BUFFER);
2861 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2862 gst_value_set_buffer (&bufval, streamheader_buffer);
2863 gst_value_array_append_value (&streamheader, &bufval);
2864 g_value_unset (&bufval);
2865 gst_structure_set_value (s, "streamheader", &streamheader);
2866 g_value_unset (&streamheader);
2867 gst_caps_replace (&ebml->caps, caps);
2868 gst_buffer_unref (streamheader_buffer);
2869 gst_caps_unref (caps);
2873 * gst_matroska_mux_write_data:
2874 * @mux: #GstMatroskaMux
2875 * @collect_pad: #GstMatroskaPad with the data
2877 * Write collected data (called from gst_matroska_mux_collected).
2879 * Returns: Result of the gst_pad_push issued to write the data.
2881 static GstFlowReturn
2882 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2885 GstEbmlWrite *ebml = mux->ebml_write;
2888 gboolean write_duration;
2889 gint16 relative_timestamp;
2890 gint64 relative_timestamp64;
2891 guint64 block_duration;
2892 gboolean is_video_keyframe = FALSE;
2893 GstMatroskamuxPad *pad;
2896 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2898 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2899 if (collect_pad->track->xiph_headers_to_skip > 0) {
2900 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2901 gst_buffer_unref (buf);
2902 --collect_pad->track->xiph_headers_to_skip;
2906 /* for dirac we have to queue up everything up to a picture unit */
2907 if (collect_pad->track->codec_id != NULL &&
2908 strcmp (collect_pad->track->codec_id,
2909 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2910 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2915 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2916 * this would wreak havoc with time stored in matroska file */
2917 /* TODO: maybe calculate a timestamp by using the previous timestamp
2918 * and default duration */
2919 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2920 GST_WARNING_OBJECT (collect_pad->collect.pad,
2921 "Invalid buffer timestamp; dropping buffer");
2922 gst_buffer_unref (buf);
2926 /* set the timestamp for outgoing buffers */
2927 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2929 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2930 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2931 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2932 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2933 is_video_keyframe = TRUE;
2937 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2938 * or when we may be reaching the limit of the relative timestamp */
2939 if (mux->cluster_time +
2940 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2941 || is_video_keyframe || mux->force_key_unit_event) {
2942 if (!mux->streamable)
2943 gst_ebml_write_master_finish (ebml, mux->cluster);
2945 /* Forward the GstForceKeyUnit event after finishing the cluster */
2946 if (mux->force_key_unit_event) {
2947 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2948 mux->force_key_unit_event = NULL;
2951 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2952 mux->cluster_pos = ebml->pos;
2953 gst_ebml_write_set_cache (ebml, 0x20);
2955 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2956 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2957 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2959 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2960 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2962 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2963 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2964 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2965 mux->prev_cluster_size);
2970 mux->cluster_pos = ebml->pos;
2971 gst_ebml_write_set_cache (ebml, 0x20);
2972 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2973 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2974 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2975 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2976 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2979 /* update duration of this track */
2980 if (GST_BUFFER_DURATION_IS_VALID (buf))
2981 collect_pad->duration += GST_BUFFER_DURATION (buf);
2983 /* We currently write index entries for all video tracks or for the audio
2984 * track in a single-track audio file. This could be improved by keeping the
2985 * index only for the *first* video track. */
2987 /* TODO: index is useful for every track, should contain the number of
2988 * the block in the cluster which contains the timestamp, should also work
2989 * for files with multiple audio tracks.
2991 if (!mux->streamable &&
2992 (is_video_keyframe ||
2993 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2994 (mux->num_streams == 1)))) {
2997 if (mux->min_index_interval != 0) {
2998 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2999 if (mux->index[last_idx].track == collect_pad->track->num)
3004 if (last_idx < 0 || mux->min_index_interval == 0 ||
3005 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3006 >= mux->min_index_interval)) {
3007 GstMatroskaIndex *idx;
3009 if (mux->num_indexes % 32 == 0) {
3010 mux->index = g_renew (GstMatroskaIndex, mux->index,
3011 mux->num_indexes + 32);
3013 idx = &mux->index[mux->num_indexes++];
3015 idx->pos = mux->cluster_pos;
3016 idx->time = GST_BUFFER_TIMESTAMP (buf);
3017 idx->track = collect_pad->track->num;
3021 /* Check if the duration differs from the default duration. */
3022 write_duration = FALSE;
3024 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3025 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3026 1, mux->time_scale);
3028 /* small difference should be ok. */
3029 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3030 block_duration < collect_pad->default_duration_scaled - 1) {
3031 write_duration = TRUE;
3035 /* write the block, for doctype v2 use SimpleBlock if possible
3036 * one slice (*breath*).
3037 * FIXME: Need to do correct lacing! */
3038 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3039 if (relative_timestamp64 >= 0) {
3040 /* round the timestamp */
3041 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3043 /* round the timestamp */
3044 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3046 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3048 if (mux->doctype_version > 1 && !write_duration) {
3050 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3053 gst_matroska_mux_create_buffer_header (collect_pad->track,
3054 relative_timestamp, flags);
3055 gst_ebml_write_set_cache (ebml, 0x40);
3056 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3057 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3058 gst_ebml_write_buffer (ebml, hdr);
3059 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3060 gst_ebml_write_buffer (ebml, buf);
3062 return gst_ebml_last_write_result (ebml);
3064 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3065 /* write and call order slightly unnatural,
3066 * but avoids seek and minizes pushing */
3067 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3069 gst_matroska_mux_create_buffer_header (collect_pad->track,
3070 relative_timestamp, 0);
3072 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3073 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3074 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3075 gst_ebml_write_buffer (ebml, hdr);
3076 gst_ebml_write_master_finish_full (ebml, blockgroup,
3077 gst_buffer_get_size (buf));
3078 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3079 gst_ebml_write_buffer (ebml, buf);
3081 return gst_ebml_last_write_result (ebml);
3086 * gst_matroska_mux_handle_buffer:
3087 * @pads: #GstCollectPads2
3088 * @uuser_data: #GstMatroskaMux
3090 * Collectpads callback.
3092 * Returns: #GstFlowReturn
3094 static GstFlowReturn
3095 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3096 GstBuffer * buf, gpointer user_data)
3098 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3099 GstEbmlWrite *ebml = mux->ebml_write;
3100 GstMatroskaPad *best;
3101 GstFlowReturn ret = GST_FLOW_OK;
3103 GST_DEBUG_OBJECT (mux, "Collected pads");
3105 /* start with a header */
3106 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3107 if (mux->collect->data == NULL) {
3108 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3109 ("No input streams configured"));
3110 return GST_FLOW_ERROR;
3112 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3113 gst_ebml_start_streamheader (ebml);
3114 gst_matroska_mux_start (mux);
3115 gst_matroska_mux_stop_streamheader (mux);
3116 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3119 /* provided with stream to write from */
3120 best = (GstMatroskaPad *) data;
3122 /* if there is no best pad, we have reached EOS */
3124 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3125 if (!mux->streamable) {
3126 gst_matroska_mux_finish (mux);
3128 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3130 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3135 /* if we have a best stream, should also have a buffer */
3138 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3139 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3140 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3141 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3143 /* make note of first and last encountered timestamps, so we can calculate
3144 * the actual duration later when we send an updated header on eos */
3145 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3146 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3147 GstClockTime end_ts = start_ts;
3149 if (GST_BUFFER_DURATION_IS_VALID (buf))
3150 end_ts += GST_BUFFER_DURATION (buf);
3151 else if (best->track->default_duration)
3152 end_ts += best->track->default_duration;
3154 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3155 best->end_ts = end_ts;
3157 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3158 start_ts < best->start_ts))
3159 best->start_ts = start_ts;
3162 /* write one buffer */
3163 ret = gst_matroska_mux_write_data (mux, best, buf);
3171 * gst_matroska_mux_change_state:
3172 * @element: #GstMatroskaMux
3173 * @transition: State change transition.
3175 * Change the muxer state.
3177 * Returns: #GstStateChangeReturn
3179 static GstStateChangeReturn
3180 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3182 GstStateChangeReturn ret;
3183 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3185 switch (transition) {
3186 case GST_STATE_CHANGE_NULL_TO_READY:
3188 case GST_STATE_CHANGE_READY_TO_PAUSED:
3189 gst_collect_pads2_start (mux->collect);
3191 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3193 case GST_STATE_CHANGE_PAUSED_TO_READY:
3194 gst_collect_pads2_stop (mux->collect);
3200 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3202 switch (transition) {
3203 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3205 case GST_STATE_CHANGE_PAUSED_TO_READY:
3206 gst_matroska_mux_reset (GST_ELEMENT (mux));
3208 case GST_STATE_CHANGE_READY_TO_NULL:
3218 gst_matroska_mux_set_property (GObject * object,
3219 guint prop_id, const GValue * value, GParamSpec * pspec)
3221 GstMatroskaMux *mux;
3223 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3224 mux = GST_MATROSKA_MUX (object);
3227 case ARG_WRITING_APP:
3228 if (!g_value_get_string (value)) {
3229 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3232 g_free (mux->writing_app);
3233 mux->writing_app = g_value_dup_string (value);
3235 case ARG_DOCTYPE_VERSION:
3236 mux->doctype_version = g_value_get_int (value);
3238 case ARG_MIN_INDEX_INTERVAL:
3239 mux->min_index_interval = g_value_get_int64 (value);
3241 case ARG_STREAMABLE:
3242 mux->streamable = g_value_get_boolean (value);
3245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3251 gst_matroska_mux_get_property (GObject * object,
3252 guint prop_id, GValue * value, GParamSpec * pspec)
3254 GstMatroskaMux *mux;
3256 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3257 mux = GST_MATROSKA_MUX (object);
3260 case ARG_WRITING_APP:
3261 g_value_set_string (value, mux->writing_app);
3263 case ARG_DOCTYPE_VERSION:
3264 g_value_set_int (value, mux->doctype_version);
3266 case ARG_MIN_INDEX_INTERVAL:
3267 g_value_set_int64 (value, mux->min_index_interval);
3269 case ARG_STREAMABLE:
3270 g_value_set_boolean (value, mux->streamable);
3273 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);