1 /* GStreamer Matroska muxer/demuxer
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
7 * matroska-mux.c: matroska file/stream muxer
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
30 * SECTION:element-matroskamux
32 * matroskamux muxes different input streams into a Matroska file.
35 * <title>Example launch line</title>
37 * gst-launch -v filesrc location=/path/to/mp3 ! mp3parse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
40 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
57 #include "matroska-mux.h"
58 #include "matroska-ids.h"
60 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
61 #define GST_CAT_DEFAULT matroskamux_debug
68 ARG_MIN_INDEX_INTERVAL,
72 #define DEFAULT_DOCTYPE_VERSION 2
73 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
74 #define DEFAULT_MIN_INDEX_INTERVAL 0
75 #define DEFAULT_STREAMABLE FALSE
77 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
78 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
80 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
83 GST_STATIC_CAPS ("video/x-matroska")
86 #define COMMON_VIDEO_CAPS \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ], " \
89 "framerate = (fraction) [ 0, MAX ]"
91 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
92 "width = (int) [ 16, 4096 ], " \
93 "height = (int) [ 16, 4096 ] "
96 * * require codec data, etc as needed
99 static GstStaticPadTemplate videosink_templ =
100 GST_STATIC_PAD_TEMPLATE ("video_%u",
103 GST_STATIC_CAPS ("video/mpeg, "
104 "mpegversion = (int) { 1, 2, 4 }, "
105 "systemstream = (boolean) false, "
106 COMMON_VIDEO_CAPS "; "
107 "video/x-h264, stream-format=avc, alignment=au, "
108 COMMON_VIDEO_CAPS "; "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS "; "
114 COMMON_VIDEO_CAPS "; "
116 COMMON_VIDEO_CAPS "; "
118 COMMON_VIDEO_CAPS "; "
120 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
125 COMMON_VIDEO_CAPS "; "
126 "video/x-pn-realvideo, "
127 "rmversion = (int) [1, 4], "
128 COMMON_VIDEO_CAPS "; "
130 COMMON_VIDEO_CAPS "; "
132 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
133 COMMON_VIDEO_CAPS "; "
134 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
137 #define COMMON_AUDIO_CAPS \
138 "channels = (int) [ 1, MAX ], " \
139 "rate = (int) [ 1, MAX ]"
142 * * require codec data, etc as needed
144 static GstStaticPadTemplate audiosink_templ =
145 GST_STATIC_PAD_TEMPLATE ("audio_%u",
148 GST_STATIC_CAPS ("audio/mpeg, "
149 "mpegversion = (int) 1, "
150 "layer = (int) [ 1, 3 ], "
151 COMMON_AUDIO_CAPS "; "
153 "mpegversion = (int) { 2, 4 }, "
154 "stream-format = (string) raw, "
155 COMMON_AUDIO_CAPS "; "
157 COMMON_AUDIO_CAPS "; "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
167 COMMON_AUDIO_CAPS "; "
169 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
170 "layout = (string) interleaved, "
171 COMMON_AUDIO_CAPS ";"
173 "width = (int) { 8, 16, 24 }, "
174 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
175 "audio/x-pn-realaudio, "
176 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
177 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
178 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
179 COMMON_AUDIO_CAPS ";"
181 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
183 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
186 static GstStaticPadTemplate subtitlesink_templ =
187 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
190 GST_STATIC_CAPS ("subtitle/x-kate; "
191 "text/plain; application/x-ssa; application/x-ass; "
192 "application/x-usf; video/x-dvd-subpicture; "
193 "application/x-subtitle-unknown")
196 static GArray *used_uids;
197 G_LOCK_DEFINE_STATIC (used_uids);
199 #define parent_class gst_matroska_mux_parent_class
200 G_DEFINE_TYPE_WITH_CODE (GstMatroskaMux, gst_matroska_mux, GST_TYPE_ELEMENT,
201 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
203 /* Matroska muxer destructor */
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
207 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
208 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
209 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
210 GstCollectData2 * data, GstEvent * event, gpointer user_data);
213 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
214 GstObject * parent, GstEvent * event);
215 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
216 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
217 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
219 /* gst internal change state handler */
220 static GstStateChangeReturn
221 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
223 /* gobject bla bla */
224 static void gst_matroska_mux_set_property (GObject * object,
225 guint prop_id, const GValue * value, GParamSpec * pspec);
226 static void gst_matroska_mux_get_property (GObject * object,
227 guint prop_id, GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_reset (GstElement * element);
233 static guint64 gst_matroska_mux_create_uid ();
235 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
246 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
250 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
252 GObjectClass *gobject_class;
253 GstElementClass *gstelement_class;
255 gobject_class = (GObjectClass *) klass;
256 gstelement_class = (GstElementClass *) klass;
258 gst_element_class_add_pad_template (gstelement_class,
259 gst_static_pad_template_get (&videosink_templ));
260 gst_element_class_add_pad_template (gstelement_class,
261 gst_static_pad_template_get (&audiosink_templ));
262 gst_element_class_add_pad_template (gstelement_class,
263 gst_static_pad_template_get (&subtitlesink_templ));
264 gst_element_class_add_pad_template (gstelement_class,
265 gst_static_pad_template_get (&src_templ));
266 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
268 "Muxes video/audio/subtitle streams into a matroska stream",
269 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
271 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
274 gobject_class->finalize = gst_matroska_mux_finalize;
276 gobject_class->get_property = gst_matroska_mux_get_property;
277 gobject_class->set_property = gst_matroska_mux_set_property;
279 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
280 g_param_spec_string ("writing-app", "Writing application.",
281 "The name the application that creates the matroska file.",
282 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
284 g_param_spec_int ("version", "DocType version",
285 "This parameter determines what Matroska features can be used.",
286 1, 2, DEFAULT_DOCTYPE_VERSION,
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
289 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
290 "entries", "An index entry is created every so many nanoseconds.",
291 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
292 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
294 g_param_spec_boolean ("streamable", "Determines whether output should "
295 "be streamable", "If set to true, the output should be as if it is "
296 "to be streamed and hence no indexes written or duration written.",
298 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
300 gstelement_class->change_state =
301 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
302 gstelement_class->request_new_pad =
303 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
304 gstelement_class->release_pad =
305 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
309 * Start of pad option handler code
311 #define DEFAULT_PAD_FRAME_DURATION TRUE
312 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
317 PROP_PAD_FRAME_DURATION
323 gboolean frame_duration;
324 gboolean frame_duration_user;
327 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
330 gst_matroskamux_pad_get_type (void)
332 static GType type = 0;
334 if (G_UNLIKELY (type == 0)) {
335 type = g_type_register_static_simple (GST_TYPE_PAD,
336 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
337 (GClassInitFunc) gst_matroskamux_pad_class_init,
338 sizeof (GstMatroskamuxPad), NULL, 0);
343 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
344 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
345 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
346 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
349 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
350 GValue * value, GParamSpec * pspec)
352 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
355 case PROP_PAD_FRAME_DURATION:
356 g_value_set_boolean (value, pad->frame_duration);
359 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
366 const GValue * value, GParamSpec * pspec)
368 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
371 case PROP_PAD_FRAME_DURATION:
372 pad->frame_duration = g_value_get_boolean (value);
373 pad->frame_duration_user = TRUE;
376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382 gst_matroskamux_pad_class_init (GstPadClass * klass)
384 GObjectClass *gobject_class = (GObjectClass *) klass;
386 gobject_class->set_property = gst_matroskamux_pad_set_property;
387 gobject_class->get_property = gst_matroskamux_pad_get_property;
389 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
390 g_param_spec_boolean ("frame-duration", "Frame duration",
391 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
392 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
396 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
398 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
399 pad->frame_duration_user = FALSE;
403 * End of pad option handler code
407 * gst_matroska_mux_init:
408 * @mux: #GstMatroskaMux that should be initialized.
409 * @g_class: Class of the muxer.
411 * Matroska muxer constructor.
414 gst_matroska_mux_init (GstMatroskaMux * mux)
416 GstPadTemplate *templ;
419 gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mux), "src");
420 mux->srcpad = gst_pad_new_from_template (templ, "src");
422 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
423 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
425 mux->collect = gst_collect_pads2_new ();
426 gst_collect_pads2_set_clip_function (mux->collect,
427 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
428 gst_collect_pads2_set_buffer_function (mux->collect,
429 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
430 gst_collect_pads2_set_event_function (mux->collect,
431 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
433 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
434 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
436 /* property defaults */
437 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
438 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
439 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
440 mux->streamable = DEFAULT_STREAMABLE;
442 /* initialize internal variables */
444 mux->num_streams = 0;
445 mux->num_a_streams = 0;
446 mux->num_t_streams = 0;
447 mux->num_v_streams = 0;
449 /* initialize remaining variables */
450 gst_matroska_mux_reset (GST_ELEMENT (mux));
455 * gst_matroska_mux_finalize:
456 * @object: #GstMatroskaMux that should be finalized.
458 * Finalize matroska muxer.
461 gst_matroska_mux_finalize (GObject * object)
463 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
465 gst_event_replace (&mux->force_key_unit_event, NULL);
467 gst_object_unref (mux->collect);
468 gst_object_unref (mux->ebml_write);
469 if (mux->writing_app)
470 g_free (mux->writing_app);
472 G_OBJECT_CLASS (parent_class)->finalize (object);
477 * gst_matroska_mux_create_uid:
479 * Generate new unused track UID.
481 * Returns: New track UID.
484 gst_matroska_mux_create_uid (void)
491 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
496 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
497 for (i = 0; i < used_uids->len; i++) {
498 if (g_array_index (used_uids, guint64, i) == uid) {
503 g_array_append_val (used_uids, uid);
506 G_UNLOCK (used_uids);
512 * gst_matroska_pad_reset:
513 * @collect_pad: the #GstMatroskaPad
515 * Reset and/or release resources of a matroska collect pad.
518 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
521 GstMatroskaTrackType type = 0;
523 /* free track information */
524 if (collect_pad->track != NULL) {
525 /* retrieve for optional later use */
526 name = collect_pad->track->name;
527 type = collect_pad->track->type;
528 /* extra for video */
529 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
530 GstMatroskaTrackVideoContext *ctx =
531 (GstMatroskaTrackVideoContext *) collect_pad->track;
533 if (ctx->dirac_unit) {
534 gst_buffer_unref (ctx->dirac_unit);
535 ctx->dirac_unit = NULL;
538 g_free (collect_pad->track->codec_id);
539 g_free (collect_pad->track->codec_name);
541 g_free (collect_pad->track->name);
542 g_free (collect_pad->track->language);
543 g_free (collect_pad->track->codec_priv);
544 g_free (collect_pad->track);
545 collect_pad->track = NULL;
548 if (!full && type != 0) {
549 GstMatroskaTrackContext *context;
551 /* create a fresh context */
553 case GST_MATROSKA_TRACK_TYPE_VIDEO:
554 context = (GstMatroskaTrackContext *)
555 g_new0 (GstMatroskaTrackVideoContext, 1);
557 case GST_MATROSKA_TRACK_TYPE_AUDIO:
558 context = (GstMatroskaTrackContext *)
559 g_new0 (GstMatroskaTrackAudioContext, 1);
561 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
562 context = (GstMatroskaTrackContext *)
563 g_new0 (GstMatroskaTrackSubtitleContext, 1);
566 g_assert_not_reached ();
570 context->type = type;
571 context->name = name;
572 /* TODO: check default values for the context */
573 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
574 collect_pad->track = context;
575 collect_pad->duration = 0;
576 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
577 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
582 * gst_matroska_pad_free:
583 * @collect_pad: the #GstMatroskaPad
585 * Release resources of a matroska collect pad.
588 gst_matroska_pad_free (GstPad * collect_pad)
590 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
595 * gst_matroska_mux_reset:
596 * @element: #GstMatroskaMux that should be reseted.
598 * Reset matroska muxer back to initial state.
601 gst_matroska_mux_reset (GstElement * element)
603 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
606 /* reset EBML write */
607 gst_ebml_write_reset (mux->ebml_write);
610 mux->state = GST_MATROSKA_MUX_STATE_START;
612 /* clean up existing streams */
614 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
615 GstMatroskaPad *collect_pad;
617 collect_pad = (GstMatroskaPad *) walk->data;
619 /* reset collect pad to pristine state */
620 gst_matroska_pad_reset (collect_pad, FALSE);
624 mux->num_indexes = 0;
629 mux->time_scale = GST_MSECOND;
630 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
635 mux->cluster_time = 0;
636 mux->cluster_pos = 0;
637 mux->prev_cluster_size = 0;
640 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
644 * gst_matroska_mux_handle_src_event:
645 * @pad: Pad which received the event.
646 * @event: Received event.
648 * handle events - copied from oggmux without understanding
650 * Returns: #TRUE on success.
653 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
658 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
662 /* disable seeking for now */
668 return gst_pad_event_default (pad, parent, event);
673 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
675 if (context->codec_priv != NULL) {
676 g_free (context->codec_priv);
677 context->codec_priv = NULL;
678 context->codec_priv_size = 0;
683 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
693 /* produce comma-separated list in hex format */
694 for (i = 0; i < 16; ++i) {
696 /* replicate vobsub's slightly off RGB conversion calculation */
697 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
698 u = ((col >> 8) & 0xff) - 128;
699 v = (col & 0xff) - 128;
700 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
701 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
702 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
703 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
706 sclut = g_strjoinv (",", clutv);
708 /* build codec private; only palette for now */
709 gst_matroska_mux_free_codec_priv (context);
710 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
711 /* include terminating 0 */
712 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
714 for (i = 0; i < 16; ++i) {
721 * gst_matroska_mux_handle_sink_event:
722 * @pad: Pad which received the event.
723 * @event: Received event.
725 * handle events - informational ones like tags
727 * Returns: #TRUE on success.
730 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
731 GstCollectData2 * data, GstEvent * event, gpointer user_data)
733 GstMatroskaPad *collect_pad;
734 GstMatroskaTrackContext *context;
738 gboolean ret = FALSE;
740 mux = GST_MATROSKA_MUX (user_data);
741 collect_pad = (GstMatroskaPad *) data;
743 context = collect_pad->track;
746 switch (GST_EVENT_TYPE (event)) {
747 case GST_EVENT_CAPS:{
750 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
751 gst_event_parse_caps (event, &caps);
753 ret = collect_pad->capsfunc (pad, caps);
754 gst_event_unref (event);
761 GST_DEBUG_OBJECT (mux, "received tag event");
762 gst_event_parse_tag (event, &list);
764 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
765 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
766 const gchar *lang_code;
768 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
770 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
771 context->language = g_strdup (lang_code);
773 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
778 /* FIXME: what about stream-specific tags? */
779 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
780 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
782 gst_event_unref (event);
783 /* handled this, don't want collectpads to forward it downstream */
787 case GST_EVENT_SEGMENT:{
788 const GstSegment *segment;
790 gst_event_parse_segment (event, &segment);
791 if (segment->format != GST_FORMAT_TIME) {
792 gst_event_unref (event);
797 case GST_EVENT_CUSTOM_DOWNSTREAM:{
798 const GstStructure *structure;
800 structure = gst_event_get_structure (event);
801 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
802 gst_event_replace (&mux->force_key_unit_event, NULL);
803 mux->force_key_unit_event = event;
805 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
806 !strcmp ("dvd-spu-clut-change",
807 gst_structure_get_string (structure, "event"))) {
812 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
813 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
814 GST_DEBUG_OBJECT (pad, "... discarding");
817 /* first transform event data into table form */
818 for (i = 0; i < 16; i++) {
819 g_snprintf (name, sizeof (name), "clut%02d", i);
820 if (!gst_structure_get_int (structure, name, &value)) {
821 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
822 "contain %s field", name);
828 /* transform into private data for stream; text form */
829 gst_matroska_mux_build_vobsub_private (context, clut);
837 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
842 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
845 g_assert (context && id);
846 if (context->codec_id)
847 g_free (context->codec_id);
848 context->codec_id = g_strdup (id);
852 * gst_matroska_mux_video_pad_setcaps:
853 * @pad: Pad which got the caps.
856 * Setcaps function for video sink pad.
858 * Returns: #TRUE on success.
861 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
863 GstMatroskaTrackContext *context = NULL;
864 GstMatroskaTrackVideoContext *videocontext;
866 GstMatroskaPad *collect_pad;
867 GstStructure *structure;
868 const gchar *mimetype;
869 const GValue *value = NULL;
870 GstBuffer *codec_buf = NULL;
871 gint width, height, pixel_width, pixel_height;
873 gboolean interlaced = FALSE;
875 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
878 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
879 g_assert (collect_pad);
880 context = collect_pad->track;
882 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
883 videocontext = (GstMatroskaTrackVideoContext *) context;
885 /* gst -> matroska ID'ing */
886 structure = gst_caps_get_structure (caps, 0);
888 mimetype = gst_structure_get_name (structure);
890 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
892 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
894 if (!strcmp (mimetype, "video/x-theora")) {
895 /* we'll extract the details later from the theora identification header */
899 /* get general properties */
900 /* spec says it is mandatory */
901 if (!gst_structure_get_int (structure, "width", &width) ||
902 !gst_structure_get_int (structure, "height", &height))
905 videocontext->pixel_width = width;
906 videocontext->pixel_height = height;
908 /* set vp8 defaults or let user override it */
909 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
910 && (!strcmp (mimetype, "video/x-vp8")))
911 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
912 DEFAULT_PAD_FRAME_DURATION_VP8;
914 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
915 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
917 context->default_duration =
918 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
919 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
920 GST_TIME_ARGS (context->default_duration));
922 context->default_duration = 0;
924 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
925 &pixel_width, &pixel_height)) {
926 if (pixel_width > pixel_height) {
927 videocontext->display_width = width * pixel_width / pixel_height;
928 videocontext->display_height = height;
929 } else if (pixel_width < pixel_height) {
930 videocontext->display_width = width;
931 videocontext->display_height = height * pixel_height / pixel_width;
933 videocontext->display_width = 0;
934 videocontext->display_height = 0;
937 videocontext->display_width = 0;
938 videocontext->display_height = 0;
943 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
944 videocontext->fourcc = 0;
946 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
947 * data and other settings
951 /* extract codec_data, may turn out needed */
952 value = gst_structure_get_value (structure, "codec_data");
954 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
957 if (!strcmp (mimetype, "video/x-raw")) {
959 gst_matroska_mux_set_codec_id (context,
960 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
961 fstr = gst_structure_get_string (structure, "format");
962 if (fstr && strlen (fstr) == 4)
963 videocontext->fourcc = GST_STR_FOURCC (fstr);
964 } else if (!strcmp (mimetype, "image/jpeg")) {
965 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
966 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
967 ||!strcmp (mimetype, "video/x-huffyuv")
968 || !strcmp (mimetype, "video/x-divx")
969 || !strcmp (mimetype, "video/x-dv")
970 || !strcmp (mimetype, "video/x-h263")
971 || !strcmp (mimetype, "video/x-msmpeg")
972 || !strcmp (mimetype, "video/x-wmv")
973 || !strcmp (mimetype, "image/jpeg")) {
974 gst_riff_strf_vids *bih;
975 gint size = sizeof (gst_riff_strf_vids);
978 if (!strcmp (mimetype, "video/x-xvid"))
979 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
980 else if (!strcmp (mimetype, "video/x-huffyuv"))
981 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
982 else if (!strcmp (mimetype, "video/x-dv"))
983 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
984 else if (!strcmp (mimetype, "video/x-h263"))
985 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
986 else if (!strcmp (mimetype, "video/x-divx")) {
989 gst_structure_get_int (structure, "divxversion", &divxversion);
990 switch (divxversion) {
992 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
995 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
998 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1001 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1004 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1005 switch (msmpegversion) {
1007 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1010 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1016 } else if (!strcmp (mimetype, "video/x-wmv")) {
1020 fstr = gst_structure_get_string (structure, "format");
1021 if (fstr && strlen (fstr) == 4) {
1022 fourcc = GST_STR_FOURCC (fstr);
1023 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1024 if (wmvversion == 2) {
1025 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1026 } else if (wmvversion == 1) {
1027 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1028 } else if (wmvversion == 3) {
1029 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1032 } else if (!strcmp (mimetype, "image/jpeg")) {
1033 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1039 bih = g_new0 (gst_riff_strf_vids, 1);
1040 GST_WRITE_UINT32_LE (&bih->size, size);
1041 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1042 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1043 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1044 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1045 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1046 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1047 videocontext->pixel_height * 3);
1049 /* process codec private/initialization data, if any */
1051 size += gst_buffer_get_size (codec_buf);
1052 bih = g_realloc (bih, size);
1053 GST_WRITE_UINT32_LE (&bih->size, size);
1054 gst_buffer_extract (codec_buf, 0,
1055 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1058 gst_matroska_mux_set_codec_id (context,
1059 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1060 gst_matroska_mux_free_codec_priv (context);
1061 context->codec_priv = (gpointer) bih;
1062 context->codec_priv_size = size;
1063 } else if (!strcmp (mimetype, "video/x-h264")) {
1064 gst_matroska_mux_set_codec_id (context,
1065 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1066 gst_matroska_mux_free_codec_priv (context);
1067 /* Create avcC header */
1068 if (codec_buf != NULL) {
1069 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1070 context->codec_priv = g_malloc0 (context->codec_priv_size);
1071 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1073 } else if (!strcmp (mimetype, "video/x-theora")) {
1074 const GValue *streamheader;
1076 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1078 gst_matroska_mux_free_codec_priv (context);
1080 streamheader = gst_structure_get_value (structure, "streamheader");
1081 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1082 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1083 ("theora stream headers missing or malformed"));
1086 } else if (!strcmp (mimetype, "video/x-dirac")) {
1087 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1088 } else if (!strcmp (mimetype, "video/x-vp8")) {
1089 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1090 } else if (!strcmp (mimetype, "video/mpeg")) {
1093 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1094 switch (mpegversion) {
1096 gst_matroska_mux_set_codec_id (context,
1097 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1100 gst_matroska_mux_set_codec_id (context,
1101 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1104 gst_matroska_mux_set_codec_id (context,
1105 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1111 /* global headers may be in codec data */
1112 if (codec_buf != NULL) {
1113 gst_matroska_mux_free_codec_priv (context);
1114 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1115 context->codec_priv = g_malloc0 (context->codec_priv_size);
1116 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1118 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1120 /* can only make it here if preceding case verified it was version 3 */
1121 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1122 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1124 const GValue *mdpr_data;
1126 gst_structure_get_int (structure, "rmversion", &rmversion);
1127 switch (rmversion) {
1129 gst_matroska_mux_set_codec_id (context,
1130 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1133 gst_matroska_mux_set_codec_id (context,
1134 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1137 gst_matroska_mux_set_codec_id (context,
1138 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1141 gst_matroska_mux_set_codec_id (context,
1142 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1148 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1149 if (mdpr_data != NULL) {
1150 guint8 *priv_data = NULL;
1151 guint priv_data_size = 0;
1153 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1155 priv_data_size = gst_buffer_get_size (codec_data_buf);
1156 priv_data = g_malloc0 (priv_data_size);
1158 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1160 gst_matroska_mux_free_codec_priv (context);
1161 context->codec_priv = priv_data;
1162 context->codec_priv_size = priv_data_size;
1171 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1172 GST_PAD_NAME (pad), caps);
1177 /* N > 0 to expect a particular number of headers, negative if the
1178 number of headers is variable */
1180 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1181 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1183 GstBuffer **buf = NULL;
1186 guint bufi, i, offset, priv_data_size;
1188 if (streamheader == NULL)
1189 goto no_stream_headers;
1191 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1194 bufarr = g_value_peek_pointer (streamheader);
1195 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1197 if (N > 0 && bufarr->len != N)
1200 context->xiph_headers_to_skip = bufarr->len;
1202 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1203 for (i = 0; i < bufarr->len; i++) {
1204 GValue *bufval = &g_array_index (bufarr, GValue, i);
1206 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1208 goto wrong_content_type;
1211 buf[i] = g_value_peek_pointer (bufval);
1215 if (bufarr->len > 0) {
1216 for (i = 0; i < bufarr->len - 1; i++) {
1217 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1221 for (i = 0; i < bufarr->len; ++i) {
1222 priv_data_size += gst_buffer_get_size (buf[i]);
1225 priv_data = g_malloc0 (priv_data_size);
1227 priv_data[0] = bufarr->len - 1;
1230 if (bufarr->len > 0) {
1231 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1232 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1233 priv_data[offset++] = 0xff;
1235 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1239 for (i = 0; i < bufarr->len; ++i) {
1240 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1241 offset += gst_buffer_get_size (buf[i]);
1244 gst_matroska_mux_free_codec_priv (context);
1245 context->codec_priv = priv_data;
1246 context->codec_priv_size = priv_data_size;
1249 *p_buf0 = gst_buffer_ref (buf[0]);
1258 GST_WARNING ("required streamheaders missing in sink caps!");
1263 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1264 G_VALUE_TYPE_NAME (streamheader));
1269 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1274 GST_WARNING ("streamheaders array does not contain GstBuffers");
1280 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1281 GstMatroskaTrackContext * context)
1283 GstBuffer *buf0 = NULL;
1285 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1288 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1289 GST_WARNING ("First vorbis header too small, ignoring");
1291 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1292 GstMatroskaTrackAudioContext *audiocontext;
1295 data = gst_buffer_map (buf0, NULL, NULL, GST_MAP_READ);
1296 hdr = data + 1 + 6 + 4;
1297 audiocontext = (GstMatroskaTrackAudioContext *) context;
1298 audiocontext->channels = GST_READ_UINT8 (hdr);
1299 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1300 gst_buffer_unmap (buf0, data, -1);
1305 gst_buffer_unref (buf0);
1311 theora_streamheader_to_codecdata (const GValue * streamheader,
1312 GstMatroskaTrackContext * context)
1314 GstBuffer *buf0 = NULL;
1316 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1319 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1320 GST_WARNING ("First theora header too small, ignoring");
1321 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1322 GST_WARNING ("First header not a theora identification header, ignoring");
1324 GstMatroskaTrackVideoContext *videocontext;
1325 guint fps_num, fps_denom, par_num, par_denom;
1328 data = gst_buffer_map (buf0, NULL, NULL, GST_MAP_READ);
1329 hdr = data + 1 + 6 + 3 + 2 + 2;
1331 videocontext = (GstMatroskaTrackVideoContext *) context;
1332 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1333 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1334 hdr += 3 + 3 + 1 + 1;
1335 fps_num = GST_READ_UINT32_BE (hdr);
1336 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1337 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1338 fps_denom, fps_num);
1340 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1341 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1342 if (par_num > 0 && par_num > 0) {
1343 if (par_num > par_denom) {
1344 videocontext->display_width =
1345 videocontext->pixel_width * par_num / par_denom;
1346 videocontext->display_height = videocontext->pixel_height;
1347 } else if (par_num < par_denom) {
1348 videocontext->display_width = videocontext->pixel_width;
1349 videocontext->display_height =
1350 videocontext->pixel_height * par_denom / par_num;
1352 videocontext->display_width = 0;
1353 videocontext->display_height = 0;
1356 videocontext->display_width = 0;
1357 videocontext->display_height = 0;
1361 gst_buffer_unmap (buf0, data, -1);
1365 gst_buffer_unref (buf0);
1371 kate_streamheader_to_codecdata (const GValue * streamheader,
1372 GstMatroskaTrackContext * context)
1374 GstBuffer *buf0 = NULL;
1376 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1379 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1380 GST_WARNING ("First kate header too small, ignoring");
1381 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1382 GST_WARNING ("First header not a kate identification header, ignoring");
1386 gst_buffer_unref (buf0);
1392 flac_streamheader_to_codecdata (const GValue * streamheader,
1393 GstMatroskaTrackContext * context)
1400 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1401 GST_WARNING ("No or invalid streamheader field in the caps");
1405 bufarr = g_value_peek_pointer (streamheader);
1406 if (bufarr->len < 2) {
1407 GST_WARNING ("Too few headers in streamheader field");
1411 context->xiph_headers_to_skip = bufarr->len + 1;
1413 bufval = &g_array_index (bufarr, GValue, 0);
1414 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1415 GST_WARNING ("streamheaders array does not contain GstBuffers");
1419 buffer = g_value_peek_pointer (bufval);
1421 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1422 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1423 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1424 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1425 GST_WARNING ("Invalid streamheader for FLAC");
1429 gst_matroska_mux_free_codec_priv (context);
1430 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1431 context->codec_priv = g_malloc (context->codec_priv_size);
1432 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1434 for (i = 1; i < bufarr->len; i++) {
1436 bufval = &g_array_index (bufarr, GValue, i);
1438 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1439 gst_matroska_mux_free_codec_priv (context);
1440 GST_WARNING ("streamheaders array does not contain GstBuffers");
1444 buffer = g_value_peek_pointer (bufval);
1446 old_size = context->codec_priv_size;
1447 context->codec_priv_size += gst_buffer_get_size (buffer);
1449 context->codec_priv = g_realloc (context->codec_priv,
1450 context->codec_priv_size);
1451 gst_buffer_extract (buffer, 0,
1452 (guint8 *) context->codec_priv + old_size, -1);
1459 speex_streamheader_to_codecdata (const GValue * streamheader,
1460 GstMatroskaTrackContext * context)
1467 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1468 GST_WARNING ("No or invalid streamheader field in the caps");
1472 bufarr = g_value_peek_pointer (streamheader);
1473 if (bufarr->len != 2) {
1474 GST_WARNING ("Too few headers in streamheader field");
1478 context->xiph_headers_to_skip = bufarr->len + 1;
1480 bufval = &g_array_index (bufarr, GValue, 0);
1481 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1482 GST_WARNING ("streamheaders array does not contain GstBuffers");
1486 buffer = g_value_peek_pointer (bufval);
1488 if (gst_buffer_get_size (buffer) < 80
1489 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1490 GST_WARNING ("Invalid streamheader for Speex");
1494 gst_matroska_mux_free_codec_priv (context);
1495 context->codec_priv_size = gst_buffer_get_size (buffer);
1496 context->codec_priv = g_malloc (context->codec_priv_size);
1497 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1499 bufval = &g_array_index (bufarr, GValue, 1);
1501 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1502 gst_matroska_mux_free_codec_priv (context);
1503 GST_WARNING ("streamheaders array does not contain GstBuffers");
1507 buffer = g_value_peek_pointer (bufval);
1509 old_size = context->codec_priv_size;
1510 context->codec_priv_size += gst_buffer_get_size (buffer);
1511 context->codec_priv = g_realloc (context->codec_priv,
1512 context->codec_priv_size);
1513 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1518 static const gchar *
1519 aac_codec_data_to_codec_id (GstBuffer * buf)
1521 const gchar *result;
1524 /* default to MAIN */
1527 if (gst_buffer_get_size (buf) >= 2) {
1528 gst_buffer_extract (buf, 0, &profile, 1);
1546 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1555 * gst_matroska_mux_audio_pad_setcaps:
1556 * @pad: Pad which got the caps.
1559 * Setcaps function for audio sink pad.
1561 * Returns: #TRUE on success.
1564 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1566 GstMatroskaTrackContext *context = NULL;
1567 GstMatroskaTrackAudioContext *audiocontext;
1568 GstMatroskaMux *mux;
1569 GstMatroskaPad *collect_pad;
1570 const gchar *mimetype;
1571 gint samplerate = 0, channels = 0;
1572 GstStructure *structure;
1573 const GValue *codec_data = NULL;
1574 GstBuffer *buf = NULL;
1575 const gchar *stream_format = NULL;
1577 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1580 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1581 g_assert (collect_pad);
1582 context = collect_pad->track;
1584 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1585 audiocontext = (GstMatroskaTrackAudioContext *) context;
1587 structure = gst_caps_get_structure (caps, 0);
1588 mimetype = gst_structure_get_name (structure);
1591 gst_structure_get_int (structure, "rate", &samplerate);
1592 gst_structure_get_int (structure, "channels", &channels);
1594 audiocontext->samplerate = samplerate;
1595 audiocontext->channels = channels;
1596 audiocontext->bitdepth = 0;
1597 context->default_duration = 0;
1599 codec_data = gst_structure_get_value (structure, "codec_data");
1601 buf = gst_value_get_buffer (codec_data);
1603 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1604 * data and other settings
1608 if (!strcmp (mimetype, "audio/mpeg")) {
1609 gint mpegversion = 0;
1611 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1612 switch (mpegversion) {
1618 gst_structure_get_int (structure, "layer", &layer);
1620 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1621 GST_WARNING_OBJECT (mux,
1622 "Unable to determine MPEG audio version, assuming 1");
1628 else if (layer == 2)
1630 else if (version == 2)
1635 context->default_duration =
1636 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1640 gst_matroska_mux_set_codec_id (context,
1641 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1644 gst_matroska_mux_set_codec_id (context,
1645 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1648 gst_matroska_mux_set_codec_id (context,
1649 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1658 stream_format = gst_structure_get_string (structure, "stream-format");
1659 /* check this is raw aac */
1660 if (stream_format) {
1661 if (strcmp (stream_format, "raw") != 0) {
1662 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1666 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1671 if (mpegversion == 2)
1673 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1674 aac_codec_data_to_codec_id (buf));
1675 else if (mpegversion == 4)
1677 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1678 aac_codec_data_to_codec_id (buf));
1680 g_assert_not_reached ();
1682 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1689 } else if (!strcmp (mimetype, "audio/x-raw")) {
1692 gst_audio_info_init (&info);
1693 if (!gst_audio_info_from_caps (&info, caps)) {
1694 GST_DEBUG_OBJECT (mux,
1695 "broken caps, rejected by gst_audio_info_from_caps");
1699 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1700 case GST_AUDIO_FORMAT_U8:
1701 case GST_AUDIO_FORMAT_S16BE:
1702 case GST_AUDIO_FORMAT_S16LE:
1703 case GST_AUDIO_FORMAT_S24BE:
1704 case GST_AUDIO_FORMAT_S24LE:
1705 case GST_AUDIO_FORMAT_S32BE:
1706 case GST_AUDIO_FORMAT_S32LE:
1707 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1708 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1711 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1712 gst_matroska_mux_set_codec_id (context,
1713 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1715 gst_matroska_mux_set_codec_id (context,
1716 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1718 case GST_AUDIO_FORMAT_F32LE:
1719 case GST_AUDIO_FORMAT_F64LE:
1720 gst_matroska_mux_set_codec_id (context,
1721 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1725 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1729 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1730 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1731 const GValue *streamheader;
1733 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1735 gst_matroska_mux_free_codec_priv (context);
1737 streamheader = gst_structure_get_value (structure, "streamheader");
1738 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1739 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1740 ("vorbis stream headers missing or malformed"));
1743 } else if (!strcmp (mimetype, "audio/x-flac")) {
1744 const GValue *streamheader;
1746 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1748 gst_matroska_mux_free_codec_priv (context);
1750 streamheader = gst_structure_get_value (structure, "streamheader");
1751 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1752 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1753 ("flac stream headers missing or malformed"));
1756 } else if (!strcmp (mimetype, "audio/x-speex")) {
1757 const GValue *streamheader;
1759 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1760 gst_matroska_mux_free_codec_priv (context);
1762 streamheader = gst_structure_get_value (structure, "streamheader");
1763 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1764 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1765 ("speex stream headers missing or malformed"));
1768 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1769 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1770 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1771 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1772 } else if (!strcmp (mimetype, "audio/x-dts")) {
1773 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1774 } else if (!strcmp (mimetype, "audio/x-tta")) {
1777 /* TTA frame duration */
1778 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1780 gst_structure_get_int (structure, "width", &width);
1781 audiocontext->bitdepth = width;
1782 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1784 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1786 const GValue *mdpr_data;
1788 gst_structure_get_int (structure, "raversion", &raversion);
1789 switch (raversion) {
1791 gst_matroska_mux_set_codec_id (context,
1792 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1795 gst_matroska_mux_set_codec_id (context,
1796 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1799 gst_matroska_mux_set_codec_id (context,
1800 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1806 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1807 if (mdpr_data != NULL) {
1808 guint8 *priv_data = NULL;
1809 guint priv_data_size = 0;
1811 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1813 priv_data_size = gst_buffer_get_size (codec_data_buf);
1814 priv_data = g_malloc0 (priv_data_size);
1816 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1818 gst_matroska_mux_free_codec_priv (context);
1820 context->codec_priv = priv_data;
1821 context->codec_priv_size = priv_data_size;
1824 } else if (!strcmp (mimetype, "audio/x-wma")
1825 || !strcmp (mimetype, "audio/x-alaw")
1826 || !strcmp (mimetype, "audio/x-mulaw")) {
1828 guint codec_priv_size;
1833 if (samplerate == 0 || channels == 0) {
1834 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1838 if (!strcmp (mimetype, "audio/x-wma")) {
1842 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1843 || !gst_structure_get_int (structure, "block_align", &block_align)
1844 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1845 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1850 switch (wmaversion) {
1852 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1855 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1858 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1861 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1865 if (gst_structure_get_int (structure, "depth", &depth))
1866 audiocontext->bitdepth = depth;
1867 } else if (!strcmp (mimetype, "audio/x-alaw")
1868 || !strcmp (mimetype, "audio/x-mulaw")) {
1869 audiocontext->bitdepth = 8;
1870 if (!strcmp (mimetype, "audio/x-alaw"))
1871 format = GST_RIFF_WAVE_FORMAT_ALAW;
1873 format = GST_RIFF_WAVE_FORMAT_MULAW;
1875 block_align = channels;
1876 bitrate = block_align * samplerate;
1878 g_assert (format != 0);
1880 codec_priv_size = WAVEFORMATEX_SIZE;
1882 codec_priv_size += gst_buffer_get_size (buf);
1884 /* serialize waveformatex structure */
1885 codec_priv = g_malloc0 (codec_priv_size);
1886 GST_WRITE_UINT16_LE (codec_priv, format);
1887 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1888 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1889 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1890 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1891 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1893 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1895 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1897 /* process codec private/initialization data, if any */
1899 gst_buffer_extract (buf, 0,
1900 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1903 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1904 gst_matroska_mux_free_codec_priv (context);
1905 context->codec_priv = (gpointer) codec_priv;
1906 context->codec_priv_size = codec_priv_size;
1914 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1915 GST_PAD_NAME (pad), caps);
1920 /* we probably don't have the data at start,
1921 * so have to reserve (a maximum) space to write this at the end.
1922 * bit spacy, but some formats can hold quite some */
1923 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1926 * gst_matroska_mux_subtitle_pad_setcaps:
1927 * @pad: Pad which got the caps.
1930 * Setcaps function for subtitle sink pad.
1932 * Returns: #TRUE on success.
1935 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1937 /* There is now (at least) one such alement (kateenc), and I'm going
1938 to handle it here and claim it works when it can be piped back
1939 through GStreamer and VLC */
1941 GstMatroskaTrackContext *context = NULL;
1942 GstMatroskaTrackSubtitleContext *scontext;
1943 GstMatroskaMux *mux;
1944 GstMatroskaPad *collect_pad;
1945 const gchar *mimetype;
1946 GstStructure *structure;
1947 const GValue *value = NULL;
1948 GstBuffer *buf = NULL;
1950 gboolean ret = TRUE;
1952 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1955 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1956 g_assert (collect_pad);
1957 context = collect_pad->track;
1959 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1960 scontext = (GstMatroskaTrackSubtitleContext *) context;
1962 structure = gst_caps_get_structure (caps, 0);
1963 mimetype = gst_structure_get_name (structure);
1965 /* keep track of default set in request_pad */
1966 id = context->codec_id;
1969 scontext->check_utf8 = 1;
1970 scontext->invalid_utf8 = 0;
1971 context->default_duration = 0;
1973 if (!strcmp (mimetype, "subtitle/x-kate")) {
1974 const GValue *streamheader;
1976 gst_matroska_mux_set_codec_id (context,
1977 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1979 gst_matroska_mux_free_codec_priv (context);
1981 streamheader = gst_structure_get_value (structure, "streamheader");
1982 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1983 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1984 ("kate stream headers missing or malformed"));
1988 } else if (!strcmp (mimetype, "text/plain")) {
1989 gst_matroska_mux_set_codec_id (context,
1990 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
1991 } else if (!strcmp (mimetype, "application/x-ssa")) {
1992 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
1993 } else if (!strcmp (mimetype, "application/x-ass")) {
1994 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
1995 } else if (!strcmp (mimetype, "application/x-usf")) {
1996 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
1997 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
1998 gst_matroska_mux_set_codec_id (context,
1999 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2006 /* maybe some private data, e.g. vobsub */
2007 value = gst_structure_get_value (structure, "codec_data");
2009 buf = gst_value_get_buffer (value);
2011 guint8 *priv_data = NULL, *priv_buffer_data;
2012 gsize priv_data_size = 0;
2015 gst_buffer_map (buf, &priv_data_size, NULL, GST_MAP_READ);
2016 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2017 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2018 " exceeded maximum (%d); discarding", pad,
2019 SUBTITLE_MAX_CODEC_PRIVATE);
2020 gst_buffer_unmap (buf, priv_data, priv_data_size);
2024 gst_matroska_mux_free_codec_priv (context);
2026 priv_data = g_malloc0 (priv_data_size);
2027 memcpy (priv_data, priv_buffer_data, priv_data_size);
2028 context->codec_priv = priv_data;
2029 context->codec_priv_size = priv_data_size;
2030 gst_buffer_unmap (buf, priv_buffer_data, priv_data_size);
2033 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2034 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2037 /* free default if modified */
2046 * gst_matroska_mux_request_new_pad:
2047 * @element: #GstMatroskaMux.
2048 * @templ: #GstPadTemplate.
2049 * @pad_name: New pad name.
2051 * Request pad function for sink templates.
2053 * Returns: New #GstPad.
2056 gst_matroska_mux_request_new_pad (GstElement * element,
2057 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2059 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2060 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2061 GstMatroskaPad *collect_pad;
2062 GstMatroskamuxPad *newpad;
2064 const gchar *pad_name = NULL;
2065 GstMatroskaCapsFunc capsfunc = NULL;
2066 GstMatroskaTrackContext *context = NULL;
2068 gboolean locked = TRUE;
2071 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2072 /* don't mix named and unnamed pads, if the pad already exists we fail when
2073 * trying to add it */
2074 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2075 pad_name = req_name;
2077 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2080 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2081 context = (GstMatroskaTrackContext *)
2082 g_new0 (GstMatroskaTrackAudioContext, 1);
2083 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2084 context->name = g_strdup ("Audio");
2085 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2086 /* don't mix named and unnamed pads, if the pad already exists we fail when
2087 * trying to add it */
2088 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2089 pad_name = req_name;
2091 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2094 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2095 context = (GstMatroskaTrackContext *)
2096 g_new0 (GstMatroskaTrackVideoContext, 1);
2097 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2098 context->name = g_strdup ("Video");
2099 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2100 /* don't mix named and unnamed pads, if the pad already exists we fail when
2101 * trying to add it */
2102 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2103 pad_name = req_name;
2105 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2108 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2109 context = (GstMatroskaTrackContext *)
2110 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2111 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2112 context->name = g_strdup ("Subtitle");
2113 /* setcaps may only provide proper one a lot later */
2114 id = g_strdup ("S_SUB_UNKNOWN");
2117 GST_WARNING_OBJECT (mux, "This is not our template!");
2121 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2122 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2125 gst_matroskamux_pad_init (newpad);
2126 collect_pad = (GstMatroskaPad *)
2127 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2128 sizeof (GstMatroskamuxPad),
2129 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2131 collect_pad->track = context;
2132 gst_matroska_pad_reset (collect_pad, FALSE);
2133 collect_pad->track->codec_id = id;
2135 collect_pad->capsfunc = capsfunc;
2136 gst_pad_set_active (GST_PAD (newpad), TRUE);
2137 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2138 goto pad_add_failed;
2142 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2144 return GST_PAD (newpad);
2149 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2150 gst_object_unref (newpad);
2156 * gst_matroska_mux_release_pad:
2157 * @element: #GstMatroskaMux.
2158 * @pad: Pad to release.
2160 * Release a previously requested pad.
2163 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2165 GstMatroskaMux *mux;
2168 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2170 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2171 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2172 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2174 if (cdata->pad == pad) {
2175 GstClockTime min_dur; /* observed minimum duration */
2177 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2178 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2179 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2180 if (collect_pad->duration < min_dur)
2181 collect_pad->duration = min_dur;
2184 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2185 mux->duration < collect_pad->duration)
2186 mux->duration = collect_pad->duration;
2192 gst_collect_pads2_remove_pad (mux->collect, pad);
2193 if (gst_element_remove_pad (element, pad))
2199 * gst_matroska_mux_track_header:
2200 * @mux: #GstMatroskaMux
2201 * @context: Tack context.
2203 * Write a track header.
2206 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2207 GstMatroskaTrackContext * context)
2209 GstEbmlWrite *ebml = mux->ebml_write;
2212 /* TODO: check if everything necessary is written and check default values */
2214 /* track type goes before the type-specific stuff */
2215 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2216 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2218 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2219 gst_matroska_mux_create_uid ());
2220 if (context->default_duration) {
2221 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2222 context->default_duration);
2224 if (context->language) {
2225 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2229 /* FIXME: until we have a nice way of getting the codecname
2230 * out of the caps, I'm not going to enable this. Too much
2231 * (useless, double, boring) work... */
2232 /* TODO: Use value from tags if any */
2233 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2234 context->codec_name); */
2235 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2237 /* type-specific stuff */
2238 switch (context->type) {
2239 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2240 GstMatroskaTrackVideoContext *videocontext =
2241 (GstMatroskaTrackVideoContext *) context;
2243 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2244 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2245 videocontext->pixel_width);
2246 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2247 videocontext->pixel_height);
2248 if (videocontext->display_width && videocontext->display_height) {
2249 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2250 videocontext->display_width);
2251 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2252 videocontext->display_height);
2254 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2255 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2256 if (videocontext->fourcc) {
2257 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2259 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2260 (gpointer) & fcc_le, 4);
2262 gst_ebml_write_master_finish (ebml, master);
2267 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2268 GstMatroskaTrackAudioContext *audiocontext =
2269 (GstMatroskaTrackAudioContext *) context;
2271 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2272 if (audiocontext->samplerate != 8000)
2273 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2274 audiocontext->samplerate);
2275 if (audiocontext->channels != 1)
2276 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2277 audiocontext->channels);
2278 if (audiocontext->bitdepth) {
2279 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2280 audiocontext->bitdepth);
2282 gst_ebml_write_master_finish (ebml, master);
2287 /* this is what we write for now and must be filled
2288 * and remainder void'ed later on */
2289 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2291 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2294 context->pos = ebml->pos;
2295 /* CodecID is mandatory ... */
2296 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2298 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2299 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2300 SUBTITLE_MAX_CODEC_PRIVATE);
2302 /* real data has to be written at finish */
2306 /* doesn't need type-specific data */
2310 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2311 if (context->codec_priv)
2312 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2313 context->codec_priv, context->codec_priv_size);
2318 * gst_matroska_mux_start:
2319 * @mux: #GstMatroskaMux
2321 * Start a new matroska file (write headers etc...)
2324 gst_matroska_mux_start (GstMatroskaMux * mux)
2326 GstEbmlWrite *ebml = mux->ebml_write;
2327 const gchar *doctype;
2328 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2329 GST_MATROSKA_ID_TRACKS,
2330 GST_MATROSKA_ID_CUES,
2331 GST_MATROSKA_ID_TAGS,
2334 guint64 master, child;
2338 GstClockTime duration = 0;
2339 guint32 segment_uid[4];
2340 GTimeVal time = { 0, 0 };
2342 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2343 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2345 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2347 /* we start with a EBML header */
2348 doctype = mux->doctype;
2349 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2350 doctype, mux->doctype_version);
2351 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2353 /* the rest of the header is cached */
2354 gst_ebml_write_set_cache (ebml, 0x1000);
2356 /* start a segment */
2358 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2359 mux->segment_master = ebml->pos;
2361 if (!mux->streamable) {
2362 /* seekhead (table of contents) - we set the positions later */
2363 mux->seekhead_pos = ebml->pos;
2364 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2365 for (i = 0; seekhead_id[i] != 0; i++) {
2366 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2367 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2368 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2369 gst_ebml_write_master_finish (ebml, child);
2371 gst_ebml_write_master_finish (ebml, master);
2374 if (mux->streamable) {
2375 const GstTagList *tags;
2378 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2380 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2381 guint64 master_tags, master_tag;
2383 GST_DEBUG_OBJECT (mux, "Writing tags");
2385 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2386 mux->tags_pos = ebml->pos;
2387 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2388 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2389 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2390 gst_ebml_write_master_finish (ebml, master_tag);
2391 gst_ebml_write_master_finish (ebml, master_tags);
2396 mux->info_pos = ebml->pos;
2397 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2398 for (i = 0; i < 4; i++) {
2399 segment_uid[i] = g_random_int ();
2401 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2402 (guint8 *) segment_uid, 16);
2403 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2404 mux->duration_pos = ebml->pos;
2406 if (!mux->streamable) {
2407 for (collected = mux->collect->data; collected;
2408 collected = g_slist_next (collected)) {
2409 GstMatroskaPad *collect_pad;
2411 gint64 trackduration;
2413 collect_pad = (GstMatroskaPad *) collected->data;
2414 thepad = collect_pad->collect.pad;
2416 /* Query the total length of the track. */
2417 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2418 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2419 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2420 GST_TIME_ARGS (trackduration));
2421 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2422 duration = (GstClockTime) trackduration;
2426 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2427 gst_guint64_to_gdouble (duration) /
2428 gst_guint64_to_gdouble (mux->time_scale));
2430 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2431 "GStreamer plugin version " PACKAGE_VERSION);
2432 if (mux->writing_app && mux->writing_app[0]) {
2433 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2435 g_get_current_time (&time);
2436 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2437 gst_ebml_write_master_finish (ebml, master);
2440 mux->tracks_pos = ebml->pos;
2441 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2443 for (collected = mux->collect->data; collected;
2444 collected = g_slist_next (collected)) {
2445 GstMatroskaPad *collect_pad;
2448 collect_pad = (GstMatroskaPad *) collected->data;
2449 thepad = collect_pad->collect.pad;
2451 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2452 collect_pad->track->codec_id != 0) {
2453 collect_pad->track->num = tracknum++;
2454 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2455 gst_matroska_mux_track_header (mux, collect_pad->track);
2456 gst_ebml_write_master_finish (ebml, child);
2457 /* some remaining pad/track setup */
2458 collect_pad->default_duration_scaled =
2459 gst_util_uint64_scale (collect_pad->track->default_duration,
2460 1, mux->time_scale);
2463 gst_ebml_write_master_finish (ebml, master);
2465 /* lastly, flush the cache */
2466 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2470 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2473 /* TODO: more sensible tag mappings */
2476 const gchar *matroska_tagname;
2477 const gchar *gstreamer_tagname;
2481 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2482 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2483 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2484 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2485 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2486 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2487 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2488 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2489 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2490 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2491 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2492 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2493 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2494 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2495 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2497 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2499 guint64 simpletag_master;
2501 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2502 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2503 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2505 if (strcmp (tagname_gst, tag) == 0) {
2506 GValue src = { 0, };
2509 if (!gst_tag_list_copy_value (&src, list, tag))
2511 if ((dest = gst_value_serialize (&src))) {
2513 simpletag_master = gst_ebml_write_master_start (ebml,
2514 GST_MATROSKA_ID_SIMPLETAG);
2515 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2516 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2517 gst_ebml_write_master_finish (ebml, simpletag_master);
2520 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2522 g_value_unset (&src);
2530 * gst_matroska_mux_finish:
2531 * @mux: #GstMatroskaMux
2533 * Finish a new matroska file (write index etc...)
2536 gst_matroska_mux_finish (GstMatroskaMux * mux)
2538 GstEbmlWrite *ebml = mux->ebml_write;
2540 guint64 duration = 0;
2542 const GstTagList *tags;
2544 /* finish last cluster */
2546 gst_ebml_write_master_finish (ebml, mux->cluster);
2550 if (mux->index != NULL) {
2552 guint64 master, pointentry_master, trackpos_master;
2554 mux->cues_pos = ebml->pos;
2555 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2556 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2558 for (n = 0; n < mux->num_indexes; n++) {
2559 GstMatroskaIndex *idx = &mux->index[n];
2561 pointentry_master = gst_ebml_write_master_start (ebml,
2562 GST_MATROSKA_ID_POINTENTRY);
2563 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2564 idx->time / mux->time_scale);
2565 trackpos_master = gst_ebml_write_master_start (ebml,
2566 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2567 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2568 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2569 idx->pos - mux->segment_master);
2570 gst_ebml_write_master_finish (ebml, trackpos_master);
2571 gst_ebml_write_master_finish (ebml, pointentry_master);
2574 gst_ebml_write_master_finish (ebml, master);
2575 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2579 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2581 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2582 guint64 master_tags, master_tag;
2584 GST_DEBUG_OBJECT (mux, "Writing tags");
2586 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2587 mux->tags_pos = ebml->pos;
2588 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2589 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2590 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2591 gst_ebml_write_master_finish (ebml, master_tag);
2592 gst_ebml_write_master_finish (ebml, master_tags);
2595 /* update seekhead. We know that:
2596 * - a seekhead contains 4 entries.
2597 * - order of entries is as above.
2598 * - a seekhead has a 4-byte header + 8-byte length
2599 * - each entry is 2-byte master, 2-byte ID pointer,
2600 * 2-byte length pointer, all 8/1-byte length, 4-
2601 * byte ID and 8-byte length pointer, where the
2602 * length pointer starts at 20.
2603 * - all entries are local to the segment (so pos - segment_master).
2604 * - so each entry is at 12 + 20 + num * 28. */
2605 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2606 mux->info_pos - mux->segment_master);
2607 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2608 mux->tracks_pos - mux->segment_master);
2609 if (mux->index != NULL) {
2610 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2611 mux->cues_pos - mux->segment_master);
2614 guint64 my_pos = ebml->pos;
2616 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2617 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2618 gst_ebml_write_seek (ebml, my_pos);
2621 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2622 mux->tags_pos - mux->segment_master);
2625 guint64 my_pos = ebml->pos;
2627 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2628 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2629 gst_ebml_write_seek (ebml, my_pos);
2633 * - first get the overall duration
2634 * (a released track may have left a duration in here)
2635 * - write some track header data for subtitles
2637 duration = mux->duration;
2639 for (collected = mux->collect->data; collected;
2640 collected = g_slist_next (collected)) {
2641 GstMatroskaPad *collect_pad;
2642 GstClockTime min_duration; /* observed minimum duration */
2643 GstMatroskaTrackContext *context;
2644 gint voidleft = 0, fill = 0;
2647 collect_pad = (GstMatroskaPad *) collected->data;
2648 context = collect_pad->track;
2650 GST_DEBUG_OBJECT (mux,
2651 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2652 " end ts %" GST_TIME_FORMAT, collect_pad,
2653 GST_TIME_ARGS (collect_pad->start_ts),
2654 GST_TIME_ARGS (collect_pad->end_ts));
2656 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2657 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2659 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2660 if (collect_pad->duration < min_duration)
2661 collect_pad->duration = min_duration;
2662 GST_DEBUG_OBJECT (collect_pad,
2663 "final track duration: %" GST_TIME_FORMAT,
2664 GST_TIME_ARGS (collect_pad->duration));
2667 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2668 duration < collect_pad->duration)
2669 duration = collect_pad->duration;
2671 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2675 /* write subtitle type and possible private data */
2676 gst_ebml_write_seek (ebml, context->pos);
2677 /* complex way to write ascii to account for extra filling */
2678 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2679 strcpy (codec_id, context->codec_id);
2680 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2681 codec_id, strlen (context->codec_id) + 1 + fill);
2683 if (context->codec_priv)
2684 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2685 context->codec_priv, context->codec_priv_size);
2686 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2687 /* void'ify; sigh, variable sized length field */
2688 if (voidleft == 1) {
2691 } else if (voidleft && voidleft <= 128)
2692 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2693 else if (voidleft >= 130)
2694 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2695 else if (voidleft == 129) {
2696 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2697 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2701 /* seek back (optional, but do anyway) */
2702 gst_ebml_write_seek (ebml, pos);
2704 /* update duration */
2705 if (duration != 0) {
2706 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2707 GST_TIME_ARGS (duration));
2708 pos = mux->ebml_write->pos;
2709 gst_ebml_write_seek (ebml, mux->duration_pos);
2710 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2711 gst_guint64_to_gdouble (duration) /
2712 gst_guint64_to_gdouble (mux->time_scale));
2713 gst_ebml_write_seek (ebml, pos);
2716 guint64 my_pos = ebml->pos;
2718 gst_ebml_write_seek (ebml, mux->duration_pos);
2719 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2720 gst_ebml_write_seek (ebml, my_pos);
2722 GST_DEBUG_OBJECT (mux, "finishing segment");
2723 /* finish segment - this also writes element length */
2724 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2728 * gst_matroska_mux_buffer_header:
2729 * @track: Track context.
2730 * @relative_timestamp: relative timestamp of the buffer
2731 * @flags: Buffer flags.
2733 * Create a buffer containing buffer header.
2735 * Returns: New buffer.
2738 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2739 gint16 relative_timestamp, int flags)
2742 guint8 *data = g_malloc (4);
2744 hdr = gst_buffer_new_wrapped (data, 4);
2745 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2746 data[0] = track->num | 0x80;
2747 /* time relative to clustertime */
2748 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
2756 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2757 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2758 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2761 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2762 GstMatroskaPad * collect_pad, GstBuffer * buf)
2764 GstMatroskaTrackVideoContext *ctx =
2765 (GstMatroskaTrackVideoContext *) collect_pad->track;
2766 guint8 *buf_data, *data;
2769 guint32 next_parse_offset;
2770 GstBuffer *ret = NULL;
2771 gboolean is_muxing_unit = FALSE;
2773 buf_data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
2777 gst_buffer_unmap (buf, buf_data, -1);
2778 gst_buffer_unref (buf);
2782 /* Check if this buffer contains a picture or end-of-sequence packet */
2783 while (size >= 13) {
2784 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2785 gst_buffer_unmap (buf, buf_data, -1);
2786 gst_buffer_unref (buf);
2790 parse_code = GST_READ_UINT8 (data + 4);
2791 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2792 if (ctx->dirac_unit) {
2793 gst_buffer_unref (ctx->dirac_unit);
2794 ctx->dirac_unit = NULL;
2796 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2797 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2798 is_muxing_unit = TRUE;
2802 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2804 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2807 data += next_parse_offset;
2808 size -= next_parse_offset;
2811 if (ctx->dirac_unit)
2812 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2814 ctx->dirac_unit = gst_buffer_ref (buf);
2816 gst_buffer_unmap (buf, buf_data, -1);
2818 if (is_muxing_unit) {
2819 ret = gst_buffer_make_writable (ctx->dirac_unit);
2820 ctx->dirac_unit = NULL;
2821 gst_buffer_copy_into (ret, buf,
2822 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2823 gst_buffer_unref (buf);
2825 gst_buffer_unref (buf);
2833 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2837 GValue streamheader = { 0 };
2838 GValue bufval = { 0 };
2839 GstBuffer *streamheader_buffer;
2840 GstEbmlWrite *ebml = mux->ebml_write;
2842 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2843 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2844 caps = gst_caps_new_empty_simple ("video/webm");
2846 caps = gst_caps_new_empty_simple ("video/x-matroska");
2848 s = gst_caps_get_structure (caps, 0);
2849 g_value_init (&streamheader, GST_TYPE_ARRAY);
2850 g_value_init (&bufval, GST_TYPE_BUFFER);
2851 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2852 gst_value_set_buffer (&bufval, streamheader_buffer);
2853 gst_value_array_append_value (&streamheader, &bufval);
2854 g_value_unset (&bufval);
2855 gst_structure_set_value (s, "streamheader", &streamheader);
2856 g_value_unset (&streamheader);
2857 gst_caps_replace (&ebml->caps, caps);
2858 gst_buffer_unref (streamheader_buffer);
2859 gst_caps_unref (caps);
2863 * gst_matroska_mux_write_data:
2864 * @mux: #GstMatroskaMux
2865 * @collect_pad: #GstMatroskaPad with the data
2867 * Write collected data (called from gst_matroska_mux_collected).
2869 * Returns: Result of the gst_pad_push issued to write the data.
2871 static GstFlowReturn
2872 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2875 GstEbmlWrite *ebml = mux->ebml_write;
2878 gboolean write_duration;
2879 gint16 relative_timestamp;
2880 gint64 relative_timestamp64;
2881 guint64 block_duration;
2882 gboolean is_video_keyframe = FALSE;
2883 GstMatroskamuxPad *pad;
2886 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2888 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2889 if (collect_pad->track->xiph_headers_to_skip > 0) {
2890 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2891 gst_buffer_unref (buf);
2892 --collect_pad->track->xiph_headers_to_skip;
2896 /* for dirac we have to queue up everything up to a picture unit */
2897 if (collect_pad->track->codec_id != NULL &&
2898 strcmp (collect_pad->track->codec_id,
2899 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2900 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2905 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2906 * this would wreak havoc with time stored in matroska file */
2907 /* TODO: maybe calculate a timestamp by using the previous timestamp
2908 * and default duration */
2909 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2910 GST_WARNING_OBJECT (collect_pad->collect.pad,
2911 "Invalid buffer timestamp; dropping buffer");
2912 gst_buffer_unref (buf);
2916 /* set the timestamp for outgoing buffers */
2917 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2919 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2920 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2921 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2922 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2923 is_video_keyframe = TRUE;
2927 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2928 * or when we may be reaching the limit of the relative timestamp */
2929 if (mux->cluster_time +
2930 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2931 || is_video_keyframe || mux->force_key_unit_event) {
2932 if (!mux->streamable)
2933 gst_ebml_write_master_finish (ebml, mux->cluster);
2935 /* Forward the GstForceKeyUnit event after finishing the cluster */
2936 if (mux->force_key_unit_event) {
2937 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2938 mux->force_key_unit_event = NULL;
2941 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2942 mux->cluster_pos = ebml->pos;
2943 gst_ebml_write_set_cache (ebml, 0x20);
2945 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2946 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2947 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2949 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2950 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2952 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2953 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2954 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2955 mux->prev_cluster_size);
2960 mux->cluster_pos = ebml->pos;
2961 gst_ebml_write_set_cache (ebml, 0x20);
2962 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2963 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2964 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2965 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2966 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2969 /* update duration of this track */
2970 if (GST_BUFFER_DURATION_IS_VALID (buf))
2971 collect_pad->duration += GST_BUFFER_DURATION (buf);
2973 /* We currently write index entries for all video tracks or for the audio
2974 * track in a single-track audio file. This could be improved by keeping the
2975 * index only for the *first* video track. */
2977 /* TODO: index is useful for every track, should contain the number of
2978 * the block in the cluster which contains the timestamp, should also work
2979 * for files with multiple audio tracks.
2981 if (!mux->streamable &&
2982 (is_video_keyframe ||
2983 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2984 (mux->num_streams == 1)))) {
2987 if (mux->min_index_interval != 0) {
2988 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2989 if (mux->index[last_idx].track == collect_pad->track->num)
2994 if (last_idx < 0 || mux->min_index_interval == 0 ||
2995 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2996 >= mux->min_index_interval)) {
2997 GstMatroskaIndex *idx;
2999 if (mux->num_indexes % 32 == 0) {
3000 mux->index = g_renew (GstMatroskaIndex, mux->index,
3001 mux->num_indexes + 32);
3003 idx = &mux->index[mux->num_indexes++];
3005 idx->pos = mux->cluster_pos;
3006 idx->time = GST_BUFFER_TIMESTAMP (buf);
3007 idx->track = collect_pad->track->num;
3011 /* Check if the duration differs from the default duration. */
3012 write_duration = FALSE;
3014 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3015 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3016 1, mux->time_scale);
3018 /* small difference should be ok. */
3019 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3020 block_duration < collect_pad->default_duration_scaled - 1) {
3021 write_duration = TRUE;
3025 /* write the block, for doctype v2 use SimpleBlock if possible
3026 * one slice (*breath*).
3027 * FIXME: Need to do correct lacing! */
3028 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3029 if (relative_timestamp64 >= 0) {
3030 /* round the timestamp */
3031 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3033 /* round the timestamp */
3034 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3036 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3038 if (mux->doctype_version > 1 && !write_duration) {
3040 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3043 gst_matroska_mux_create_buffer_header (collect_pad->track,
3044 relative_timestamp, flags);
3045 gst_ebml_write_set_cache (ebml, 0x40);
3046 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3047 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3048 gst_ebml_write_buffer (ebml, hdr);
3049 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3050 gst_ebml_write_buffer (ebml, buf);
3052 return gst_ebml_last_write_result (ebml);
3054 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3055 /* write and call order slightly unnatural,
3056 * but avoids seek and minizes pushing */
3057 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3059 gst_matroska_mux_create_buffer_header (collect_pad->track,
3060 relative_timestamp, 0);
3062 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3063 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3064 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3065 gst_ebml_write_buffer (ebml, hdr);
3066 gst_ebml_write_master_finish_full (ebml, blockgroup,
3067 gst_buffer_get_size (buf));
3068 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3069 gst_ebml_write_buffer (ebml, buf);
3071 return gst_ebml_last_write_result (ebml);
3076 * gst_matroska_mux_handle_buffer:
3077 * @pads: #GstCollectPads2
3078 * @uuser_data: #GstMatroskaMux
3080 * Collectpads callback.
3082 * Returns: #GstFlowReturn
3084 static GstFlowReturn
3085 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3086 GstBuffer * buf, gpointer user_data)
3088 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3089 GstEbmlWrite *ebml = mux->ebml_write;
3090 GstMatroskaPad *best;
3091 GstFlowReturn ret = GST_FLOW_OK;
3093 GST_DEBUG_OBJECT (mux, "Collected pads");
3095 /* start with a header */
3096 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3097 if (mux->collect->data == NULL) {
3098 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3099 ("No input streams configured"));
3100 return GST_FLOW_ERROR;
3102 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3103 gst_ebml_start_streamheader (ebml);
3104 gst_matroska_mux_start (mux);
3105 gst_matroska_mux_stop_streamheader (mux);
3106 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3109 /* provided with stream to write from */
3110 best = (GstMatroskaPad *) data;
3112 /* if there is no best pad, we have reached EOS */
3114 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3115 if (!mux->streamable) {
3116 gst_matroska_mux_finish (mux);
3118 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3120 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3125 /* if we have a best stream, should also have a buffer */
3128 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3129 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3130 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3131 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3133 /* make note of first and last encountered timestamps, so we can calculate
3134 * the actual duration later when we send an updated header on eos */
3135 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3136 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3137 GstClockTime end_ts = start_ts;
3139 if (GST_BUFFER_DURATION_IS_VALID (buf))
3140 end_ts += GST_BUFFER_DURATION (buf);
3141 else if (best->track->default_duration)
3142 end_ts += best->track->default_duration;
3144 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3145 best->end_ts = end_ts;
3147 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3148 start_ts < best->start_ts))
3149 best->start_ts = start_ts;
3152 /* write one buffer */
3153 ret = gst_matroska_mux_write_data (mux, best, buf);
3161 * gst_matroska_mux_change_state:
3162 * @element: #GstMatroskaMux
3163 * @transition: State change transition.
3165 * Change the muxer state.
3167 * Returns: #GstStateChangeReturn
3169 static GstStateChangeReturn
3170 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3172 GstStateChangeReturn ret;
3173 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3175 switch (transition) {
3176 case GST_STATE_CHANGE_NULL_TO_READY:
3178 case GST_STATE_CHANGE_READY_TO_PAUSED:
3179 gst_collect_pads2_start (mux->collect);
3181 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3183 case GST_STATE_CHANGE_PAUSED_TO_READY:
3184 gst_collect_pads2_stop (mux->collect);
3190 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3192 switch (transition) {
3193 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3195 case GST_STATE_CHANGE_PAUSED_TO_READY:
3196 gst_matroska_mux_reset (GST_ELEMENT (mux));
3198 case GST_STATE_CHANGE_READY_TO_NULL:
3208 gst_matroska_mux_set_property (GObject * object,
3209 guint prop_id, const GValue * value, GParamSpec * pspec)
3211 GstMatroskaMux *mux;
3213 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3214 mux = GST_MATROSKA_MUX (object);
3217 case ARG_WRITING_APP:
3218 if (!g_value_get_string (value)) {
3219 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3222 g_free (mux->writing_app);
3223 mux->writing_app = g_value_dup_string (value);
3225 case ARG_DOCTYPE_VERSION:
3226 mux->doctype_version = g_value_get_int (value);
3228 case ARG_MIN_INDEX_INTERVAL:
3229 mux->min_index_interval = g_value_get_int64 (value);
3231 case ARG_STREAMABLE:
3232 mux->streamable = g_value_get_boolean (value);
3235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3241 gst_matroska_mux_get_property (GObject * object,
3242 guint prop_id, GValue * value, GParamSpec * pspec)
3244 GstMatroskaMux *mux;
3246 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3247 mux = GST_MATROSKA_MUX (object);
3250 case ARG_WRITING_APP:
3251 g_value_set_string (value, mux->writing_app);
3253 case ARG_DOCTYPE_VERSION:
3254 g_value_set_int (value, mux->doctype_version);
3256 case ARG_MIN_INDEX_INTERVAL:
3257 g_value_set_int64 (value, mux->min_index_interval);
3259 case ARG_STREAMABLE:
3260 g_value_set_boolean (value, mux->streamable);
3263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);