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