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., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, 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-1.0 -v filesrc location=/path/to/mp3 ! mpegaudioparse ! 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-1.0 -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>
56 #include <gst/pbutils/codec-utils.h>
58 #include "matroska-mux.h"
59 #include "matroska-ids.h"
61 #define GST_MATROSKA_MUX_CHAPLANG "und"
63 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
64 #define GST_CAT_DEFAULT matroskamux_debug
71 PROP_MIN_INDEX_INTERVAL,
76 #define DEFAULT_DOCTYPE_VERSION 2
77 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
78 #define DEFAULT_MIN_INDEX_INTERVAL 0
79 #define DEFAULT_STREAMABLE FALSE
80 #define DEFAULT_TIMECODESCALE GST_MSECOND
82 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
83 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
85 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
88 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
91 #define COMMON_VIDEO_CAPS \
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 "; "
109 "video/x-h265, stream-format=hvc1, alignment=au, "
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 "; "
125 COMMON_VIDEO_CAPS "; "
126 "video/x-pn-realvideo, "
127 "rmversion = (int) [1, 4], "
128 COMMON_VIDEO_CAPS "; "
130 COMMON_VIDEO_CAPS "; "
132 COMMON_VIDEO_CAPS "; "
134 "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }, "
135 COMMON_VIDEO_CAPS "; "
137 COMMON_VIDEO_CAPS "; "
138 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
141 #define COMMON_AUDIO_CAPS \
142 "channels = (int) [ 1, MAX ], " \
143 "rate = (int) [ 1, MAX ]"
146 * * require codec data, etc as needed
148 static GstStaticPadTemplate audiosink_templ =
149 GST_STATIC_PAD_TEMPLATE ("audio_%u",
152 GST_STATIC_CAPS ("audio/mpeg, "
153 "mpegversion = (int) 1, "
154 "layer = (int) [ 1, 3 ], "
155 COMMON_AUDIO_CAPS "; "
157 "mpegversion = (int) { 2, 4 }, "
158 "stream-format = (string) raw, "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
167 COMMON_AUDIO_CAPS "; "
169 COMMON_AUDIO_CAPS "; "
172 COMMON_AUDIO_CAPS "; "
174 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
175 "layout = (string) interleaved, "
176 COMMON_AUDIO_CAPS ";"
178 "width = (int) { 8, 16, 24 }, "
179 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
180 "audio/x-pn-realaudio, "
181 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
182 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
183 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
184 COMMON_AUDIO_CAPS ";"
186 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
188 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
190 "layout = (string)dvi, "
191 "block_align = (int)[64, 8192], "
192 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
194 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
197 static GstStaticPadTemplate subtitlesink_templ =
198 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
201 GST_STATIC_CAPS ("subtitle/x-kate; "
202 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
203 "application/x-usf; subpicture/x-dvd; "
204 "application/x-subtitle-unknown")
207 static gpointer parent_class; /* NULL */
209 /* Matroska muxer destructor */
210 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
211 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
212 static void gst_matroska_mux_finalize (GObject * object);
214 /* Pads collected callback */
215 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
216 GstCollectData * data, GstBuffer * buf, gpointer user_data);
217 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
218 GstCollectData * data, GstEvent * event, gpointer user_data);
221 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
222 GstObject * parent, GstEvent * event);
223 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
224 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
225 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
227 /* gst internal change state handler */
228 static GstStateChangeReturn
229 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
231 /* gobject bla bla */
232 static void gst_matroska_mux_set_property (GObject * object,
233 guint prop_id, const GValue * value, GParamSpec * pspec);
234 static void gst_matroska_mux_get_property (GObject * object,
235 guint prop_id, GValue * value, GParamSpec * pspec);
238 static void gst_matroska_mux_reset (GstElement * element);
241 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
243 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
251 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
254 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
256 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
257 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
258 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
260 /* Cannot use boilerplate macros here because we need the full init function
261 * signature with the additional class argument, so we use the right template
262 * for the sink caps */
264 gst_matroska_mux_get_type (void)
266 static GType object_type; /* 0 */
268 if (object_type == 0) {
269 static const GTypeInfo object_info = {
270 sizeof (GstMatroskaMuxClass),
271 NULL, /* base_init */
272 NULL, /* base_finalize */
273 (GClassInitFunc) gst_matroska_mux_class_init,
274 NULL, /* class_finalize */
275 NULL, /* class_data */
276 sizeof (GstMatroskaMux),
278 (GInstanceInitFunc) gst_matroska_mux_init
280 const GInterfaceInfo iface_info = { NULL };
282 object_type = g_type_register_static (GST_TYPE_ELEMENT,
283 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
285 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
286 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
293 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
295 GObjectClass *gobject_class;
296 GstElementClass *gstelement_class;
298 gobject_class = (GObjectClass *) klass;
299 gstelement_class = (GstElementClass *) klass;
301 gst_element_class_add_static_pad_template (gstelement_class,
303 gst_element_class_add_static_pad_template (gstelement_class,
305 gst_element_class_add_static_pad_template (gstelement_class,
306 &subtitlesink_templ);
307 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
308 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
310 "Muxes video/audio/subtitle streams into a matroska stream",
311 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
313 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
316 gobject_class->finalize = gst_matroska_mux_finalize;
318 gobject_class->get_property = gst_matroska_mux_get_property;
319 gobject_class->set_property = gst_matroska_mux_set_property;
321 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
322 g_param_spec_string ("writing-app", "Writing application.",
323 "The name the application that creates the matroska file.",
324 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
325 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
326 g_param_spec_int ("version", "DocType version",
327 "This parameter determines what Matroska features can be used.",
328 1, 2, DEFAULT_DOCTYPE_VERSION,
329 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
330 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
331 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
332 "entries", "An index entry is created every so many nanoseconds.",
333 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
334 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
335 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
336 g_param_spec_boolean ("streamable", "Determines whether output should "
337 "be streamable", "If set to true, the output should be as if it is "
338 "to be streamed and hence no indexes written or duration written.",
339 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
340 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
341 g_param_spec_int64 ("timecodescale", "Timecode Scale",
342 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
343 GST_SECOND, DEFAULT_TIMECODESCALE,
344 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
346 gstelement_class->change_state =
347 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
348 gstelement_class->request_new_pad =
349 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
350 gstelement_class->release_pad =
351 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
353 parent_class = g_type_class_peek_parent (klass);
357 * Start of pad option handler code
359 #define DEFAULT_PAD_FRAME_DURATION TRUE
360 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
365 PROP_PAD_FRAME_DURATION
371 gboolean frame_duration;
372 gboolean frame_duration_user;
375 typedef GstPadClass GstMatroskamuxPadClass;
377 GType gst_matroskamux_pad_get_type (void);
378 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
380 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
381 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
382 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
383 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
386 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
387 GValue * value, GParamSpec * pspec)
389 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
392 case PROP_PAD_FRAME_DURATION:
393 g_value_set_boolean (value, pad->frame_duration);
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
403 const GValue * value, GParamSpec * pspec)
405 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
408 case PROP_PAD_FRAME_DURATION:
409 pad->frame_duration = g_value_get_boolean (value);
410 pad->frame_duration_user = TRUE;
413 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
421 GObjectClass *gobject_class = (GObjectClass *) klass;
423 gobject_class->set_property = gst_matroskamux_pad_set_property;
424 gobject_class->get_property = gst_matroskamux_pad_get_property;
426 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
427 g_param_spec_boolean ("frame-duration", "Frame duration",
428 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
429 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
433 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
435 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
436 pad->frame_duration_user = FALSE;
440 * End of pad option handler code
444 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
446 GstPadTemplate *templ;
449 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
450 mux->srcpad = gst_pad_new_from_template (templ, "src");
452 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
453 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
454 gst_pad_use_fixed_caps (mux->srcpad);
456 mux->collect = gst_collect_pads_new ();
457 gst_collect_pads_set_clip_function (mux->collect,
458 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
459 gst_collect_pads_set_buffer_function (mux->collect,
460 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
461 gst_collect_pads_set_event_function (mux->collect,
462 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
464 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
465 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
467 /* property defaults */
468 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
469 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
470 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
471 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
472 mux->time_scale = DEFAULT_TIMECODESCALE;
474 /* initialize internal variables */
476 mux->num_streams = 0;
477 mux->num_a_streams = 0;
478 mux->num_t_streams = 0;
479 mux->num_v_streams = 0;
481 /* create used uid list */
482 mux->used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
484 /* initialize remaining variables */
485 gst_matroska_mux_reset (GST_ELEMENT (mux));
490 * gst_matroska_mux_finalize:
491 * @object: #GstMatroskaMux that should be finalized.
493 * Finalize matroska muxer.
496 gst_matroska_mux_finalize (GObject * object)
498 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
500 gst_event_replace (&mux->force_key_unit_event, NULL);
502 gst_object_unref (mux->collect);
503 gst_object_unref (mux->ebml_write);
504 g_free (mux->writing_app);
506 g_array_free (mux->used_uids, TRUE);
508 G_OBJECT_CLASS (parent_class)->finalize (object);
513 * gst_matroska_mux_create_uid:
514 * @mux: #GstMatroskaMux to generate UID for.
516 * Generate new unused track UID.
518 * Returns: New track UID.
521 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
528 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
529 for (i = 0; i < mux->used_uids->len; i++) {
530 if (g_array_index (mux->used_uids, guint64, i) == uid) {
535 g_array_append_val (mux->used_uids, uid);
543 * gst_matroska_pad_reset:
544 * @collect_pad: the #GstMatroskaPad
546 * Reset and/or release resources of a matroska collect pad.
549 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
552 GstMatroskaTrackType type = 0;
554 /* free track information */
555 if (collect_pad->track != NULL) {
556 /* retrieve for optional later use */
557 name = collect_pad->track->name;
558 type = collect_pad->track->type;
559 /* extra for video */
560 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
561 GstMatroskaTrackVideoContext *ctx =
562 (GstMatroskaTrackVideoContext *) collect_pad->track;
564 if (ctx->dirac_unit) {
565 gst_buffer_unref (ctx->dirac_unit);
566 ctx->dirac_unit = NULL;
569 g_free (collect_pad->track->codec_id);
570 g_free (collect_pad->track->codec_name);
572 g_free (collect_pad->track->name);
573 g_free (collect_pad->track->language);
574 g_free (collect_pad->track->codec_priv);
575 g_free (collect_pad->track);
576 collect_pad->track = NULL;
577 if (collect_pad->tags) {
578 gst_tag_list_unref (collect_pad->tags);
579 collect_pad->tags = NULL;
583 if (!full && type != 0) {
584 GstMatroskaTrackContext *context;
586 /* create a fresh context */
588 case GST_MATROSKA_TRACK_TYPE_VIDEO:
589 context = (GstMatroskaTrackContext *)
590 g_new0 (GstMatroskaTrackVideoContext, 1);
592 case GST_MATROSKA_TRACK_TYPE_AUDIO:
593 context = (GstMatroskaTrackContext *)
594 g_new0 (GstMatroskaTrackAudioContext, 1);
596 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
597 context = (GstMatroskaTrackContext *)
598 g_new0 (GstMatroskaTrackSubtitleContext, 1);
601 g_assert_not_reached ();
605 context->type = type;
606 context->name = name;
607 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
608 /* TODO: check default values for the context */
609 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
610 collect_pad->track = context;
611 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
612 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
613 collect_pad->tags = gst_tag_list_new_empty ();
614 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
619 * gst_matroska_pad_free:
620 * @collect_pad: the #GstMatroskaPad
622 * Release resources of a matroska collect pad.
625 gst_matroska_pad_free (GstPad * collect_pad)
627 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
632 * gst_matroska_mux_reset:
633 * @element: #GstMatroskaMux that should be reseted.
635 * Reset matroska muxer back to initial state.
638 gst_matroska_mux_reset (GstElement * element)
640 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
643 /* reset EBML write */
644 gst_ebml_write_reset (mux->ebml_write);
647 mux->state = GST_MATROSKA_MUX_STATE_START;
649 /* clean up existing streams */
651 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
652 GstMatroskaPad *collect_pad;
654 collect_pad = (GstMatroskaPad *) walk->data;
656 /* reset collect pad to pristine state */
657 gst_matroska_pad_reset (collect_pad, FALSE);
661 mux->num_indexes = 0;
666 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
671 mux->cluster_time = 0;
672 mux->cluster_pos = 0;
673 mux->prev_cluster_size = 0;
676 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
681 gst_toc_setter_reset (GST_TOC_SETTER (mux));
683 mux->chapters_pos = 0;
685 /* clear used uids */
686 if (mux->used_uids->len > 0) {
687 g_array_remove_range (mux->used_uids, 0, mux->used_uids->len);
692 * gst_matroska_mux_handle_src_event:
693 * @pad: Pad which received the event.
694 * @event: Received event.
696 * handle events - copied from oggmux without understanding
698 * Returns: #TRUE on success.
701 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
706 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
710 /* disable seeking for now */
716 return gst_pad_event_default (pad, parent, event);
721 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
723 if (context->codec_priv != NULL) {
724 g_free (context->codec_priv);
725 context->codec_priv = NULL;
726 context->codec_priv_size = 0;
731 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
741 /* produce comma-separated list in hex format */
742 for (i = 0; i < 16; ++i) {
744 /* replicate vobsub's slightly off RGB conversion calculation */
745 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
746 u = ((col >> 8) & 0xff) - 128;
747 v = (col & 0xff) - 128;
748 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
749 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
750 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
751 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
754 sclut = g_strjoinv (",", clutv);
756 /* build codec private; only palette for now */
757 gst_matroska_mux_free_codec_priv (context);
758 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
759 /* include terminating 0 */
760 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
762 for (i = 0; i < 16; ++i) {
769 * gst_matroska_mux_handle_sink_event:
770 * @pad: Pad which received the event.
771 * @event: Received event.
773 * handle events - informational ones like tags
775 * Returns: #TRUE on success.
778 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
779 GstCollectData * data, GstEvent * event, gpointer user_data)
781 GstMatroskaPad *collect_pad;
782 GstMatroskaTrackContext *context;
788 mux = GST_MATROSKA_MUX (user_data);
789 collect_pad = (GstMatroskaPad *) data;
791 context = collect_pad->track;
794 switch (GST_EVENT_TYPE (event)) {
795 case GST_EVENT_CAPS:{
798 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
799 gst_event_parse_caps (event, &caps);
801 ret = collect_pad->capsfunc (pad, caps);
802 gst_event_unref (event);
809 GST_DEBUG_OBJECT (mux, "received tag event");
810 gst_event_parse_tag (event, &list);
812 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
813 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
814 const gchar *lang_code;
816 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
818 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
819 g_free (context->language);
820 context->language = g_strdup (lang_code);
822 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
827 /* FIXME: what about stream-specific tags? */
828 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
829 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
830 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
832 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
835 gst_event_unref (event);
836 /* handled this, don't want collectpads to forward it downstream */
842 GstToc *toc, *old_toc;
844 if (mux->chapters_pos > 0)
847 GST_DEBUG_OBJECT (mux, "received toc event");
848 gst_event_parse_toc (event, &toc, NULL);
851 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
852 if (old_toc != NULL) {
854 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
855 gst_toc_unref (old_toc);
858 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
862 gst_event_unref (event);
863 /* handled this, don't want collectpads to forward it downstream */
867 case GST_EVENT_CUSTOM_DOWNSTREAM:
868 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
869 const GstStructure *structure;
871 structure = gst_event_get_structure (event);
872 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
873 gst_event_replace (&mux->force_key_unit_event, NULL);
874 mux->force_key_unit_event = event;
876 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
877 !strcmp ("dvd-spu-clut-change",
878 gst_structure_get_string (structure, "event"))) {
883 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
884 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
885 GST_DEBUG_OBJECT (pad, "... discarding");
888 /* first transform event data into table form */
889 for (i = 0; i < 16; i++) {
890 g_snprintf (name, sizeof (name), "clut%02d", i);
891 if (!gst_structure_get_int (structure, name, &value)) {
892 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
893 "contain %s field", name);
899 /* transform into private data for stream; text form */
900 gst_matroska_mux_build_vobsub_private (context, clut);
910 return gst_collect_pads_event_default (pads, data, event, FALSE);
916 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
919 g_assert (context && id);
920 g_free (context->codec_id);
921 context->codec_id = g_strdup (id);
925 * gst_matroska_mux_video_pad_setcaps:
926 * @pad: Pad which got the caps.
929 * Setcaps function for video sink pad.
931 * Returns: #TRUE on success.
934 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
936 GstMatroskaTrackContext *context = NULL;
937 GstMatroskaTrackVideoContext *videocontext;
939 GstMatroskaPad *collect_pad;
940 GstStructure *structure;
941 const gchar *mimetype;
942 const gchar *interlace_mode, *s;
943 const GValue *value = NULL;
944 GstBuffer *codec_buf = NULL;
945 gint width, height, pixel_width, pixel_height;
948 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
951 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
952 g_assert (collect_pad);
953 context = collect_pad->track;
955 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
956 videocontext = (GstMatroskaTrackVideoContext *) context;
958 /* gst -> matroska ID'ing */
959 structure = gst_caps_get_structure (caps, 0);
961 mimetype = gst_structure_get_name (structure);
963 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
964 if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
965 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
967 if (!strcmp (mimetype, "video/x-theora")) {
968 /* we'll extract the details later from the theora identification header */
972 /* get general properties */
973 /* spec says it is mandatory */
974 if (!gst_structure_get_int (structure, "width", &width) ||
975 !gst_structure_get_int (structure, "height", &height))
978 videocontext->pixel_width = width;
979 videocontext->pixel_height = height;
981 /* set vp8 defaults or let user override it */
982 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
983 && (!strcmp (mimetype, "video/x-vp8")
984 || !strcmp (mimetype, "video/x-vp9")))
985 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
986 DEFAULT_PAD_FRAME_DURATION_VP8;
988 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
989 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
991 context->default_duration =
992 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
993 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
994 GST_TIME_ARGS (context->default_duration));
996 context->default_duration = 0;
998 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
999 &pixel_width, &pixel_height)) {
1000 if (pixel_width > pixel_height) {
1001 videocontext->display_width = width * pixel_width / pixel_height;
1002 videocontext->display_height = height;
1003 } else if (pixel_width < pixel_height) {
1004 videocontext->display_width = width;
1005 videocontext->display_height = height * pixel_height / pixel_width;
1007 videocontext->display_width = 0;
1008 videocontext->display_height = 0;
1011 videocontext->display_width = 0;
1012 videocontext->display_height = 0;
1015 /* Collect stereoscopic info, if any */
1016 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1017 videocontext->multiview_mode =
1018 gst_video_multiview_mode_from_caps_string (s);
1019 gst_structure_get_flagset (structure, "multiview-flags",
1020 &videocontext->multiview_flags, NULL);
1025 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1026 videocontext->fourcc = 0;
1028 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1029 * data and other settings
1033 /* extract codec_data, may turn out needed */
1034 value = gst_structure_get_value (structure, "codec_data");
1036 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1039 if (!strcmp (mimetype, "video/x-raw")) {
1041 gst_matroska_mux_set_codec_id (context,
1042 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1043 fstr = gst_structure_get_string (structure, "format");
1045 if (strlen (fstr) == 4)
1046 videocontext->fourcc = GST_STR_FOURCC (fstr);
1047 else if (!strcmp (fstr, "GRAY8"))
1048 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1049 else if (!strcmp (fstr, "BGR"))
1050 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1051 else if (!strcmp (fstr, "RGB"))
1052 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1054 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1055 ||!strcmp (mimetype, "video/x-divx")
1056 || !strcmp (mimetype, "video/x-dv")
1057 || !strcmp (mimetype, "video/x-h263")
1058 || !strcmp (mimetype, "video/x-msmpeg")
1059 || !strcmp (mimetype, "video/x-wmv")
1060 || !strcmp (mimetype, "image/jpeg")) {
1061 gst_riff_strf_vids *bih;
1062 gint size = sizeof (gst_riff_strf_vids);
1065 if (!strcmp (mimetype, "video/x-huffyuv"))
1066 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1067 else if (!strcmp (mimetype, "video/x-dv"))
1068 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1069 else if (!strcmp (mimetype, "video/x-h263"))
1070 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1071 else if (!strcmp (mimetype, "video/x-divx")) {
1074 gst_structure_get_int (structure, "divxversion", &divxversion);
1075 switch (divxversion) {
1077 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1080 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1083 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1086 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1089 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1090 switch (msmpegversion) {
1092 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1095 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1101 } else if (!strcmp (mimetype, "video/x-wmv")) {
1105 fstr = gst_structure_get_string (structure, "format");
1106 if (fstr && strlen (fstr) == 4) {
1107 fourcc = GST_STR_FOURCC (fstr);
1108 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1109 if (wmvversion == 2) {
1110 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1111 } else if (wmvversion == 1) {
1112 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1113 } else if (wmvversion == 3) {
1114 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1117 } else if (!strcmp (mimetype, "image/jpeg")) {
1118 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1124 bih = g_new0 (gst_riff_strf_vids, 1);
1125 GST_WRITE_UINT32_LE (&bih->size, size);
1126 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1127 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1128 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1129 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1130 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1131 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1132 videocontext->pixel_height * 3);
1134 /* process codec private/initialization data, if any */
1136 size += gst_buffer_get_size (codec_buf);
1137 bih = g_realloc (bih, size);
1138 GST_WRITE_UINT32_LE (&bih->size, size);
1139 gst_buffer_extract (codec_buf, 0,
1140 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1143 gst_matroska_mux_set_codec_id (context,
1144 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1145 gst_matroska_mux_free_codec_priv (context);
1146 context->codec_priv = (gpointer) bih;
1147 context->codec_priv_size = size;
1148 context->dts_only = TRUE;
1149 } else if (!strcmp (mimetype, "video/x-h264")) {
1150 gst_matroska_mux_set_codec_id (context,
1151 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1152 gst_matroska_mux_free_codec_priv (context);
1153 /* Create avcC header */
1154 if (codec_buf != NULL) {
1155 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1156 context->codec_priv = g_malloc0 (context->codec_priv_size);
1157 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1159 } else if (!strcmp (mimetype, "video/x-h265")) {
1160 gst_matroska_mux_set_codec_id (context,
1161 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1162 gst_matroska_mux_free_codec_priv (context);
1163 /* Create hvcC header */
1164 if (codec_buf != NULL) {
1165 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1166 context->codec_priv = g_malloc0 (context->codec_priv_size);
1167 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1169 } else if (!strcmp (mimetype, "video/x-theora")) {
1170 const GValue *streamheader;
1172 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1174 gst_matroska_mux_free_codec_priv (context);
1176 streamheader = gst_structure_get_value (structure, "streamheader");
1177 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1178 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1179 ("theora stream headers missing or malformed"));
1182 } else if (!strcmp (mimetype, "video/x-dirac")) {
1183 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1184 } else if (!strcmp (mimetype, "video/x-vp8")) {
1185 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1186 } else if (!strcmp (mimetype, "video/x-vp9")) {
1187 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1188 } else if (!strcmp (mimetype, "video/mpeg")) {
1191 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1192 switch (mpegversion) {
1194 gst_matroska_mux_set_codec_id (context,
1195 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1198 gst_matroska_mux_set_codec_id (context,
1199 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1202 gst_matroska_mux_set_codec_id (context,
1203 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1209 /* global headers may be in codec data */
1210 if (codec_buf != NULL) {
1211 gst_matroska_mux_free_codec_priv (context);
1212 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1213 context->codec_priv = g_malloc0 (context->codec_priv_size);
1214 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1216 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1218 /* can only make it here if preceding case verified it was version 3 */
1219 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1220 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1222 const GValue *mdpr_data;
1224 gst_structure_get_int (structure, "rmversion", &rmversion);
1225 switch (rmversion) {
1227 gst_matroska_mux_set_codec_id (context,
1228 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1231 gst_matroska_mux_set_codec_id (context,
1232 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1235 gst_matroska_mux_set_codec_id (context,
1236 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1239 gst_matroska_mux_set_codec_id (context,
1240 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1246 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1247 if (mdpr_data != NULL) {
1248 guint8 *priv_data = NULL;
1249 guint priv_data_size = 0;
1251 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1253 priv_data_size = gst_buffer_get_size (codec_data_buf);
1254 priv_data = g_malloc0 (priv_data_size);
1256 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1258 gst_matroska_mux_free_codec_priv (context);
1259 context->codec_priv = priv_data;
1260 context->codec_priv_size = priv_data_size;
1262 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1263 const gchar *variant;
1265 gst_matroska_mux_free_codec_priv (context);
1267 variant = gst_structure_get_string (structure, "format");
1268 if (!variant || !g_strcmp0 (variant, "standard"))
1269 context->codec_priv = g_strdup ("apcn");
1270 else if (!g_strcmp0 (variant, "hq"))
1271 context->codec_priv = g_strdup ("apch");
1272 else if (!g_strcmp0 (variant, "lt"))
1273 context->codec_priv = g_strdup ("apcs");
1274 else if (!g_strcmp0 (variant, "proxy"))
1275 context->codec_priv = g_strdup ("apco");
1276 else if (!g_strcmp0 (variant, "4444"))
1277 context->codec_priv = g_strdup ("ap4h");
1279 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1284 context->codec_priv_size = sizeof (guint32);
1285 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1293 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1294 GST_PAD_NAME (pad), caps);
1299 /* N > 0 to expect a particular number of headers, negative if the
1300 number of headers is variable */
1302 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1303 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1305 GstBuffer **buf = NULL;
1308 guint bufi, i, offset, priv_data_size;
1310 if (streamheader == NULL)
1311 goto no_stream_headers;
1313 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1316 bufarr = g_value_peek_pointer (streamheader);
1317 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1319 if (N > 0 && bufarr->len != N)
1322 context->xiph_headers_to_skip = bufarr->len;
1324 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1325 for (i = 0; i < bufarr->len; i++) {
1326 GValue *bufval = &g_array_index (bufarr, GValue, i);
1328 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1330 goto wrong_content_type;
1333 buf[i] = g_value_peek_pointer (bufval);
1337 if (bufarr->len > 0) {
1338 for (i = 0; i < bufarr->len - 1; i++) {
1339 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1343 for (i = 0; i < bufarr->len; ++i) {
1344 priv_data_size += gst_buffer_get_size (buf[i]);
1347 priv_data = g_malloc0 (priv_data_size);
1349 priv_data[0] = bufarr->len - 1;
1352 if (bufarr->len > 0) {
1353 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1354 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1355 priv_data[offset++] = 0xff;
1357 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1361 for (i = 0; i < bufarr->len; ++i) {
1362 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1363 offset += gst_buffer_get_size (buf[i]);
1366 gst_matroska_mux_free_codec_priv (context);
1367 context->codec_priv = priv_data;
1368 context->codec_priv_size = priv_data_size;
1371 *p_buf0 = gst_buffer_ref (buf[0]);
1380 GST_WARNING ("required streamheaders missing in sink caps!");
1385 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1386 G_VALUE_TYPE_NAME (streamheader));
1391 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1396 GST_WARNING ("streamheaders array does not contain GstBuffers");
1402 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1403 GstMatroskaTrackContext * context)
1405 GstBuffer *buf0 = NULL;
1407 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1410 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1411 GST_WARNING ("First vorbis header too small, ignoring");
1413 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1414 GstMatroskaTrackAudioContext *audiocontext;
1418 gst_buffer_map (buf0, &map, GST_MAP_READ);
1419 hdr = map.data + 1 + 6 + 4;
1420 audiocontext = (GstMatroskaTrackAudioContext *) context;
1421 audiocontext->channels = GST_READ_UINT8 (hdr);
1422 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1423 gst_buffer_unmap (buf0, &map);
1428 gst_buffer_unref (buf0);
1434 theora_streamheader_to_codecdata (const GValue * streamheader,
1435 GstMatroskaTrackContext * context)
1437 GstBuffer *buf0 = NULL;
1439 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1442 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1443 GST_WARNING ("First theora header too small, ignoring");
1444 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1445 GST_WARNING ("First header not a theora identification header, ignoring");
1447 GstMatroskaTrackVideoContext *videocontext;
1448 guint fps_num, fps_denom, par_num, par_denom;
1452 gst_buffer_map (buf0, &map, GST_MAP_READ);
1453 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1455 videocontext = (GstMatroskaTrackVideoContext *) context;
1456 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1457 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1458 hdr += 3 + 3 + 1 + 1;
1459 fps_num = GST_READ_UINT32_BE (hdr);
1460 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1461 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1462 fps_denom, fps_num);
1464 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1465 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1466 if (par_num > 0 && par_denom > 0) {
1467 if (par_num > par_denom) {
1468 videocontext->display_width =
1469 videocontext->pixel_width * par_num / par_denom;
1470 videocontext->display_height = videocontext->pixel_height;
1471 } else if (par_num < par_denom) {
1472 videocontext->display_width = videocontext->pixel_width;
1473 videocontext->display_height =
1474 videocontext->pixel_height * par_denom / par_num;
1476 videocontext->display_width = 0;
1477 videocontext->display_height = 0;
1480 videocontext->display_width = 0;
1481 videocontext->display_height = 0;
1484 gst_buffer_unmap (buf0, &map);
1488 gst_buffer_unref (buf0);
1494 kate_streamheader_to_codecdata (const GValue * streamheader,
1495 GstMatroskaTrackContext * context)
1497 GstBuffer *buf0 = NULL;
1499 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1502 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1503 GST_WARNING ("First kate header too small, ignoring");
1504 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1505 GST_WARNING ("First header not a kate identification header, ignoring");
1509 gst_buffer_unref (buf0);
1515 flac_streamheader_to_codecdata (const GValue * streamheader,
1516 GstMatroskaTrackContext * context)
1523 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1524 GST_WARNING ("No or invalid streamheader field in the caps");
1528 bufarr = g_value_peek_pointer (streamheader);
1529 if (bufarr->len < 2) {
1530 GST_WARNING ("Too few headers in streamheader field");
1534 context->xiph_headers_to_skip = bufarr->len + 1;
1536 bufval = &g_array_index (bufarr, GValue, 0);
1537 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1538 GST_WARNING ("streamheaders array does not contain GstBuffers");
1542 buffer = g_value_peek_pointer (bufval);
1544 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1545 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1546 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1547 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1548 GST_WARNING ("Invalid streamheader for FLAC");
1552 gst_matroska_mux_free_codec_priv (context);
1553 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1554 context->codec_priv = g_malloc (context->codec_priv_size);
1555 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1557 for (i = 1; i < bufarr->len; i++) {
1559 bufval = &g_array_index (bufarr, GValue, i);
1561 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1562 gst_matroska_mux_free_codec_priv (context);
1563 GST_WARNING ("streamheaders array does not contain GstBuffers");
1567 buffer = g_value_peek_pointer (bufval);
1569 old_size = context->codec_priv_size;
1570 context->codec_priv_size += gst_buffer_get_size (buffer);
1572 context->codec_priv = g_realloc (context->codec_priv,
1573 context->codec_priv_size);
1574 gst_buffer_extract (buffer, 0,
1575 (guint8 *) context->codec_priv + old_size, -1);
1582 speex_streamheader_to_codecdata (const GValue * streamheader,
1583 GstMatroskaTrackContext * context)
1590 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1591 GST_WARNING ("No or invalid streamheader field in the caps");
1595 bufarr = g_value_peek_pointer (streamheader);
1596 if (bufarr->len != 2) {
1597 GST_WARNING ("Too few headers in streamheader field");
1601 context->xiph_headers_to_skip = bufarr->len + 1;
1603 bufval = &g_array_index (bufarr, GValue, 0);
1604 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1605 GST_WARNING ("streamheaders array does not contain GstBuffers");
1609 buffer = g_value_peek_pointer (bufval);
1611 if (gst_buffer_get_size (buffer) < 80
1612 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1613 GST_WARNING ("Invalid streamheader for Speex");
1617 gst_matroska_mux_free_codec_priv (context);
1618 context->codec_priv_size = gst_buffer_get_size (buffer);
1619 context->codec_priv = g_malloc (context->codec_priv_size);
1620 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1622 bufval = &g_array_index (bufarr, GValue, 1);
1624 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1625 gst_matroska_mux_free_codec_priv (context);
1626 GST_WARNING ("streamheaders array does not contain GstBuffers");
1630 buffer = g_value_peek_pointer (bufval);
1632 old_size = context->codec_priv_size;
1633 context->codec_priv_size += gst_buffer_get_size (buffer);
1634 context->codec_priv = g_realloc (context->codec_priv,
1635 context->codec_priv_size);
1636 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1642 opus_streamheader_to_codecdata (const GValue * streamheader,
1643 GstMatroskaTrackContext * context)
1649 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1652 bufarr = g_value_peek_pointer (streamheader);
1653 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1656 /* Opus headers are not in-band */
1657 context->xiph_headers_to_skip = 0;
1659 bufval = &g_array_index (bufarr, GValue, 0);
1660 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1661 goto wrong_content_type;
1663 buf = g_value_peek_pointer (bufval);
1665 gst_matroska_mux_free_codec_priv (context);
1667 context->codec_priv_size = gst_buffer_get_size (buf);
1668 context->codec_priv = g_malloc0 (context->codec_priv_size);
1669 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1671 context->codec_delay =
1672 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1673 context->codec_delay =
1674 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1675 context->seek_preroll = 80 * GST_MSECOND;
1682 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1683 G_VALUE_TYPE_NAME (streamheader));
1688 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1693 GST_WARNING ("streamheaders array does not contain GstBuffers");
1699 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1703 guint8 channel_mapping_family;
1704 guint8 stream_count, coupled_count, channel_mapping[256];
1708 /* Opus headers are not in-band */
1709 context->xiph_headers_to_skip = 0;
1711 context->codec_delay = 0;
1712 context->seek_preroll = 80 * GST_MSECOND;
1714 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1715 &channel_mapping_family, &stream_count, &coupled_count,
1717 GST_WARNING ("Failed to parse caps for Opus");
1722 gst_codec_utils_opus_create_header (rate, channels,
1723 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1726 GST_WARNING ("Failed to create Opus header from caps");
1730 gst_buffer_map (buffer, &map, GST_MAP_READ);
1731 context->codec_priv_size = map.size;
1732 context->codec_priv = g_malloc (context->codec_priv_size);
1733 memcpy (context->codec_priv, map.data, map.size);
1734 gst_buffer_unmap (buffer, &map);
1735 gst_buffer_unref (buffer);
1741 * gst_matroska_mux_audio_pad_setcaps:
1742 * @pad: Pad which got the caps.
1745 * Setcaps function for audio sink pad.
1747 * Returns: #TRUE on success.
1750 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1752 GstMatroskaTrackContext *context = NULL;
1753 GstMatroskaTrackAudioContext *audiocontext;
1754 GstMatroskaMux *mux;
1755 GstMatroskaPad *collect_pad;
1756 const gchar *mimetype;
1757 gint samplerate = 0, channels = 0;
1758 GstStructure *structure;
1759 const GValue *codec_data = NULL;
1760 GstBuffer *buf = NULL;
1761 const gchar *stream_format = NULL;
1763 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1766 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1767 g_assert (collect_pad);
1768 context = collect_pad->track;
1770 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1771 audiocontext = (GstMatroskaTrackAudioContext *) context;
1773 structure = gst_caps_get_structure (caps, 0);
1774 mimetype = gst_structure_get_name (structure);
1777 gst_structure_get_int (structure, "rate", &samplerate);
1778 gst_structure_get_int (structure, "channels", &channels);
1780 audiocontext->samplerate = samplerate;
1781 audiocontext->channels = channels;
1782 audiocontext->bitdepth = 0;
1783 context->default_duration = 0;
1785 codec_data = gst_structure_get_value (structure, "codec_data");
1787 buf = gst_value_get_buffer (codec_data);
1789 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1790 * data and other settings
1794 if (!strcmp (mimetype, "audio/mpeg")) {
1795 gint mpegversion = 0;
1797 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1798 switch (mpegversion) {
1804 gst_structure_get_int (structure, "layer", &layer);
1806 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1807 GST_WARNING_OBJECT (mux,
1808 "Unable to determine MPEG audio version, assuming 1");
1814 else if (layer == 2)
1816 else if (version == 2)
1821 context->default_duration =
1822 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1826 gst_matroska_mux_set_codec_id (context,
1827 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1830 gst_matroska_mux_set_codec_id (context,
1831 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1834 gst_matroska_mux_set_codec_id (context,
1835 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1844 stream_format = gst_structure_get_string (structure, "stream-format");
1845 /* check this is raw aac */
1846 if (stream_format) {
1847 if (strcmp (stream_format, "raw") != 0) {
1848 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1852 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1857 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AAC);
1858 context->codec_priv_size = gst_buffer_get_size (buf);
1859 context->codec_priv = g_malloc (context->codec_priv_size);
1860 gst_buffer_extract (buf, 0, context->codec_priv,
1861 context->codec_priv_size);
1863 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1870 } else if (!strcmp (mimetype, "audio/x-raw")) {
1873 gst_audio_info_init (&info);
1874 if (!gst_audio_info_from_caps (&info, caps)) {
1875 GST_DEBUG_OBJECT (mux,
1876 "broken caps, rejected by gst_audio_info_from_caps");
1880 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1881 case GST_AUDIO_FORMAT_U8:
1882 case GST_AUDIO_FORMAT_S16BE:
1883 case GST_AUDIO_FORMAT_S16LE:
1884 case GST_AUDIO_FORMAT_S24BE:
1885 case GST_AUDIO_FORMAT_S24LE:
1886 case GST_AUDIO_FORMAT_S32BE:
1887 case GST_AUDIO_FORMAT_S32LE:
1888 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1889 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1892 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1893 gst_matroska_mux_set_codec_id (context,
1894 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1896 gst_matroska_mux_set_codec_id (context,
1897 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1899 case GST_AUDIO_FORMAT_F32LE:
1900 case GST_AUDIO_FORMAT_F64LE:
1901 gst_matroska_mux_set_codec_id (context,
1902 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1906 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1910 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1911 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1912 const GValue *streamheader;
1914 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1916 gst_matroska_mux_free_codec_priv (context);
1918 streamheader = gst_structure_get_value (structure, "streamheader");
1919 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1920 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1921 ("vorbis stream headers missing or malformed"));
1924 } else if (!strcmp (mimetype, "audio/x-flac")) {
1925 const GValue *streamheader;
1927 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1929 gst_matroska_mux_free_codec_priv (context);
1931 streamheader = gst_structure_get_value (structure, "streamheader");
1932 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1933 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1934 ("flac stream headers missing or malformed"));
1937 } else if (!strcmp (mimetype, "audio/x-speex")) {
1938 const GValue *streamheader;
1940 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1941 gst_matroska_mux_free_codec_priv (context);
1943 streamheader = gst_structure_get_value (structure, "streamheader");
1944 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1945 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1946 ("speex stream headers missing or malformed"));
1949 } else if (!strcmp (mimetype, "audio/x-opus")) {
1950 const GValue *streamheader;
1952 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
1954 streamheader = gst_structure_get_value (structure, "streamheader");
1956 gst_matroska_mux_free_codec_priv (context);
1957 if (!opus_streamheader_to_codecdata (streamheader, context)) {
1958 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1959 ("opus stream headers missing or malformed"));
1963 /* no streamheader, but we need to have one, so we make one up
1965 gst_matroska_mux_free_codec_priv (context);
1966 if (!opus_make_codecdata (context, caps)) {
1967 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1968 ("opus stream headers missing or malformed"));
1972 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1973 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1974 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1975 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1976 } else if (!strcmp (mimetype, "audio/x-dts")) {
1977 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1978 } else if (!strcmp (mimetype, "audio/x-tta")) {
1981 /* TTA frame duration */
1982 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1984 gst_structure_get_int (structure, "width", &width);
1985 audiocontext->bitdepth = width;
1986 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1988 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1990 const GValue *mdpr_data;
1992 gst_structure_get_int (structure, "raversion", &raversion);
1993 switch (raversion) {
1995 gst_matroska_mux_set_codec_id (context,
1996 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1999 gst_matroska_mux_set_codec_id (context,
2000 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2003 gst_matroska_mux_set_codec_id (context,
2004 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2010 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2011 if (mdpr_data != NULL) {
2012 guint8 *priv_data = NULL;
2013 guint priv_data_size = 0;
2015 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2017 priv_data_size = gst_buffer_get_size (codec_data_buf);
2018 priv_data = g_malloc0 (priv_data_size);
2020 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2022 gst_matroska_mux_free_codec_priv (context);
2024 context->codec_priv = priv_data;
2025 context->codec_priv_size = priv_data_size;
2028 } else if (!strcmp (mimetype, "audio/x-wma")
2029 || !strcmp (mimetype, "audio/x-alaw")
2030 || !strcmp (mimetype, "audio/x-mulaw")
2031 || !strcmp (mimetype, "audio/x-adpcm")) {
2033 guint codec_priv_size;
2035 gint block_align = 0;
2038 if (samplerate == 0 || channels == 0) {
2039 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2043 if (!strcmp (mimetype, "audio/x-wma")) {
2047 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2048 || !gst_structure_get_int (structure, "block_align", &block_align)
2049 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2050 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2055 switch (wmaversion) {
2057 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2060 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2063 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2066 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2070 if (gst_structure_get_int (structure, "depth", &depth))
2071 audiocontext->bitdepth = depth;
2072 } else if (!strcmp (mimetype, "audio/x-alaw")
2073 || !strcmp (mimetype, "audio/x-mulaw")) {
2074 audiocontext->bitdepth = 8;
2075 if (!strcmp (mimetype, "audio/x-alaw"))
2076 format = GST_RIFF_WAVE_FORMAT_ALAW;
2078 format = GST_RIFF_WAVE_FORMAT_MULAW;
2080 block_align = channels;
2081 bitrate = block_align * samplerate;
2082 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2085 layout = gst_structure_get_string (structure, "layout");
2087 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2091 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2092 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2096 if (!strcmp (layout, "dvi")) {
2097 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2098 } else if (!strcmp (layout, "g726")) {
2099 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2100 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2101 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2105 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2110 g_assert (format != 0);
2112 codec_priv_size = WAVEFORMATEX_SIZE;
2114 codec_priv_size += gst_buffer_get_size (buf);
2116 /* serialize waveformatex structure */
2117 codec_priv = g_malloc0 (codec_priv_size);
2118 GST_WRITE_UINT16_LE (codec_priv, format);
2119 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2120 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2121 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2122 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2123 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2125 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2127 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2129 /* process codec private/initialization data, if any */
2131 gst_buffer_extract (buf, 0,
2132 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2135 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2136 gst_matroska_mux_free_codec_priv (context);
2137 context->codec_priv = (gpointer) codec_priv;
2138 context->codec_priv_size = codec_priv_size;
2146 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2147 GST_PAD_NAME (pad), caps);
2152 /* we probably don't have the data at start,
2153 * so have to reserve (a maximum) space to write this at the end.
2154 * bit spacy, but some formats can hold quite some */
2155 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2158 * gst_matroska_mux_subtitle_pad_setcaps:
2159 * @pad: Pad which got the caps.
2162 * Setcaps function for subtitle sink pad.
2164 * Returns: #TRUE on success.
2167 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2169 /* There is now (at least) one such alement (kateenc), and I'm going
2170 to handle it here and claim it works when it can be piped back
2171 through GStreamer and VLC */
2173 GstMatroskaTrackContext *context = NULL;
2174 GstMatroskaTrackSubtitleContext *scontext;
2175 GstMatroskaMux *mux;
2176 GstMatroskaPad *collect_pad;
2177 const gchar *mimetype;
2178 GstStructure *structure;
2179 const GValue *value = NULL;
2180 GstBuffer *buf = NULL;
2181 gboolean ret = TRUE;
2183 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2186 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2187 g_assert (collect_pad);
2188 context = collect_pad->track;
2190 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2191 scontext = (GstMatroskaTrackSubtitleContext *) context;
2193 structure = gst_caps_get_structure (caps, 0);
2194 mimetype = gst_structure_get_name (structure);
2197 scontext->check_utf8 = 1;
2198 scontext->invalid_utf8 = 0;
2199 context->default_duration = 0;
2201 if (!strcmp (mimetype, "subtitle/x-kate")) {
2202 const GValue *streamheader;
2204 gst_matroska_mux_set_codec_id (context,
2205 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2207 gst_matroska_mux_free_codec_priv (context);
2209 streamheader = gst_structure_get_value (structure, "streamheader");
2210 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2211 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2212 ("kate stream headers missing or malformed"));
2216 } else if (!strcmp (mimetype, "text/x-raw")) {
2217 gst_matroska_mux_set_codec_id (context,
2218 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2219 } else if (!strcmp (mimetype, "application/x-ssa")) {
2220 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2221 } else if (!strcmp (mimetype, "application/x-ass")) {
2222 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2223 } else if (!strcmp (mimetype, "application/x-usf")) {
2224 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2225 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2226 gst_matroska_mux_set_codec_id (context,
2227 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2233 /* maybe some private data, e.g. vobsub */
2234 value = gst_structure_get_value (structure, "codec_data");
2236 buf = gst_value_get_buffer (value);
2239 guint8 *priv_data = NULL;
2241 gst_buffer_map (buf, &map, GST_MAP_READ);
2243 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2244 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2245 " exceeded maximum (%d); discarding", pad,
2246 SUBTITLE_MAX_CODEC_PRIVATE);
2247 gst_buffer_unmap (buf, &map);
2251 gst_matroska_mux_free_codec_priv (context);
2253 priv_data = g_malloc0 (map.size);
2254 memcpy (priv_data, map.data, map.size);
2255 context->codec_priv = priv_data;
2256 context->codec_priv_size = map.size;
2257 gst_buffer_unmap (buf, &map);
2260 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2261 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2270 * gst_matroska_mux_request_new_pad:
2271 * @element: #GstMatroskaMux.
2272 * @templ: #GstPadTemplate.
2273 * @pad_name: New pad name.
2275 * Request pad function for sink templates.
2277 * Returns: New #GstPad.
2280 gst_matroska_mux_request_new_pad (GstElement * element,
2281 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2283 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2284 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2285 GstMatroskaPad *collect_pad;
2286 GstMatroskamuxPad *newpad;
2288 const gchar *pad_name = NULL;
2289 GstMatroskaCapsFunc capsfunc = NULL;
2290 GstMatroskaTrackContext *context = NULL;
2292 gboolean locked = TRUE;
2295 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2296 /* don't mix named and unnamed pads, if the pad already exists we fail when
2297 * trying to add it */
2298 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2299 pad_name = req_name;
2301 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2304 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2305 context = (GstMatroskaTrackContext *)
2306 g_new0 (GstMatroskaTrackAudioContext, 1);
2307 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2308 context->name = g_strdup ("Audio");
2309 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2310 /* don't mix named and unnamed pads, if the pad already exists we fail when
2311 * trying to add it */
2312 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2313 pad_name = req_name;
2315 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2318 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2319 context = (GstMatroskaTrackContext *)
2320 g_new0 (GstMatroskaTrackVideoContext, 1);
2321 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2322 context->name = g_strdup ("Video");
2323 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2324 /* don't mix named and unnamed pads, if the pad already exists we fail when
2325 * trying to add it */
2326 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2327 pad_name = req_name;
2329 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2332 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2333 context = (GstMatroskaTrackContext *)
2334 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2335 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2336 context->name = g_strdup ("Subtitle");
2337 /* setcaps may only provide proper one a lot later */
2338 id = g_strdup ("S_SUB_UNKNOWN");
2341 GST_WARNING_OBJECT (mux, "This is not our template!");
2345 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2346 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2348 gst_matroskamux_pad_init (newpad);
2349 collect_pad = (GstMatroskaPad *)
2350 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2351 sizeof (GstMatroskamuxPad),
2352 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2354 collect_pad->mux = mux;
2355 collect_pad->track = context;
2356 gst_matroska_pad_reset (collect_pad, FALSE);
2357 collect_pad->track->codec_id = id;
2358 collect_pad->track->dts_only = FALSE;
2360 collect_pad->capsfunc = capsfunc;
2361 gst_pad_set_active (GST_PAD (newpad), TRUE);
2362 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2363 goto pad_add_failed;
2369 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2371 return GST_PAD (newpad);
2376 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2378 gst_object_unref (newpad);
2384 * gst_matroska_mux_release_pad:
2385 * @element: #GstMatroskaMux.
2386 * @pad: Pad to release.
2388 * Release a previously requested pad.
2391 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2393 GstMatroskaMux *mux;
2396 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2398 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2399 GstCollectData *cdata = (GstCollectData *) walk->data;
2400 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2402 if (cdata->pad == pad) {
2404 * observed duration, this will remain GST_CLOCK_TIME_NONE
2405 * only if the pad is resetted
2407 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2409 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2410 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2411 collected_duration =
2412 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2415 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2416 && mux->duration < collected_duration)
2417 mux->duration = collected_duration;
2423 gst_collect_pads_remove_pad (mux->collect, pad);
2424 if (gst_element_remove_pad (element, pad))
2430 * gst_matroska_mux_track_header:
2431 * @mux: #GstMatroskaMux
2432 * @context: Tack context.
2434 * Write a track header.
2437 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2438 GstMatroskaTrackContext * context)
2440 GstEbmlWrite *ebml = mux->ebml_write;
2443 /* TODO: check if everything necessary is written and check default values */
2445 /* track type goes before the type-specific stuff */
2446 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2447 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2449 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2450 if (context->default_duration) {
2451 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2452 context->default_duration);
2454 if (context->language) {
2455 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2459 /* FIXME: until we have a nice way of getting the codecname
2460 * out of the caps, I'm not going to enable this. Too much
2461 * (useless, double, boring) work... */
2462 /* TODO: Use value from tags if any */
2463 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2464 context->codec_name); */
2465 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2467 /* type-specific stuff */
2468 switch (context->type) {
2469 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2470 GstMatroskaTrackVideoContext *videocontext =
2471 (GstMatroskaTrackVideoContext *) context;
2473 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2474 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2475 videocontext->pixel_width);
2476 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2477 videocontext->pixel_height);
2478 if (videocontext->display_width && videocontext->display_height) {
2479 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2480 videocontext->display_width);
2481 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2482 videocontext->display_height);
2484 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2485 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2486 if (videocontext->fourcc) {
2487 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2489 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2490 (gpointer) & fcc_le, 4);
2492 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2493 guint64 stereo_mode = 0;
2495 switch (videocontext->multiview_mode) {
2496 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2498 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2499 if (videocontext->multiview_flags &
2500 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2501 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2503 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2505 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2506 if (videocontext->multiview_flags &
2507 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2508 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2510 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2512 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2513 if (videocontext->multiview_flags &
2514 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2515 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2517 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2519 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2520 if (videocontext->multiview_flags &
2521 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2522 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2524 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2525 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2526 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2527 GST_FIXME_OBJECT (mux,
2528 "Frame-by-frame stereoscopic mode not fully implemented");
2531 GST_WARNING_OBJECT (mux,
2532 "Multiview mode %d not supported in Matroska/WebM",
2533 videocontext->multiview_mode);
2537 if (stereo_mode != 0)
2538 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2541 gst_ebml_write_master_finish (ebml, master);
2546 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2547 GstMatroskaTrackAudioContext *audiocontext =
2548 (GstMatroskaTrackAudioContext *) context;
2550 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2551 if (audiocontext->samplerate != 8000)
2552 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2553 audiocontext->samplerate);
2554 if (audiocontext->channels != 1)
2555 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2556 audiocontext->channels);
2557 if (audiocontext->bitdepth) {
2558 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2559 audiocontext->bitdepth);
2562 gst_ebml_write_master_finish (ebml, master);
2567 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2571 /* doesn't need type-specific data */
2575 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2576 if (context->codec_priv)
2577 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2578 context->codec_priv, context->codec_priv_size);
2580 if (context->seek_preroll) {
2581 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2582 context->seek_preroll);
2585 if (context->codec_delay) {
2586 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2587 context->codec_delay);
2593 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2595 guint64 title_master;
2598 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2600 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2601 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2602 GST_MATROSKA_MUX_CHAPLANG);
2604 gst_ebml_write_master_finish (ebml, title_master);
2608 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2609 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2610 guint64 * master_edition)
2612 guint64 uid, master_chapteratom;
2614 GstTocEntry *cur_entry;
2619 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2621 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2623 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2624 /* create uid for the parent */
2625 uid = gst_matroska_mux_create_uid ();
2626 g_free (edition->uid);
2627 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2630 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2632 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2633 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2634 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2635 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2638 uid = gst_matroska_mux_create_uid ();
2639 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2641 master_chapteratom =
2642 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2643 g_free (entry->uid);
2644 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2645 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2646 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2647 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2648 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2649 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2651 cur = entry->subentries;
2652 while (cur != NULL) {
2653 cur_entry = cur->data;
2654 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2659 if (G_LIKELY (entry->tags != NULL)) {
2660 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2662 for (i = 0; i < count; ++i) {
2663 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2664 gst_matroska_mux_write_chapter_title (title, ebml);
2668 /* remove title tag */
2669 if (G_LIKELY (count > 0))
2670 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2673 gst_ebml_write_master_finish (ebml, master_chapteratom);
2677 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2678 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2680 guint64 master_edition = 0;
2682 GstTocEntry *subentry;
2684 cur = gst_toc_entry_get_sub_entries (entry);
2685 while (cur != NULL) {
2686 subentry = cur->data;
2687 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2693 if (G_LIKELY (master_edition != 0))
2694 gst_ebml_write_master_finish (ebml, master_edition);
2699 * gst_matroska_mux_start:
2700 * @mux: #GstMatroskaMux
2702 * Start a new matroska file (write headers etc...)
2705 gst_matroska_mux_start (GstMatroskaMux * mux)
2707 GstEbmlWrite *ebml = mux->ebml_write;
2708 const gchar *doctype;
2709 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2710 GST_MATROSKA_ID_TRACKS,
2711 GST_MATROSKA_ID_CHAPTERS,
2712 GST_MATROSKA_ID_CUES,
2713 GST_MATROSKA_ID_TAGS,
2716 const gchar *media_type;
2717 gboolean audio_only;
2718 guint64 master, child;
2722 GstClockTime duration = 0;
2723 guint32 segment_uid[4];
2724 GTimeVal time = { 0, 0 };
2730 /* if not streaming, check if downstream is seekable */
2731 if (!mux->ebml_write->streamable) {
2735 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2736 if (gst_pad_peer_query (mux->srcpad, query)) {
2737 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2738 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2740 /* assume seeking is not supported if query not handled downstream */
2741 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2745 mux->ebml_write->streamable = TRUE;
2746 g_object_notify (G_OBJECT (mux), "streamable");
2747 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2748 "streamable=false. Will ignore that and create streamable output "
2751 gst_query_unref (query);
2754 /* stream-start (FIXME: create id based on input ids) */
2755 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
2756 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
2759 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
2761 media_type = (audio_only) ? "audio/webm" : "video/webm";
2763 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
2765 ebml->caps = gst_caps_new_empty_simple (media_type);
2766 gst_pad_set_caps (mux->srcpad, ebml->caps);
2767 /* we start with a EBML header */
2768 doctype = mux->doctype;
2769 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2770 doctype, mux->doctype_version);
2771 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2773 /* the rest of the header is cached */
2774 gst_ebml_write_set_cache (ebml, 0x1000);
2776 /* start a segment */
2778 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2779 mux->segment_master = ebml->pos;
2781 if (!mux->ebml_write->streamable) {
2782 /* seekhead (table of contents) - we set the positions later */
2783 mux->seekhead_pos = ebml->pos;
2784 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2785 for (i = 0; seekhead_id[i] != 0; i++) {
2786 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2787 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2788 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2789 gst_ebml_write_master_finish (ebml, child);
2791 gst_ebml_write_master_finish (ebml, master);
2794 if (mux->ebml_write->streamable) {
2795 const GstTagList *tags;
2796 gboolean has_main_tags;
2799 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2800 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
2802 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
2803 guint64 master_tags, master_tag;
2805 GST_DEBUG_OBJECT (mux, "Writing tags");
2807 mux->tags_pos = ebml->pos;
2808 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2809 if (has_main_tags) {
2810 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2811 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2812 gst_ebml_write_master_finish (ebml, master_tag);
2814 gst_matroska_mux_write_streams_tags (mux);
2815 gst_ebml_write_master_finish (ebml, master_tags);
2820 mux->info_pos = ebml->pos;
2821 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2823 /* WebM does not support SegmentUID field on SegmentInfo */
2824 if (!mux->is_webm) {
2825 for (i = 0; i < 4; i++) {
2826 segment_uid[i] = g_random_int ();
2828 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2829 (guint8 *) segment_uid, 16);
2832 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2833 mux->duration_pos = ebml->pos;
2835 if (!mux->ebml_write->streamable) {
2836 for (collected = mux->collect->data; collected;
2837 collected = g_slist_next (collected)) {
2838 GstMatroskaPad *collect_pad;
2840 gint64 trackduration;
2842 collect_pad = (GstMatroskaPad *) collected->data;
2843 thepad = collect_pad->collect.pad;
2845 /* Query the total length of the track. */
2846 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2847 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2848 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2849 GST_TIME_ARGS (trackduration));
2850 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2851 duration = (GstClockTime) trackduration;
2855 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2856 gst_guint64_to_gdouble (duration) /
2857 gst_guint64_to_gdouble (mux->time_scale));
2859 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2860 "GStreamer matroskamux version " PACKAGE_VERSION);
2861 if (mux->writing_app && mux->writing_app[0]) {
2862 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2864 g_get_current_time (&time);
2865 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2866 gst_ebml_write_master_finish (ebml, master);
2869 mux->tracks_pos = ebml->pos;
2870 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2872 for (collected = mux->collect->data; collected;
2873 collected = g_slist_next (collected)) {
2874 GstMatroskaPad *collect_pad;
2877 collect_pad = (GstMatroskaPad *) collected->data;
2878 thepad = collect_pad->collect.pad;
2880 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2881 collect_pad->track->codec_id != 0) {
2882 collect_pad->track->num = tracknum++;
2883 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2884 gst_matroska_mux_track_header (mux, collect_pad->track);
2885 gst_ebml_write_master_finish (ebml, child);
2886 /* some remaining pad/track setup */
2887 collect_pad->default_duration_scaled =
2888 gst_util_uint64_scale (collect_pad->track->default_duration,
2889 1, mux->time_scale);
2892 gst_ebml_write_master_finish (ebml, master);
2894 /* FIXME: Check if we get a TOC that is supported by Matroska
2895 * and clean up the code below */
2898 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2899 if (toc != NULL && !mux->ebml_write->streamable) {
2900 guint64 master_chapters = 0;
2901 GstTocEntry *toc_entry;
2902 GList *cur, *to_write = NULL;
2905 GST_DEBUG ("Writing chapters");
2907 /* check whether we have editions or chapters at the root level */
2908 toc_entry = toc->entries->data;
2910 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2911 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2912 gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
2914 /* aggregate all chapters without root edition */
2915 cur = gst_toc_get_entries (toc);
2916 while (cur != NULL) {
2917 toc_entry->subentries =
2918 g_list_prepend (toc_entry->subentries, cur->data);
2922 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2923 toc_entry->subentries->data), &start, NULL);
2924 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2925 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2926 toc_entry->subentries->data), NULL, &stop);
2927 gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
2929 to_write = g_list_append (to_write, toc_entry);
2932 to_write = toc->entries;
2935 /* finally write chapters */
2936 mux->chapters_pos = ebml->pos;
2939 while (cur != NULL) {
2940 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2945 /* close master element if any edition was written */
2946 if (G_LIKELY (master_chapters != 0))
2947 gst_ebml_write_master_finish (ebml, master_chapters);
2949 if (toc_entry != NULL) {
2950 g_list_free (toc_entry->subentries);
2951 toc_entry->subentries = NULL;
2952 gst_toc_entry_unref (toc_entry);
2953 g_list_free (to_write);
2958 /* lastly, flush the cache */
2959 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2963 gst_toc_unref (toc);
2967 /* TODO: more sensible tag mappings */
2970 const gchar *matroska_tagname;
2971 const gchar *gstreamer_tagname;
2973 gst_matroska_tag_conv[] = {
2975 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2976 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2977 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2978 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2979 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2980 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2981 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2982 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2983 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2984 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2985 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2986 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2987 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2988 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2989 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2992 /* Every stagefright implementation on android up to and including 6.0.1 is using
2993 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
2994 so before outputting tags and tag elements we better make sure that there are
2995 actually tags we are going to write */
2997 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3000 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3001 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3003 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3004 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3005 if (strcmp (tagname_gst, tag) == 0) {
3006 GValue src = { 0, };
3009 if (!gst_tag_list_copy_value (&src, list, tag))
3011 dest = gst_value_serialize (&src);
3013 g_value_unset (&src);
3025 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3028 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3030 guint64 simpletag_master;
3032 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3033 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3034 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3036 if (strcmp (tagname_gst, tag) == 0) {
3037 GValue src = { 0, };
3040 if (!gst_tag_list_copy_value (&src, list, tag))
3042 if ((dest = gst_value_serialize (&src))) {
3044 simpletag_master = gst_ebml_write_master_start (ebml,
3045 GST_MATROSKA_ID_SIMPLETAG);
3046 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3047 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3048 gst_ebml_write_master_finish (ebml, simpletag_master);
3051 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3053 g_value_unset (&src);
3060 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3062 guint64 master_tag, master_targets;
3065 ebml = mux->ebml_write;
3067 if (G_UNLIKELY (mpad->tags == NULL
3068 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3071 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3072 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3074 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3076 gst_ebml_write_master_finish (ebml, master_targets);
3077 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3078 gst_ebml_write_master_finish (ebml, master_tag);
3082 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3086 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3087 GstMatroskaPad *collect_pad;
3089 collect_pad = (GstMatroskaPad *) walk->data;
3091 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3096 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3100 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3101 GstMatroskaPad *collect_pad;
3103 collect_pad = (GstMatroskaPad *) walk->data;
3104 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3112 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3113 const GstTocEntry * entry, guint64 * master_tags)
3115 guint64 master_tag, master_targets;
3119 ebml = mux->ebml_write;
3121 if (G_UNLIKELY (entry->tags != NULL
3122 && !gst_matroska_mux_tag_list_is_empty (entry->tags))) {
3123 if (*master_tags == 0) {
3124 mux->tags_pos = ebml->pos;
3125 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3128 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3130 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3132 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
3133 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3134 g_ascii_strtoull (entry->uid, NULL, 10));
3136 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3137 g_ascii_strtoull (entry->uid, NULL, 10));
3139 gst_ebml_write_master_finish (ebml, master_targets);
3140 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
3141 gst_ebml_write_master_finish (ebml, master_tag);
3144 cur = entry->subentries;
3145 while (cur != NULL) {
3146 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
3153 * gst_matroska_mux_finish:
3154 * @mux: #GstMatroskaMux
3156 * Finish a new matroska file (write index etc...)
3159 gst_matroska_mux_finish (GstMatroskaMux * mux)
3161 GstEbmlWrite *ebml = mux->ebml_write;
3163 guint64 duration = 0;
3165 const GstTagList *tags;
3166 gboolean has_main_tags;
3168 /* finish last cluster */
3170 gst_ebml_write_master_finish (ebml, mux->cluster);
3174 if (mux->index != NULL) {
3176 guint64 master, pointentry_master, trackpos_master;
3178 mux->cues_pos = ebml->pos;
3179 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3180 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3182 for (n = 0; n < mux->num_indexes; n++) {
3183 GstMatroskaIndex *idx = &mux->index[n];
3185 pointentry_master = gst_ebml_write_master_start (ebml,
3186 GST_MATROSKA_ID_POINTENTRY);
3187 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3188 idx->time / mux->time_scale);
3189 trackpos_master = gst_ebml_write_master_start (ebml,
3190 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3191 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3192 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3193 idx->pos - mux->segment_master);
3194 gst_ebml_write_master_finish (ebml, trackpos_master);
3195 gst_ebml_write_master_finish (ebml, pointentry_master);
3198 gst_ebml_write_master_finish (ebml, master);
3199 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3203 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3204 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3206 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)
3207 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
3208 guint64 master_tags = 0, master_tag;
3213 GST_DEBUG_OBJECT (mux, "Writing tags");
3216 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3219 if (has_main_tags) {
3220 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3221 mux->tags_pos = ebml->pos;
3222 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3223 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3226 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3229 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
3233 gst_ebml_write_master_finish (ebml, master_tag);
3238 while (cur != NULL) {
3239 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
3245 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3246 mux->tags_pos = ebml->pos;
3247 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3249 gst_matroska_mux_write_streams_tags (mux);
3251 if (master_tags != 0)
3252 gst_ebml_write_master_finish (ebml, master_tags);
3255 /* update seekhead. We know that:
3256 * - a seekhead contains 5 entries.
3257 * - order of entries is as above.
3258 * - a seekhead has a 4-byte header + 8-byte length
3259 * - each entry is 2-byte master, 2-byte ID pointer,
3260 * 2-byte length pointer, all 8/1-byte length, 4-
3261 * byte ID and 8-byte length pointer, where the
3262 * length pointer starts at 20.
3263 * - all entries are local to the segment (so pos - segment_master).
3264 * - so each entry is at 12 + 20 + num * 28. */
3265 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3266 mux->info_pos - mux->segment_master);
3267 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3268 mux->tracks_pos - mux->segment_master);
3269 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
3270 && mux->chapters_pos > 0) {
3271 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3272 mux->chapters_pos - mux->segment_master);
3275 guint64 my_pos = ebml->pos;
3277 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3278 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3279 gst_ebml_write_seek (ebml, my_pos);
3281 if (mux->index != NULL) {
3282 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3283 mux->cues_pos - mux->segment_master);
3286 guint64 my_pos = ebml->pos;
3288 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3289 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3290 gst_ebml_write_seek (ebml, my_pos);
3294 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3295 mux->tags_pos - mux->segment_master);
3298 guint64 my_pos = ebml->pos;
3300 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3301 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3302 gst_ebml_write_seek (ebml, my_pos);
3306 * - first get the overall duration
3307 * (a released track may have left a duration in here)
3308 * - write some track header data for subtitles
3310 duration = mux->duration;
3312 for (collected = mux->collect->data; collected;
3313 collected = g_slist_next (collected)) {
3314 GstMatroskaPad *collect_pad;
3316 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3317 * since this means buffer without timestamps that is not possibile
3319 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3321 collect_pad = (GstMatroskaPad *) collected->data;
3323 GST_DEBUG_OBJECT (mux,
3324 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3325 " end ts %" GST_TIME_FORMAT, collect_pad,
3326 GST_TIME_ARGS (collect_pad->start_ts),
3327 GST_TIME_ARGS (collect_pad->end_ts));
3329 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3330 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3331 collected_duration =
3332 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3333 GST_DEBUG_OBJECT (collect_pad,
3334 "final track duration: %" GST_TIME_FORMAT,
3335 GST_TIME_ARGS (collected_duration));
3337 GST_WARNING_OBJECT (collect_pad, "unable to get final track duration");
3339 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3340 duration < collected_duration)
3341 duration = collected_duration;
3345 /* seek back (optional, but do anyway) */
3346 gst_ebml_write_seek (ebml, pos);
3348 /* update duration */
3349 if (duration != 0) {
3350 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3351 GST_TIME_ARGS (duration));
3352 pos = mux->ebml_write->pos;
3353 gst_ebml_write_seek (ebml, mux->duration_pos);
3354 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3355 gst_guint64_to_gdouble (duration) /
3356 gst_guint64_to_gdouble (mux->time_scale));
3357 gst_ebml_write_seek (ebml, pos);
3360 guint64 my_pos = ebml->pos;
3362 gst_ebml_write_seek (ebml, mux->duration_pos);
3363 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3364 gst_ebml_write_seek (ebml, my_pos);
3366 GST_DEBUG_OBJECT (mux, "finishing segment");
3367 /* finish segment - this also writes element length */
3368 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3372 * gst_matroska_mux_buffer_header:
3373 * @track: Track context.
3374 * @relative_timestamp: relative timestamp of the buffer
3375 * @flags: Buffer flags.
3377 * Create a buffer containing buffer header.
3379 * Returns: New buffer.
3382 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3383 gint16 relative_timestamp, int flags)
3386 guint8 *data = g_malloc (4);
3388 hdr = gst_buffer_new_wrapped (data, 4);
3389 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3390 data[0] = track->num | 0x80;
3391 /* time relative to clustertime */
3392 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3400 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3401 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3402 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3405 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3406 GstMatroskaPad * collect_pad, GstBuffer * buf)
3408 GstMatroskaTrackVideoContext *ctx =
3409 (GstMatroskaTrackVideoContext *) collect_pad->track;
3414 guint32 next_parse_offset;
3415 GstBuffer *ret = NULL;
3416 gboolean is_muxing_unit = FALSE;
3418 gst_buffer_map (buf, &map, GST_MAP_READ);
3423 gst_buffer_unmap (buf, &map);
3424 gst_buffer_unref (buf);
3428 /* Check if this buffer contains a picture or end-of-sequence packet */
3429 while (size >= 13) {
3430 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3431 gst_buffer_unmap (buf, &map);
3432 gst_buffer_unref (buf);
3436 parse_code = GST_READ_UINT8 (data + 4);
3437 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3438 if (ctx->dirac_unit) {
3439 gst_buffer_unref (ctx->dirac_unit);
3440 ctx->dirac_unit = NULL;
3442 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3443 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3444 is_muxing_unit = TRUE;
3448 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3450 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3453 data += next_parse_offset;
3454 size -= next_parse_offset;
3457 if (ctx->dirac_unit)
3458 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3460 ctx->dirac_unit = gst_buffer_ref (buf);
3462 gst_buffer_unmap (buf, &map);
3464 if (is_muxing_unit) {
3465 ret = gst_buffer_make_writable (ctx->dirac_unit);
3466 ctx->dirac_unit = NULL;
3467 gst_buffer_copy_into (ret, buf,
3468 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3469 gst_buffer_unref (buf);
3471 gst_buffer_unref (buf);
3479 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3483 GValue streamheader = { 0 };
3484 GValue bufval = { 0 };
3485 GstBuffer *streamheader_buffer;
3486 GstEbmlWrite *ebml = mux->ebml_write;
3488 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3489 caps = gst_caps_copy (mux->ebml_write->caps);
3490 s = gst_caps_get_structure (caps, 0);
3491 g_value_init (&streamheader, GST_TYPE_ARRAY);
3492 g_value_init (&bufval, GST_TYPE_BUFFER);
3493 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3494 gst_value_set_buffer (&bufval, streamheader_buffer);
3495 gst_value_array_append_value (&streamheader, &bufval);
3496 g_value_unset (&bufval);
3497 gst_structure_set_value (s, "streamheader", &streamheader);
3498 g_value_unset (&streamheader);
3499 gst_caps_replace (&ebml->caps, caps);
3500 gst_buffer_unref (streamheader_buffer);
3501 gst_pad_set_caps (mux->srcpad, caps);
3502 gst_caps_unref (caps);
3506 * gst_matroska_mux_write_data:
3507 * @mux: #GstMatroskaMux
3508 * @collect_pad: #GstMatroskaPad with the data
3510 * Write collected data (called from gst_matroska_mux_collected).
3512 * Returns: Result of the gst_pad_push issued to write the data.
3514 static GstFlowReturn
3515 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3518 GstEbmlWrite *ebml = mux->ebml_write;
3521 gboolean write_duration;
3522 gint16 relative_timestamp;
3523 gint64 relative_timestamp64;
3524 guint64 block_duration, duration_diff = 0;
3525 gboolean is_video_keyframe = FALSE;
3526 gboolean is_video_invisible = FALSE;
3527 GstMatroskamuxPad *pad;
3529 GstClockTime buffer_timestamp;
3530 GstAudioClippingMeta *cmeta = NULL;
3533 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3535 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3536 if (collect_pad->track->xiph_headers_to_skip > 0) {
3537 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3538 gst_buffer_unref (buf);
3539 --collect_pad->track->xiph_headers_to_skip;
3543 /* for dirac we have to queue up everything up to a picture unit */
3544 if (collect_pad->track->codec_id != NULL &&
3545 strcmp (collect_pad->track->codec_id,
3546 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3547 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3550 } else if (strcmp (collect_pad->track->codec_id,
3551 GST_MATROSKA_CODEC_ID_VIDEO_PRORES) == 0) {
3552 /* Remove the 'Frame container atom' header' */
3553 buf = gst_buffer_make_writable (buf);
3554 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
3558 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3560 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3561 * this would wreak havoc with time stored in matroska file */
3562 /* TODO: maybe calculate a timestamp by using the previous timestamp
3563 * and default duration */
3564 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
3565 GST_WARNING_OBJECT (collect_pad->collect.pad,
3566 "Invalid buffer timestamp; dropping buffer");
3567 gst_buffer_unref (buf);
3571 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
3572 && collect_pad->track->codec_delay) {
3573 /* All timestamps should include the codec delay */
3574 if (buffer_timestamp > collect_pad->track->codec_delay) {
3575 buffer_timestamp += collect_pad->track->codec_delay;
3577 buffer_timestamp = 0;
3578 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
3582 /* set the timestamp for outgoing buffers */
3583 ebml->timestamp = buffer_timestamp;
3585 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3586 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3587 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3588 GST_TIME_ARGS (buffer_timestamp));
3589 is_video_keyframe = TRUE;
3590 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3591 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3592 || !strcmp (collect_pad->track->codec_id,
3593 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3594 GST_LOG_OBJECT (mux,
3595 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3596 GST_TIME_ARGS (buffer_timestamp));
3597 is_video_invisible = TRUE;
3602 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3603 * or when we may be reaching the limit of the relative timestamp */
3604 if (mux->cluster_time +
3605 mux->max_cluster_duration < buffer_timestamp
3606 || is_video_keyframe || mux->force_key_unit_event) {
3607 if (!mux->ebml_write->streamable)
3608 gst_ebml_write_master_finish (ebml, mux->cluster);
3610 /* Forward the GstForceKeyUnit event after finishing the cluster */
3611 if (mux->force_key_unit_event) {
3612 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3613 mux->force_key_unit_event = NULL;
3616 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3617 mux->cluster_pos = ebml->pos;
3618 gst_ebml_write_set_cache (ebml, 0x20);
3620 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3621 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3622 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3623 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3624 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3625 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
3626 mux->cluster_time = buffer_timestamp;
3627 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3628 mux->prev_cluster_size);
3633 mux->cluster_pos = ebml->pos;
3634 gst_ebml_write_set_cache (ebml, 0x20);
3635 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3636 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3637 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3638 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
3639 mux->cluster_time = buffer_timestamp;
3642 /* We currently write index entries for all video tracks or for the audio
3643 * track in a single-track audio file. This could be improved by keeping the
3644 * index only for the *first* video track. */
3646 /* TODO: index is useful for every track, should contain the number of
3647 * the block in the cluster which contains the timestamp, should also work
3648 * for files with multiple audio tracks.
3650 if (!mux->ebml_write->streamable &&
3651 (is_video_keyframe ||
3652 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3653 (mux->num_streams == 1)))) {
3656 if (mux->min_index_interval != 0) {
3657 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3658 if (mux->index[last_idx].track == collect_pad->track->num)
3663 if (last_idx < 0 || mux->min_index_interval == 0 ||
3664 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
3665 >= mux->min_index_interval)) {
3666 GstMatroskaIndex *idx;
3668 if (mux->num_indexes % 32 == 0) {
3669 mux->index = g_renew (GstMatroskaIndex, mux->index,
3670 mux->num_indexes + 32);
3672 idx = &mux->index[mux->num_indexes++];
3674 idx->pos = mux->cluster_pos;
3675 idx->time = buffer_timestamp;
3676 idx->track = collect_pad->track->num;
3680 /* Check if the duration differs from the default duration. */
3681 write_duration = FALSE;
3683 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3684 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
3685 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
3687 /* small difference should be ok. */
3688 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3689 block_duration < collect_pad->default_duration_scaled - 1) {
3690 write_duration = TRUE;
3694 /* write the block, for doctype v2 use SimpleBlock if possible
3695 * one slice (*breath*).
3696 * FIXME: Need to do correct lacing! */
3697 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
3698 if (relative_timestamp64 >= 0) {
3699 /* round the timestamp */
3700 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3701 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3704 /* round the timestamp */
3705 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3706 relative_timestamp =
3707 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3711 if (is_video_invisible)
3714 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
3715 cmeta = gst_buffer_get_audio_clipping_meta (buf);
3716 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
3718 /* Start clipping is done via header and CodecDelay */
3719 if (cmeta && !cmeta->end)
3723 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
3724 if (is_video_keyframe)
3728 gst_matroska_mux_create_buffer_header (collect_pad->track,
3729 relative_timestamp, flags);
3730 gst_ebml_write_set_cache (ebml, 0x40);
3731 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3732 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3733 gst_ebml_write_buffer (ebml, hdr);
3734 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
3735 gst_ebml_write_buffer (ebml, buf);
3737 return gst_ebml_last_write_result (ebml);
3739 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3740 /* write and call order slightly unnatural,
3741 * but avoids seek and minizes pushing */
3742 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3744 gst_matroska_mux_create_buffer_header (collect_pad->track,
3745 relative_timestamp, flags);
3747 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3749 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
3751 /* Start clipping is done via header and CodecDelay */
3754 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
3755 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
3759 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3760 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3761 gst_ebml_write_buffer (ebml, hdr);
3762 gst_ebml_write_master_finish_full (ebml, blockgroup,
3763 gst_buffer_get_size (buf));
3764 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
3765 gst_ebml_write_buffer (ebml, buf);
3767 return gst_ebml_last_write_result (ebml);
3772 * gst_matroska_mux_handle_buffer:
3773 * @pads: #GstCollectPads
3774 * @uuser_data: #GstMatroskaMux
3776 * Collectpads callback.
3778 * Returns: #GstFlowReturn
3780 static GstFlowReturn
3781 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
3782 GstBuffer * buf, gpointer user_data)
3784 GstClockTime buffer_timestamp;
3785 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3786 GstEbmlWrite *ebml = mux->ebml_write;
3787 GstMatroskaPad *best;
3788 GstFlowReturn ret = GST_FLOW_OK;
3789 GST_DEBUG_OBJECT (mux, "Collected pads");
3791 /* start with a header */
3792 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3793 if (mux->collect->data == NULL) {
3794 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3795 ("No input streams configured"));
3796 return GST_FLOW_ERROR;
3798 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3799 gst_ebml_start_streamheader (ebml);
3800 gst_matroska_mux_start (mux);
3801 gst_matroska_mux_stop_streamheader (mux);
3802 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3805 /* provided with stream to write from */
3806 best = (GstMatroskaPad *) data;
3808 /* if there is no best pad, we have reached EOS */
3810 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
3811 if (!mux->ebml_write->streamable) {
3812 gst_matroska_mux_finish (mux);
3814 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3816 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3821 /* if we have a best stream, should also have a buffer */
3824 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
3826 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3827 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3828 GST_TIME_ARGS (buffer_timestamp),
3829 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3831 /* make note of first and last encountered timestamps, so we can calculate
3832 * the actual duration later when we send an updated header on eos */
3833 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
3834 GstClockTime start_ts = buffer_timestamp;
3835 GstClockTime end_ts = start_ts;
3837 if (GST_BUFFER_DURATION_IS_VALID (buf))
3838 end_ts += GST_BUFFER_DURATION (buf);
3839 else if (best->track->default_duration)
3840 end_ts += best->track->default_duration;
3842 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3843 best->end_ts = end_ts;
3845 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3846 start_ts < best->start_ts))
3847 best->start_ts = start_ts;
3850 /* write one buffer */
3851 ret = gst_matroska_mux_write_data (mux, best, buf);
3859 * gst_matroska_mux_change_state:
3860 * @element: #GstMatroskaMux
3861 * @transition: State change transition.
3863 * Change the muxer state.
3865 * Returns: #GstStateChangeReturn
3867 static GstStateChangeReturn
3868 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3870 GstStateChangeReturn ret;
3871 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3873 switch (transition) {
3874 case GST_STATE_CHANGE_NULL_TO_READY:
3876 case GST_STATE_CHANGE_READY_TO_PAUSED:
3877 gst_collect_pads_start (mux->collect);
3879 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3881 case GST_STATE_CHANGE_PAUSED_TO_READY:
3882 gst_collect_pads_stop (mux->collect);
3888 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3890 switch (transition) {
3891 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3893 case GST_STATE_CHANGE_PAUSED_TO_READY:
3894 gst_matroska_mux_reset (GST_ELEMENT (mux));
3896 case GST_STATE_CHANGE_READY_TO_NULL:
3906 gst_matroska_mux_set_property (GObject * object,
3907 guint prop_id, const GValue * value, GParamSpec * pspec)
3909 GstMatroskaMux *mux;
3911 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3912 mux = GST_MATROSKA_MUX (object);
3915 case PROP_WRITING_APP:
3916 if (!g_value_get_string (value)) {
3917 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3920 g_free (mux->writing_app);
3921 mux->writing_app = g_value_dup_string (value);
3923 case PROP_DOCTYPE_VERSION:
3924 mux->doctype_version = g_value_get_int (value);
3926 case PROP_MIN_INDEX_INTERVAL:
3927 mux->min_index_interval = g_value_get_int64 (value);
3929 case PROP_STREAMABLE:
3930 mux->ebml_write->streamable = g_value_get_boolean (value);
3932 case PROP_TIMECODESCALE:
3933 mux->time_scale = g_value_get_int64 (value);
3936 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3942 gst_matroska_mux_get_property (GObject * object,
3943 guint prop_id, GValue * value, GParamSpec * pspec)
3945 GstMatroskaMux *mux;
3947 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3948 mux = GST_MATROSKA_MUX (object);
3951 case PROP_WRITING_APP:
3952 g_value_set_string (value, mux->writing_app);
3954 case PROP_DOCTYPE_VERSION:
3955 g_value_set_int (value, mux->doctype_version);
3957 case PROP_MIN_INDEX_INTERVAL:
3958 g_value_set_int64 (value, mux->min_index_interval);
3960 case PROP_STREAMABLE:
3961 g_value_set_boolean (value, mux->ebml_write->streamable);
3963 case PROP_TIMECODESCALE:
3964 g_value_set_int64 (value, mux->time_scale);
3967 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);