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, MAX ], " \
93 "height = (int) [ 16, MAX ] "
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 "channels = (int)1," "rate = (int)16000; "
196 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
199 static GstStaticPadTemplate subtitlesink_templ =
200 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
203 GST_STATIC_CAPS ("subtitle/x-kate; "
204 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
205 "application/x-usf; subpicture/x-dvd; "
206 "application/x-subtitle-unknown")
209 static gpointer parent_class; /* NULL */
211 /* Matroska muxer destructor */
212 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
213 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
214 static void gst_matroska_mux_finalize (GObject * object);
216 /* Pads collected callback */
217 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
218 GstCollectData * data, GstBuffer * buf, gpointer user_data);
219 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
220 GstCollectData * data, GstEvent * event, gpointer user_data);
223 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
224 GstObject * parent, GstEvent * event);
225 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
226 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
227 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
229 /* gst internal change state handler */
230 static GstStateChangeReturn
231 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
233 /* gobject bla bla */
234 static void gst_matroska_mux_set_property (GObject * object,
235 guint prop_id, const GValue * value, GParamSpec * pspec);
236 static void gst_matroska_mux_get_property (GObject * object,
237 guint prop_id, GValue * value, GParamSpec * pspec);
240 static void gst_matroska_mux_reset (GstElement * element);
243 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
245 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
251 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
253 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
254 GstMatroskaTrackContext * context);
256 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
258 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
259 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
260 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
262 /* Cannot use boilerplate macros here because we need the full init function
263 * signature with the additional class argument, so we use the right template
264 * for the sink caps */
266 gst_matroska_mux_get_type (void)
268 static GType object_type; /* 0 */
270 if (object_type == 0) {
271 static const GTypeInfo object_info = {
272 sizeof (GstMatroskaMuxClass),
273 NULL, /* base_init */
274 NULL, /* base_finalize */
275 (GClassInitFunc) gst_matroska_mux_class_init,
276 NULL, /* class_finalize */
277 NULL, /* class_data */
278 sizeof (GstMatroskaMux),
280 (GInstanceInitFunc) gst_matroska_mux_init
282 const GInterfaceInfo iface_info = { NULL };
284 object_type = g_type_register_static (GST_TYPE_ELEMENT,
285 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
287 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
288 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
295 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
297 GObjectClass *gobject_class;
298 GstElementClass *gstelement_class;
300 gobject_class = (GObjectClass *) klass;
301 gstelement_class = (GstElementClass *) klass;
303 gst_element_class_add_static_pad_template (gstelement_class,
305 gst_element_class_add_static_pad_template (gstelement_class,
307 gst_element_class_add_static_pad_template (gstelement_class,
308 &subtitlesink_templ);
309 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
310 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
312 "Muxes video/audio/subtitle streams into a matroska stream",
313 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
315 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
318 gobject_class->finalize = gst_matroska_mux_finalize;
320 gobject_class->get_property = gst_matroska_mux_get_property;
321 gobject_class->set_property = gst_matroska_mux_set_property;
323 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
324 g_param_spec_string ("writing-app", "Writing application.",
325 "The name the application that creates the matroska file.",
326 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
327 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
328 g_param_spec_int ("version", "DocType version",
329 "This parameter determines what Matroska features can be used.",
330 1, 2, DEFAULT_DOCTYPE_VERSION,
331 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
332 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
333 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
334 "entries", "An index entry is created every so many nanoseconds.",
335 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
336 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
337 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
338 g_param_spec_boolean ("streamable", "Determines whether output should "
339 "be streamable", "If set to true, the output should be as if it is "
340 "to be streamed and hence no indexes written or duration written.",
341 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
343 g_param_spec_int64 ("timecodescale", "Timecode Scale",
344 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
345 GST_SECOND, DEFAULT_TIMECODESCALE,
346 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
348 gstelement_class->change_state =
349 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
350 gstelement_class->request_new_pad =
351 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
352 gstelement_class->release_pad =
353 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
355 parent_class = g_type_class_peek_parent (klass);
359 * Start of pad option handler code
361 #define DEFAULT_PAD_FRAME_DURATION TRUE
366 PROP_PAD_FRAME_DURATION
372 gboolean frame_duration;
373 gboolean frame_duration_user;
376 typedef GstPadClass GstMatroskamuxPadClass;
378 GType gst_matroskamux_pad_get_type (void);
379 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
381 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
382 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
383 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
384 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
387 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
388 GValue * value, GParamSpec * pspec)
390 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
393 case PROP_PAD_FRAME_DURATION:
394 g_value_set_boolean (value, pad->frame_duration);
397 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
403 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
404 const GValue * value, GParamSpec * pspec)
406 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
409 case PROP_PAD_FRAME_DURATION:
410 pad->frame_duration = g_value_get_boolean (value);
411 pad->frame_duration_user = TRUE;
414 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
420 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
422 GObjectClass *gobject_class = (GObjectClass *) klass;
424 gobject_class->set_property = gst_matroskamux_pad_set_property;
425 gobject_class->get_property = gst_matroskamux_pad_get_property;
427 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
428 g_param_spec_boolean ("frame-duration", "Frame duration",
429 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
430 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
434 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
436 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
437 pad->frame_duration_user = FALSE;
441 * End of pad option handler code
445 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
447 GstPadTemplate *templ;
450 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
451 mux->srcpad = gst_pad_new_from_template (templ, "src");
453 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
454 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
455 gst_pad_use_fixed_caps (mux->srcpad);
457 mux->collect = gst_collect_pads_new ();
458 gst_collect_pads_set_clip_function (mux->collect,
459 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
460 gst_collect_pads_set_buffer_function (mux->collect,
461 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
462 gst_collect_pads_set_event_function (mux->collect,
463 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
465 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
466 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
468 /* property defaults */
469 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
470 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
471 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
472 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
473 mux->time_scale = DEFAULT_TIMECODESCALE;
475 /* initialize internal variables */
477 mux->num_streams = 0;
478 mux->num_a_streams = 0;
479 mux->num_t_streams = 0;
480 mux->num_v_streams = 0;
482 /* create used uid list */
483 mux->used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
485 /* initialize remaining variables */
486 gst_matroska_mux_reset (GST_ELEMENT (mux));
491 * gst_matroska_mux_finalize:
492 * @object: #GstMatroskaMux that should be finalized.
494 * Finalize matroska muxer.
497 gst_matroska_mux_finalize (GObject * object)
499 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
501 gst_event_replace (&mux->force_key_unit_event, NULL);
503 gst_object_unref (mux->collect);
504 gst_object_unref (mux->ebml_write);
505 g_free (mux->writing_app);
507 g_array_free (mux->used_uids, TRUE);
509 G_OBJECT_CLASS (parent_class)->finalize (object);
514 * gst_matroska_mux_create_uid:
515 * @mux: #GstMatroskaMux to generate UID for.
517 * Generate new unused track UID.
519 * Returns: New track UID.
522 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
529 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
530 for (i = 0; i < mux->used_uids->len; i++) {
531 if (g_array_index (mux->used_uids, guint64, i) == uid) {
536 g_array_append_val (mux->used_uids, uid);
544 * gst_matroska_pad_reset:
545 * @collect_pad: the #GstMatroskaPad
547 * Reset and/or release resources of a matroska collect pad.
550 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
553 GstMatroskaTrackType type = 0;
555 /* free track information */
556 if (collect_pad->track != NULL) {
557 /* retrieve for optional later use */
558 name = collect_pad->track->name;
559 type = collect_pad->track->type;
560 /* extra for video */
561 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
562 GstMatroskaTrackVideoContext *ctx =
563 (GstMatroskaTrackVideoContext *) collect_pad->track;
565 if (ctx->dirac_unit) {
566 gst_buffer_unref (ctx->dirac_unit);
567 ctx->dirac_unit = NULL;
570 g_free (collect_pad->track->codec_id);
571 g_free (collect_pad->track->codec_name);
573 g_free (collect_pad->track->name);
574 g_free (collect_pad->track->language);
575 g_free (collect_pad->track->codec_priv);
576 g_free (collect_pad->track);
577 collect_pad->track = NULL;
578 if (collect_pad->tags) {
579 gst_tag_list_unref (collect_pad->tags);
580 collect_pad->tags = NULL;
584 if (!full && type != 0) {
585 GstMatroskaTrackContext *context;
587 /* create a fresh context */
589 case GST_MATROSKA_TRACK_TYPE_VIDEO:
590 context = (GstMatroskaTrackContext *)
591 g_new0 (GstMatroskaTrackVideoContext, 1);
593 case GST_MATROSKA_TRACK_TYPE_AUDIO:
594 context = (GstMatroskaTrackContext *)
595 g_new0 (GstMatroskaTrackAudioContext, 1);
597 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
598 context = (GstMatroskaTrackContext *)
599 g_new0 (GstMatroskaTrackSubtitleContext, 1);
602 g_assert_not_reached ();
606 context->type = type;
607 context->name = name;
608 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
609 /* TODO: check default values for the context */
610 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
611 collect_pad->track = context;
612 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
613 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
614 collect_pad->tags = gst_tag_list_new_empty ();
615 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
620 * gst_matroska_pad_free:
621 * @collect_pad: the #GstMatroskaPad
623 * Release resources of a matroska collect pad.
626 gst_matroska_pad_free (GstPad * collect_pad)
628 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
633 * gst_matroska_mux_reset:
634 * @element: #GstMatroskaMux that should be reseted.
636 * Reset matroska muxer back to initial state.
639 gst_matroska_mux_reset (GstElement * element)
641 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
644 /* reset EBML write */
645 gst_ebml_write_reset (mux->ebml_write);
648 mux->state = GST_MATROSKA_MUX_STATE_START;
650 /* clean up existing streams */
652 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
653 GstMatroskaPad *collect_pad;
655 collect_pad = (GstMatroskaPad *) walk->data;
657 /* reset collect pad to pristine state */
658 gst_matroska_pad_reset (collect_pad, FALSE);
662 mux->num_indexes = 0;
667 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
672 mux->cluster_time = 0;
673 mux->cluster_pos = 0;
674 mux->prev_cluster_size = 0;
677 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
682 gst_toc_setter_reset (GST_TOC_SETTER (mux));
684 mux->chapters_pos = 0;
686 /* clear used uids */
687 if (mux->used_uids->len > 0) {
688 g_array_remove_range (mux->used_uids, 0, mux->used_uids->len);
693 * gst_matroska_mux_handle_src_event:
694 * @pad: Pad which received the event.
695 * @event: Received event.
697 * handle events - copied from oggmux without understanding
699 * Returns: #TRUE on success.
702 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
707 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
711 /* disable seeking for now */
717 return gst_pad_event_default (pad, parent, event);
722 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
724 if (context->codec_priv != NULL) {
725 g_free (context->codec_priv);
726 context->codec_priv = NULL;
727 context->codec_priv_size = 0;
732 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
742 /* produce comma-separated list in hex format */
743 for (i = 0; i < 16; ++i) {
745 /* replicate vobsub's slightly off RGB conversion calculation */
746 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
747 u = ((col >> 8) & 0xff) - 128;
748 v = (col & 0xff) - 128;
749 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
750 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
751 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
752 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
755 sclut = g_strjoinv (",", clutv);
757 /* build codec private; only palette for now */
758 gst_matroska_mux_free_codec_priv (context);
759 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
760 /* include terminating 0 */
761 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
763 for (i = 0; i < 16; ++i) {
770 * gst_matroska_mux_handle_sink_event:
771 * @pad: Pad which received the event.
772 * @event: Received event.
774 * handle events - informational ones like tags
776 * Returns: #TRUE on success.
779 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
780 GstCollectData * data, GstEvent * event, gpointer user_data)
782 GstMatroskaPad *collect_pad;
783 GstMatroskaTrackContext *context;
789 mux = GST_MATROSKA_MUX (user_data);
790 collect_pad = (GstMatroskaPad *) data;
792 context = collect_pad->track;
795 switch (GST_EVENT_TYPE (event)) {
796 case GST_EVENT_CAPS:{
799 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
800 gst_event_parse_caps (event, &caps);
802 ret = collect_pad->capsfunc (pad, caps);
803 gst_event_unref (event);
810 GST_DEBUG_OBJECT (mux, "received tag event");
811 gst_event_parse_tag (event, &list);
813 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
814 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
815 const gchar *lang_code;
817 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
819 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
820 g_free (context->language);
821 context->language = g_strdup (lang_code);
823 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
828 /* FIXME: what about stream-specific tags? */
829 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
830 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
831 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
833 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
836 gst_event_unref (event);
837 /* handled this, don't want collectpads to forward it downstream */
843 GstToc *toc, *old_toc;
845 if (mux->chapters_pos > 0)
848 GST_DEBUG_OBJECT (mux, "received toc event");
849 gst_event_parse_toc (event, &toc, NULL);
852 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
853 if (old_toc != NULL) {
855 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
856 gst_toc_unref (old_toc);
859 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
863 gst_event_unref (event);
864 /* handled this, don't want collectpads to forward it downstream */
868 case GST_EVENT_CUSTOM_DOWNSTREAM:
869 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
870 const GstStructure *structure;
872 structure = gst_event_get_structure (event);
873 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
874 gst_event_replace (&mux->force_key_unit_event, NULL);
875 mux->force_key_unit_event = event;
877 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
878 !strcmp ("dvd-spu-clut-change",
879 gst_structure_get_string (structure, "event"))) {
884 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
885 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
886 GST_DEBUG_OBJECT (pad, "... discarding");
889 /* first transform event data into table form */
890 for (i = 0; i < 16; i++) {
891 g_snprintf (name, sizeof (name), "clut%02d", i);
892 if (!gst_structure_get_int (structure, name, &value)) {
893 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
894 "contain %s field", name);
900 /* transform into private data for stream; text form */
901 gst_matroska_mux_build_vobsub_private (context, clut);
911 return gst_collect_pads_event_default (pads, data, event, FALSE);
917 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
920 g_assert (context && id);
921 g_free (context->codec_id);
922 context->codec_id = g_strdup (id);
926 * gst_matroska_mux_video_pad_setcaps:
927 * @pad: Pad which got the caps.
930 * Setcaps function for video sink pad.
932 * Returns: #TRUE on success.
935 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
937 GstMatroskaTrackContext *context = NULL;
938 GstMatroskaTrackVideoContext *videocontext;
940 GstMatroskaPad *collect_pad;
941 GstStructure *structure;
942 const gchar *mimetype;
943 const gchar *interlace_mode, *s;
944 const GValue *value = NULL;
945 GstBuffer *codec_buf = NULL;
946 gint width, height, pixel_width, pixel_height;
948 guint multiview_flags;
950 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
953 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
954 g_assert (collect_pad);
955 context = collect_pad->track;
957 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
958 videocontext = (GstMatroskaTrackVideoContext *) context;
960 /* gst -> matroska ID'ing */
961 structure = gst_caps_get_structure (caps, 0);
963 mimetype = gst_structure_get_name (structure);
965 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
966 if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
967 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
969 if (!strcmp (mimetype, "video/x-theora")) {
970 /* we'll extract the details later from the theora identification header */
974 /* get general properties */
975 /* spec says it is mandatory */
976 if (!gst_structure_get_int (structure, "width", &width) ||
977 !gst_structure_get_int (structure, "height", &height))
980 videocontext->pixel_width = width;
981 videocontext->pixel_height = height;
983 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
984 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
986 context->default_duration =
987 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
988 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
989 GST_TIME_ARGS (context->default_duration));
991 context->default_duration = 0;
993 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
994 &pixel_width, &pixel_height)) {
995 if (pixel_width > pixel_height) {
996 videocontext->display_width = width * pixel_width / pixel_height;
997 videocontext->display_height = height;
998 } else if (pixel_width < pixel_height) {
999 videocontext->display_width = width;
1000 videocontext->display_height = height * pixel_height / pixel_width;
1002 videocontext->display_width = 0;
1003 videocontext->display_height = 0;
1006 videocontext->display_width = 0;
1007 videocontext->display_height = 0;
1010 /* Collect stereoscopic info, if any */
1011 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1012 videocontext->multiview_mode =
1013 gst_video_multiview_mode_from_caps_string (s);
1014 gst_structure_get_flagset (structure, "multiview-flags", &multiview_flags,
1016 videocontext->multiview_flags = multiview_flags;
1021 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1022 videocontext->fourcc = 0;
1024 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1025 * data and other settings
1029 /* extract codec_data, may turn out needed */
1030 value = gst_structure_get_value (structure, "codec_data");
1032 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1035 if (!strcmp (mimetype, "video/x-raw")) {
1037 gst_matroska_mux_set_codec_id (context,
1038 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1039 fstr = gst_structure_get_string (structure, "format");
1041 if (strlen (fstr) == 4)
1042 videocontext->fourcc = GST_STR_FOURCC (fstr);
1043 else if (!strcmp (fstr, "GRAY8"))
1044 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1045 else if (!strcmp (fstr, "BGR"))
1046 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1047 else if (!strcmp (fstr, "RGB"))
1048 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1050 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1051 ||!strcmp (mimetype, "video/x-divx")
1052 || !strcmp (mimetype, "video/x-dv")
1053 || !strcmp (mimetype, "video/x-h263")
1054 || !strcmp (mimetype, "video/x-msmpeg")
1055 || !strcmp (mimetype, "video/x-wmv")
1056 || !strcmp (mimetype, "image/jpeg")) {
1057 gst_riff_strf_vids *bih;
1058 gint size = sizeof (gst_riff_strf_vids);
1061 if (!strcmp (mimetype, "video/x-huffyuv"))
1062 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1063 else if (!strcmp (mimetype, "video/x-dv"))
1064 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1065 else if (!strcmp (mimetype, "video/x-h263"))
1066 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1067 else if (!strcmp (mimetype, "video/x-divx")) {
1070 gst_structure_get_int (structure, "divxversion", &divxversion);
1071 switch (divxversion) {
1073 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1076 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1079 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1082 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1085 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1086 switch (msmpegversion) {
1088 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1091 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1097 } else if (!strcmp (mimetype, "video/x-wmv")) {
1101 fstr = gst_structure_get_string (structure, "format");
1102 if (fstr && strlen (fstr) == 4) {
1103 fourcc = GST_STR_FOURCC (fstr);
1104 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1105 if (wmvversion == 2) {
1106 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1107 } else if (wmvversion == 1) {
1108 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1109 } else if (wmvversion == 3) {
1110 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1113 } else if (!strcmp (mimetype, "image/jpeg")) {
1114 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1120 bih = g_new0 (gst_riff_strf_vids, 1);
1121 GST_WRITE_UINT32_LE (&bih->size, size);
1122 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1123 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1124 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1125 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1126 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1127 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1128 videocontext->pixel_height * 3);
1130 /* process codec private/initialization data, if any */
1132 size += gst_buffer_get_size (codec_buf);
1133 bih = g_realloc (bih, size);
1134 GST_WRITE_UINT32_LE (&bih->size, size);
1135 gst_buffer_extract (codec_buf, 0,
1136 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1139 gst_matroska_mux_set_codec_id (context,
1140 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1141 gst_matroska_mux_free_codec_priv (context);
1142 context->codec_priv = (gpointer) bih;
1143 context->codec_priv_size = size;
1144 context->dts_only = TRUE;
1145 } else if (!strcmp (mimetype, "video/x-h264")) {
1146 gst_matroska_mux_set_codec_id (context,
1147 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1148 gst_matroska_mux_free_codec_priv (context);
1149 /* Create avcC header */
1150 if (codec_buf != NULL) {
1151 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1152 context->codec_priv = g_malloc0 (context->codec_priv_size);
1153 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1155 } else if (!strcmp (mimetype, "video/x-h265")) {
1156 gst_matroska_mux_set_codec_id (context,
1157 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1158 gst_matroska_mux_free_codec_priv (context);
1159 /* Create hvcC header */
1160 if (codec_buf != NULL) {
1161 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1162 context->codec_priv = g_malloc0 (context->codec_priv_size);
1163 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1165 } else if (!strcmp (mimetype, "video/x-theora")) {
1166 const GValue *streamheader;
1168 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1170 gst_matroska_mux_free_codec_priv (context);
1172 streamheader = gst_structure_get_value (structure, "streamheader");
1173 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1174 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1175 ("theora stream headers missing or malformed"));
1178 } else if (!strcmp (mimetype, "video/x-dirac")) {
1179 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1180 } else if (!strcmp (mimetype, "video/x-vp8")) {
1181 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1182 } else if (!strcmp (mimetype, "video/x-vp9")) {
1183 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1184 } else if (!strcmp (mimetype, "video/mpeg")) {
1187 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1188 switch (mpegversion) {
1190 gst_matroska_mux_set_codec_id (context,
1191 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1194 gst_matroska_mux_set_codec_id (context,
1195 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1198 gst_matroska_mux_set_codec_id (context,
1199 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1205 /* global headers may be in codec data */
1206 if (codec_buf != NULL) {
1207 gst_matroska_mux_free_codec_priv (context);
1208 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1209 context->codec_priv = g_malloc0 (context->codec_priv_size);
1210 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1212 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1214 /* can only make it here if preceding case verified it was version 3 */
1215 gst_matroska_mux_set_codec_id (context,
1216 GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1217 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1219 const GValue *mdpr_data;
1221 gst_structure_get_int (structure, "rmversion", &rmversion);
1222 switch (rmversion) {
1224 gst_matroska_mux_set_codec_id (context,
1225 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1228 gst_matroska_mux_set_codec_id (context,
1229 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1232 gst_matroska_mux_set_codec_id (context,
1233 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1236 gst_matroska_mux_set_codec_id (context,
1237 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1243 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1244 if (mdpr_data != NULL) {
1245 guint8 *priv_data = NULL;
1246 guint priv_data_size = 0;
1248 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1250 priv_data_size = gst_buffer_get_size (codec_data_buf);
1251 priv_data = g_malloc0 (priv_data_size);
1253 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1255 gst_matroska_mux_free_codec_priv (context);
1256 context->codec_priv = priv_data;
1257 context->codec_priv_size = priv_data_size;
1259 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1260 const gchar *variant;
1262 gst_matroska_mux_free_codec_priv (context);
1264 variant = gst_structure_get_string (structure, "format");
1265 if (!variant || !g_strcmp0 (variant, "standard"))
1266 context->codec_priv = g_strdup ("apcn");
1267 else if (!g_strcmp0 (variant, "hq"))
1268 context->codec_priv = g_strdup ("apch");
1269 else if (!g_strcmp0 (variant, "lt"))
1270 context->codec_priv = g_strdup ("apcs");
1271 else if (!g_strcmp0 (variant, "proxy"))
1272 context->codec_priv = g_strdup ("apco");
1273 else if (!g_strcmp0 (variant, "4444"))
1274 context->codec_priv = g_strdup ("ap4h");
1276 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1281 context->codec_priv_size = sizeof (guint32);
1282 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1290 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1291 GST_PAD_NAME (pad), caps);
1296 /* N > 0 to expect a particular number of headers, negative if the
1297 number of headers is variable */
1299 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1300 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1302 GstBuffer **buf = NULL;
1305 guint bufi, i, offset, priv_data_size;
1307 if (streamheader == NULL)
1308 goto no_stream_headers;
1310 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1313 bufarr = g_value_peek_pointer (streamheader);
1314 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1316 if (N > 0 && bufarr->len != N)
1319 context->xiph_headers_to_skip = bufarr->len;
1321 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1322 for (i = 0; i < bufarr->len; i++) {
1323 GValue *bufval = &g_array_index (bufarr, GValue, i);
1325 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1327 goto wrong_content_type;
1330 buf[i] = g_value_peek_pointer (bufval);
1334 if (bufarr->len > 0) {
1335 for (i = 0; i < bufarr->len - 1; i++) {
1336 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1340 for (i = 0; i < bufarr->len; ++i) {
1341 priv_data_size += gst_buffer_get_size (buf[i]);
1344 priv_data = g_malloc0 (priv_data_size);
1346 priv_data[0] = bufarr->len - 1;
1349 if (bufarr->len > 0) {
1350 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1351 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1352 priv_data[offset++] = 0xff;
1354 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1358 for (i = 0; i < bufarr->len; ++i) {
1359 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1360 offset += gst_buffer_get_size (buf[i]);
1363 gst_matroska_mux_free_codec_priv (context);
1364 context->codec_priv = priv_data;
1365 context->codec_priv_size = priv_data_size;
1368 *p_buf0 = gst_buffer_ref (buf[0]);
1377 GST_WARNING ("required streamheaders missing in sink caps!");
1382 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1383 G_VALUE_TYPE_NAME (streamheader));
1388 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1393 GST_WARNING ("streamheaders array does not contain GstBuffers");
1399 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1400 GstMatroskaTrackContext * context)
1402 GstBuffer *buf0 = NULL;
1404 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1407 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1408 GST_WARNING ("First vorbis header too small, ignoring");
1410 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1411 GstMatroskaTrackAudioContext *audiocontext;
1415 gst_buffer_map (buf0, &map, GST_MAP_READ);
1416 hdr = map.data + 1 + 6 + 4;
1417 audiocontext = (GstMatroskaTrackAudioContext *) context;
1418 audiocontext->channels = GST_READ_UINT8 (hdr);
1419 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1420 gst_buffer_unmap (buf0, &map);
1425 gst_buffer_unref (buf0);
1431 theora_streamheader_to_codecdata (const GValue * streamheader,
1432 GstMatroskaTrackContext * context)
1434 GstBuffer *buf0 = NULL;
1436 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1439 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1440 GST_WARNING ("First theora header too small, ignoring");
1441 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1442 GST_WARNING ("First header not a theora identification header, ignoring");
1444 GstMatroskaTrackVideoContext *videocontext;
1445 guint fps_num, fps_denom, par_num, par_denom;
1449 gst_buffer_map (buf0, &map, GST_MAP_READ);
1450 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1452 videocontext = (GstMatroskaTrackVideoContext *) context;
1453 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1454 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1455 hdr += 3 + 3 + 1 + 1;
1456 fps_num = GST_READ_UINT32_BE (hdr);
1457 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1458 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1459 fps_denom, fps_num);
1461 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1462 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1463 if (par_num > 0 && par_denom > 0) {
1464 if (par_num > par_denom) {
1465 videocontext->display_width =
1466 videocontext->pixel_width * par_num / par_denom;
1467 videocontext->display_height = videocontext->pixel_height;
1468 } else if (par_num < par_denom) {
1469 videocontext->display_width = videocontext->pixel_width;
1470 videocontext->display_height =
1471 videocontext->pixel_height * par_denom / par_num;
1473 videocontext->display_width = 0;
1474 videocontext->display_height = 0;
1477 videocontext->display_width = 0;
1478 videocontext->display_height = 0;
1481 gst_buffer_unmap (buf0, &map);
1485 gst_buffer_unref (buf0);
1491 kate_streamheader_to_codecdata (const GValue * streamheader,
1492 GstMatroskaTrackContext * context)
1494 GstBuffer *buf0 = NULL;
1496 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1499 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1500 GST_WARNING ("First kate header too small, ignoring");
1501 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1502 GST_WARNING ("First header not a kate identification header, ignoring");
1506 gst_buffer_unref (buf0);
1512 flac_streamheader_to_codecdata (const GValue * streamheader,
1513 GstMatroskaTrackContext * context)
1520 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1521 GST_WARNING ("No or invalid streamheader field in the caps");
1525 bufarr = g_value_peek_pointer (streamheader);
1526 if (bufarr->len < 2) {
1527 GST_WARNING ("Too few headers in streamheader field");
1531 context->xiph_headers_to_skip = bufarr->len + 1;
1533 bufval = &g_array_index (bufarr, GValue, 0);
1534 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1535 GST_WARNING ("streamheaders array does not contain GstBuffers");
1539 buffer = g_value_peek_pointer (bufval);
1541 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1542 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1543 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1544 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1545 GST_WARNING ("Invalid streamheader for FLAC");
1549 gst_matroska_mux_free_codec_priv (context);
1550 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1551 context->codec_priv = g_malloc (context->codec_priv_size);
1552 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1554 for (i = 1; i < bufarr->len; i++) {
1556 bufval = &g_array_index (bufarr, GValue, i);
1558 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1559 gst_matroska_mux_free_codec_priv (context);
1560 GST_WARNING ("streamheaders array does not contain GstBuffers");
1564 buffer = g_value_peek_pointer (bufval);
1566 old_size = context->codec_priv_size;
1567 context->codec_priv_size += gst_buffer_get_size (buffer);
1569 context->codec_priv = g_realloc (context->codec_priv,
1570 context->codec_priv_size);
1571 gst_buffer_extract (buffer, 0,
1572 (guint8 *) context->codec_priv + old_size, -1);
1579 speex_streamheader_to_codecdata (const GValue * streamheader,
1580 GstMatroskaTrackContext * context)
1587 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1588 GST_WARNING ("No or invalid streamheader field in the caps");
1592 bufarr = g_value_peek_pointer (streamheader);
1593 if (bufarr->len != 2) {
1594 GST_WARNING ("Too few headers in streamheader field");
1598 context->xiph_headers_to_skip = bufarr->len + 1;
1600 bufval = &g_array_index (bufarr, GValue, 0);
1601 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1602 GST_WARNING ("streamheaders array does not contain GstBuffers");
1606 buffer = g_value_peek_pointer (bufval);
1608 if (gst_buffer_get_size (buffer) < 80
1609 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1610 GST_WARNING ("Invalid streamheader for Speex");
1614 gst_matroska_mux_free_codec_priv (context);
1615 context->codec_priv_size = gst_buffer_get_size (buffer);
1616 context->codec_priv = g_malloc (context->codec_priv_size);
1617 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1619 bufval = &g_array_index (bufarr, GValue, 1);
1621 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1622 gst_matroska_mux_free_codec_priv (context);
1623 GST_WARNING ("streamheaders array does not contain GstBuffers");
1627 buffer = g_value_peek_pointer (bufval);
1629 old_size = context->codec_priv_size;
1630 context->codec_priv_size += gst_buffer_get_size (buffer);
1631 context->codec_priv = g_realloc (context->codec_priv,
1632 context->codec_priv_size);
1633 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1639 opus_streamheader_to_codecdata (const GValue * streamheader,
1640 GstMatroskaTrackContext * context)
1646 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1649 bufarr = g_value_peek_pointer (streamheader);
1650 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1653 /* Opus headers are not in-band */
1654 context->xiph_headers_to_skip = 0;
1656 bufval = &g_array_index (bufarr, GValue, 0);
1657 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1658 goto wrong_content_type;
1660 buf = g_value_peek_pointer (bufval);
1662 gst_matroska_mux_free_codec_priv (context);
1664 context->codec_priv_size = gst_buffer_get_size (buf);
1665 context->codec_priv = g_malloc0 (context->codec_priv_size);
1666 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1668 context->codec_delay =
1669 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1670 context->codec_delay =
1671 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1672 context->seek_preroll = 80 * GST_MSECOND;
1679 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1680 G_VALUE_TYPE_NAME (streamheader));
1685 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1690 GST_WARNING ("streamheaders array does not contain GstBuffers");
1696 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1700 guint8 channel_mapping_family;
1701 guint8 stream_count, coupled_count, channel_mapping[256];
1705 /* Opus headers are not in-band */
1706 context->xiph_headers_to_skip = 0;
1708 context->codec_delay = 0;
1709 context->seek_preroll = 80 * GST_MSECOND;
1711 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1712 &channel_mapping_family, &stream_count, &coupled_count,
1714 GST_WARNING ("Failed to parse caps for Opus");
1719 gst_codec_utils_opus_create_header (rate, channels,
1720 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1723 GST_WARNING ("Failed to create Opus header from caps");
1727 gst_buffer_map (buffer, &map, GST_MAP_READ);
1728 context->codec_priv_size = map.size;
1729 context->codec_priv = g_malloc (context->codec_priv_size);
1730 memcpy (context->codec_priv, map.data, map.size);
1731 gst_buffer_unmap (buffer, &map);
1732 gst_buffer_unref (buffer);
1738 * gst_matroska_mux_audio_pad_setcaps:
1739 * @pad: Pad which got the caps.
1742 * Setcaps function for audio sink pad.
1744 * Returns: #TRUE on success.
1747 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1749 GstMatroskaTrackContext *context = NULL;
1750 GstMatroskaTrackAudioContext *audiocontext;
1751 GstMatroskaMux *mux;
1752 GstMatroskaPad *collect_pad;
1753 const gchar *mimetype;
1754 gint samplerate = 0, channels = 0;
1755 GstStructure *structure;
1756 const GValue *codec_data = NULL;
1757 GstBuffer *buf = NULL;
1758 const gchar *stream_format = NULL;
1760 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1763 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1764 g_assert (collect_pad);
1765 context = collect_pad->track;
1767 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1768 audiocontext = (GstMatroskaTrackAudioContext *) context;
1770 structure = gst_caps_get_structure (caps, 0);
1771 mimetype = gst_structure_get_name (structure);
1774 gst_structure_get_int (structure, "rate", &samplerate);
1775 gst_structure_get_int (structure, "channels", &channels);
1777 audiocontext->samplerate = samplerate;
1778 audiocontext->channels = channels;
1779 audiocontext->bitdepth = 0;
1780 context->default_duration = 0;
1782 codec_data = gst_structure_get_value (structure, "codec_data");
1784 buf = gst_value_get_buffer (codec_data);
1786 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1787 * data and other settings
1791 if (!strcmp (mimetype, "audio/mpeg")) {
1792 gint mpegversion = 0;
1794 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1795 switch (mpegversion) {
1801 gst_structure_get_int (structure, "layer", &layer);
1803 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1804 GST_WARNING_OBJECT (mux,
1805 "Unable to determine MPEG audio version, assuming 1");
1811 else if (layer == 2)
1813 else if (version == 2)
1818 context->default_duration =
1819 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1823 gst_matroska_mux_set_codec_id (context,
1824 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1827 gst_matroska_mux_set_codec_id (context,
1828 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1831 gst_matroska_mux_set_codec_id (context,
1832 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1841 stream_format = gst_structure_get_string (structure, "stream-format");
1842 /* check this is raw aac */
1843 if (stream_format) {
1844 if (strcmp (stream_format, "raw") != 0) {
1845 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1849 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1854 gst_matroska_mux_set_codec_id (context,
1855 GST_MATROSKA_CODEC_ID_AUDIO_AAC);
1856 context->codec_priv_size = gst_buffer_get_size (buf);
1857 context->codec_priv = g_malloc (context->codec_priv_size);
1858 gst_buffer_extract (buf, 0, context->codec_priv,
1859 context->codec_priv_size);
1861 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1868 } else if (!strcmp (mimetype, "audio/x-raw")) {
1871 gst_audio_info_init (&info);
1872 if (!gst_audio_info_from_caps (&info, caps)) {
1873 GST_DEBUG_OBJECT (mux,
1874 "broken caps, rejected by gst_audio_info_from_caps");
1878 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1879 case GST_AUDIO_FORMAT_U8:
1880 case GST_AUDIO_FORMAT_S16BE:
1881 case GST_AUDIO_FORMAT_S16LE:
1882 case GST_AUDIO_FORMAT_S24BE:
1883 case GST_AUDIO_FORMAT_S24LE:
1884 case GST_AUDIO_FORMAT_S32BE:
1885 case GST_AUDIO_FORMAT_S32LE:
1886 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1887 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1890 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1891 gst_matroska_mux_set_codec_id (context,
1892 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1894 gst_matroska_mux_set_codec_id (context,
1895 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1897 case GST_AUDIO_FORMAT_F32LE:
1898 case GST_AUDIO_FORMAT_F64LE:
1899 gst_matroska_mux_set_codec_id (context,
1900 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1904 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1908 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1909 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1910 const GValue *streamheader;
1912 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1914 gst_matroska_mux_free_codec_priv (context);
1916 streamheader = gst_structure_get_value (structure, "streamheader");
1917 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1918 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1919 ("vorbis stream headers missing or malformed"));
1922 } else if (!strcmp (mimetype, "audio/x-flac")) {
1923 const GValue *streamheader;
1925 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1927 gst_matroska_mux_free_codec_priv (context);
1929 streamheader = gst_structure_get_value (structure, "streamheader");
1930 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1931 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1932 ("flac stream headers missing or malformed"));
1935 } else if (!strcmp (mimetype, "audio/x-speex")) {
1936 const GValue *streamheader;
1938 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1939 gst_matroska_mux_free_codec_priv (context);
1941 streamheader = gst_structure_get_value (structure, "streamheader");
1942 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1943 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1944 ("speex stream headers missing or malformed"));
1947 } else if (!strcmp (mimetype, "audio/x-opus")) {
1948 const GValue *streamheader;
1950 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
1952 streamheader = gst_structure_get_value (structure, "streamheader");
1954 gst_matroska_mux_free_codec_priv (context);
1955 if (!opus_streamheader_to_codecdata (streamheader, context)) {
1956 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1957 ("opus stream headers missing or malformed"));
1961 /* no streamheader, but we need to have one, so we make one up
1963 gst_matroska_mux_free_codec_priv (context);
1964 if (!opus_make_codecdata (context, caps)) {
1965 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1966 ("opus stream headers missing or malformed"));
1970 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1971 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1972 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1973 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1974 } else if (!strcmp (mimetype, "audio/x-dts")) {
1975 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1976 } else if (!strcmp (mimetype, "audio/x-tta")) {
1979 /* TTA frame duration */
1980 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1982 gst_structure_get_int (structure, "width", &width);
1983 audiocontext->bitdepth = width;
1984 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1986 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1988 const GValue *mdpr_data;
1990 gst_structure_get_int (structure, "raversion", &raversion);
1991 switch (raversion) {
1993 gst_matroska_mux_set_codec_id (context,
1994 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1997 gst_matroska_mux_set_codec_id (context,
1998 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2001 gst_matroska_mux_set_codec_id (context,
2002 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2008 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2009 if (mdpr_data != NULL) {
2010 guint8 *priv_data = NULL;
2011 guint priv_data_size = 0;
2013 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2015 priv_data_size = gst_buffer_get_size (codec_data_buf);
2016 priv_data = g_malloc0 (priv_data_size);
2018 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2020 gst_matroska_mux_free_codec_priv (context);
2022 context->codec_priv = priv_data;
2023 context->codec_priv_size = priv_data_size;
2026 } else if (!strcmp (mimetype, "audio/x-wma")
2027 || !strcmp (mimetype, "audio/x-alaw")
2028 || !strcmp (mimetype, "audio/x-mulaw")
2029 || !strcmp (mimetype, "audio/x-adpcm")
2030 || !strcmp (mimetype, "audio/G722")) {
2032 guint codec_priv_size;
2034 gint block_align = 0;
2037 if (samplerate == 0 || channels == 0) {
2038 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2042 if (!strcmp (mimetype, "audio/x-wma")) {
2046 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2047 || !gst_structure_get_int (structure, "block_align", &block_align)
2048 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2049 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2054 switch (wmaversion) {
2056 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2059 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2062 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2065 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2069 if (gst_structure_get_int (structure, "depth", &depth))
2070 audiocontext->bitdepth = depth;
2071 } else if (!strcmp (mimetype, "audio/x-alaw")
2072 || !strcmp (mimetype, "audio/x-mulaw")) {
2073 audiocontext->bitdepth = 8;
2074 if (!strcmp (mimetype, "audio/x-alaw"))
2075 format = GST_RIFF_WAVE_FORMAT_ALAW;
2077 format = GST_RIFF_WAVE_FORMAT_MULAW;
2079 block_align = channels;
2080 bitrate = block_align * samplerate;
2081 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2084 layout = gst_structure_get_string (structure, "layout");
2086 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2090 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2091 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2095 if (!strcmp (layout, "dvi")) {
2096 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2097 } else if (!strcmp (layout, "g726")) {
2098 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2099 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2100 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2104 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2108 } else if (!strcmp (mimetype, "audio/G722")) {
2109 format = GST_RIFF_WAVE_FORMAT_ADPCM_G722;
2111 g_assert (format != 0);
2113 codec_priv_size = WAVEFORMATEX_SIZE;
2115 codec_priv_size += gst_buffer_get_size (buf);
2117 /* serialize waveformatex structure */
2118 codec_priv = g_malloc0 (codec_priv_size);
2119 GST_WRITE_UINT16_LE (codec_priv, format);
2120 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2121 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2122 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2123 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2124 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2126 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2128 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2130 /* process codec private/initialization data, if any */
2132 gst_buffer_extract (buf, 0,
2133 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2136 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2137 gst_matroska_mux_free_codec_priv (context);
2138 context->codec_priv = (gpointer) codec_priv;
2139 context->codec_priv_size = codec_priv_size;
2147 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2148 GST_PAD_NAME (pad), caps);
2153 /* we probably don't have the data at start,
2154 * so have to reserve (a maximum) space to write this at the end.
2155 * bit spacy, but some formats can hold quite some */
2156 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2159 * gst_matroska_mux_subtitle_pad_setcaps:
2160 * @pad: Pad which got the caps.
2163 * Setcaps function for subtitle sink pad.
2165 * Returns: #TRUE on success.
2168 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2170 /* There is now (at least) one such alement (kateenc), and I'm going
2171 to handle it here and claim it works when it can be piped back
2172 through GStreamer and VLC */
2174 GstMatroskaTrackContext *context = NULL;
2175 GstMatroskaTrackSubtitleContext *scontext;
2176 GstMatroskaMux *mux;
2177 GstMatroskaPad *collect_pad;
2178 const gchar *mimetype;
2179 GstStructure *structure;
2180 const GValue *value = NULL;
2181 GstBuffer *buf = NULL;
2182 gboolean ret = TRUE;
2184 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2187 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2188 g_assert (collect_pad);
2189 context = collect_pad->track;
2191 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2192 scontext = (GstMatroskaTrackSubtitleContext *) context;
2194 structure = gst_caps_get_structure (caps, 0);
2195 mimetype = gst_structure_get_name (structure);
2198 scontext->check_utf8 = 1;
2199 scontext->invalid_utf8 = 0;
2200 context->default_duration = 0;
2202 if (!strcmp (mimetype, "subtitle/x-kate")) {
2203 const GValue *streamheader;
2205 gst_matroska_mux_set_codec_id (context,
2206 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2208 gst_matroska_mux_free_codec_priv (context);
2210 streamheader = gst_structure_get_value (structure, "streamheader");
2211 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2212 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2213 ("kate stream headers missing or malformed"));
2217 } else if (!strcmp (mimetype, "text/x-raw")) {
2218 gst_matroska_mux_set_codec_id (context,
2219 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2220 } else if (!strcmp (mimetype, "application/x-ssa")) {
2221 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2222 } else if (!strcmp (mimetype, "application/x-ass")) {
2223 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2224 } else if (!strcmp (mimetype, "application/x-usf")) {
2225 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2226 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2227 gst_matroska_mux_set_codec_id (context,
2228 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2234 /* maybe some private data, e.g. vobsub */
2235 value = gst_structure_get_value (structure, "codec_data");
2237 buf = gst_value_get_buffer (value);
2240 guint8 *priv_data = NULL;
2242 gst_buffer_map (buf, &map, GST_MAP_READ);
2244 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2245 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2246 " exceeded maximum (%d); discarding", pad,
2247 SUBTITLE_MAX_CODEC_PRIVATE);
2248 gst_buffer_unmap (buf, &map);
2252 gst_matroska_mux_free_codec_priv (context);
2254 priv_data = g_malloc0 (map.size);
2255 memcpy (priv_data, map.data, map.size);
2256 context->codec_priv = priv_data;
2257 context->codec_priv_size = map.size;
2258 gst_buffer_unmap (buf, &map);
2261 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2262 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2271 * gst_matroska_mux_request_new_pad:
2272 * @element: #GstMatroskaMux.
2273 * @templ: #GstPadTemplate.
2274 * @pad_name: New pad name.
2276 * Request pad function for sink templates.
2278 * Returns: New #GstPad.
2281 gst_matroska_mux_request_new_pad (GstElement * element,
2282 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2284 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2285 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2286 GstMatroskaPad *collect_pad;
2287 GstMatroskamuxPad *newpad;
2289 const gchar *pad_name = NULL;
2290 GstMatroskaCapsFunc capsfunc = NULL;
2291 GstMatroskaTrackContext *context = NULL;
2293 gboolean locked = TRUE;
2294 const gchar *id = NULL;
2296 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2297 /* don't mix named and unnamed pads, if the pad already exists we fail when
2298 * trying to add it */
2299 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2300 pad_name = req_name;
2302 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2305 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2306 context = (GstMatroskaTrackContext *)
2307 g_new0 (GstMatroskaTrackAudioContext, 1);
2308 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2309 context->name = g_strdup ("Audio");
2310 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2311 /* don't mix named and unnamed pads, if the pad already exists we fail when
2312 * trying to add it */
2313 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2314 pad_name = req_name;
2316 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2319 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2320 context = (GstMatroskaTrackContext *)
2321 g_new0 (GstMatroskaTrackVideoContext, 1);
2322 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2323 context->name = g_strdup ("Video");
2324 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2325 /* don't mix named and unnamed pads, if the pad already exists we fail when
2326 * trying to add it */
2327 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2328 pad_name = req_name;
2330 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2333 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2334 context = (GstMatroskaTrackContext *)
2335 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2336 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2337 context->name = g_strdup ("Subtitle");
2338 /* setcaps may only provide proper one a lot later */
2339 id = "S_SUB_UNKNOWN";
2342 GST_WARNING_OBJECT (mux, "This is not our template!");
2346 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2347 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2349 gst_matroskamux_pad_init (newpad);
2350 collect_pad = (GstMatroskaPad *)
2351 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2352 sizeof (GstMatroskamuxPad),
2353 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2355 collect_pad->mux = mux;
2356 collect_pad->track = context;
2357 gst_matroska_pad_reset (collect_pad, FALSE);
2359 gst_matroska_mux_set_codec_id (collect_pad->track, id);
2360 collect_pad->track->dts_only = FALSE;
2362 collect_pad->capsfunc = capsfunc;
2363 gst_pad_set_active (GST_PAD (newpad), TRUE);
2364 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2365 goto pad_add_failed;
2371 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2373 return GST_PAD (newpad);
2378 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2380 gst_object_unref (newpad);
2386 * gst_matroska_mux_release_pad:
2387 * @element: #GstMatroskaMux.
2388 * @pad: Pad to release.
2390 * Release a previously requested pad.
2393 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2395 GstMatroskaMux *mux;
2398 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2400 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2401 GstCollectData *cdata = (GstCollectData *) walk->data;
2402 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2404 if (cdata->pad == pad) {
2406 * observed duration, this will remain GST_CLOCK_TIME_NONE
2407 * only if the pad is resetted
2409 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2411 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2412 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2413 collected_duration =
2414 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2417 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2418 && mux->duration < collected_duration)
2419 mux->duration = collected_duration;
2425 gst_collect_pads_remove_pad (mux->collect, pad);
2426 if (gst_element_remove_pad (element, pad))
2432 * gst_matroska_mux_track_header:
2433 * @mux: #GstMatroskaMux
2434 * @context: Tack context.
2436 * Write a track header.
2439 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2440 GstMatroskaTrackContext * context)
2442 GstEbmlWrite *ebml = mux->ebml_write;
2445 /* TODO: check if everything necessary is written and check default values */
2447 /* track type goes before the type-specific stuff */
2448 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2449 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2451 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2452 if (context->default_duration) {
2453 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2454 context->default_duration);
2456 if (context->language) {
2457 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2461 /* FIXME: until we have a nice way of getting the codecname
2462 * out of the caps, I'm not going to enable this. Too much
2463 * (useless, double, boring) work... */
2464 /* TODO: Use value from tags if any */
2465 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2466 context->codec_name); */
2467 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2469 /* type-specific stuff */
2470 switch (context->type) {
2471 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2472 GstMatroskaTrackVideoContext *videocontext =
2473 (GstMatroskaTrackVideoContext *) context;
2475 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2476 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2477 videocontext->pixel_width);
2478 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2479 videocontext->pixel_height);
2480 if (videocontext->display_width && videocontext->display_height) {
2481 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2482 videocontext->display_width);
2483 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2484 videocontext->display_height);
2486 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2487 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2488 if (videocontext->fourcc) {
2489 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2491 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2492 (gpointer) & fcc_le, 4);
2494 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2495 guint64 stereo_mode = 0;
2497 switch (videocontext->multiview_mode) {
2498 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2500 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2501 if (videocontext->multiview_flags &
2502 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2503 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2505 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2507 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2508 if (videocontext->multiview_flags &
2509 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2510 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2512 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2514 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2515 if (videocontext->multiview_flags &
2516 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2517 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2519 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2521 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2522 if (videocontext->multiview_flags &
2523 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2524 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2526 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2527 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2528 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2529 GST_FIXME_OBJECT (mux,
2530 "Frame-by-frame stereoscopic mode not fully implemented");
2533 GST_WARNING_OBJECT (mux,
2534 "Multiview mode %d not supported in Matroska/WebM",
2535 videocontext->multiview_mode);
2539 if (stereo_mode != 0)
2540 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2543 gst_ebml_write_master_finish (ebml, master);
2548 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2549 GstMatroskaTrackAudioContext *audiocontext =
2550 (GstMatroskaTrackAudioContext *) context;
2552 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2553 if (audiocontext->samplerate != 8000)
2554 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2555 audiocontext->samplerate);
2556 if (audiocontext->channels != 1)
2557 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2558 audiocontext->channels);
2559 if (audiocontext->bitdepth) {
2560 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2561 audiocontext->bitdepth);
2564 gst_ebml_write_master_finish (ebml, master);
2569 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2573 /* doesn't need type-specific data */
2577 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2578 if (context->codec_priv)
2579 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2580 context->codec_priv, context->codec_priv_size);
2582 if (context->seek_preroll) {
2583 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2584 context->seek_preroll);
2587 if (context->codec_delay) {
2588 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2589 context->codec_delay);
2595 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2597 guint64 title_master;
2600 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2602 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2603 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2604 GST_MATROSKA_MUX_CHAPLANG);
2606 gst_ebml_write_master_finish (ebml, title_master);
2610 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2611 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2612 guint64 * master_edition)
2614 guint64 uid, master_chapteratom;
2616 GstTocEntry *cur_entry;
2621 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2623 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2625 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2626 /* create uid for the parent */
2627 uid = gst_matroska_mux_create_uid ();
2628 g_free (edition->uid);
2629 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2632 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2634 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2635 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2636 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2637 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2640 uid = gst_matroska_mux_create_uid ();
2641 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2643 master_chapteratom =
2644 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2645 g_free (entry->uid);
2646 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2647 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2648 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2649 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2650 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2651 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2653 cur = entry->subentries;
2654 while (cur != NULL) {
2655 cur_entry = cur->data;
2656 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2661 if (G_LIKELY (entry->tags != NULL)) {
2662 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2664 for (i = 0; i < count; ++i) {
2665 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2666 gst_matroska_mux_write_chapter_title (title, ebml);
2670 /* remove title tag */
2671 if (G_LIKELY (count > 0))
2672 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2675 gst_ebml_write_master_finish (ebml, master_chapteratom);
2679 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2680 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2682 guint64 master_edition = 0;
2684 GstTocEntry *subentry;
2686 cur = gst_toc_entry_get_sub_entries (entry);
2687 while (cur != NULL) {
2688 subentry = cur->data;
2689 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2695 if (G_LIKELY (master_edition != 0))
2696 gst_ebml_write_master_finish (ebml, master_edition);
2701 * gst_matroska_mux_start:
2702 * @mux: #GstMatroskaMux
2704 * Start a new matroska file (write headers etc...)
2707 gst_matroska_mux_start (GstMatroskaMux * mux)
2709 GstEbmlWrite *ebml = mux->ebml_write;
2710 const gchar *doctype;
2711 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2712 GST_MATROSKA_ID_TRACKS,
2713 GST_MATROSKA_ID_CHAPTERS,
2714 GST_MATROSKA_ID_CUES,
2715 GST_MATROSKA_ID_TAGS,
2718 const gchar *media_type;
2719 gboolean audio_only;
2720 guint64 master, child;
2724 GstClockTime duration = 0;
2725 guint32 segment_uid[4];
2726 GTimeVal time = { 0, 0 };
2732 /* if not streaming, check if downstream is seekable */
2733 if (!mux->ebml_write->streamable) {
2737 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2738 if (gst_pad_peer_query (mux->srcpad, query)) {
2739 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2740 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2742 /* assume seeking is not supported if query not handled downstream */
2743 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2747 mux->ebml_write->streamable = TRUE;
2748 g_object_notify (G_OBJECT (mux), "streamable");
2749 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2750 "streamable=false. Will ignore that and create streamable output "
2753 gst_query_unref (query);
2756 /* stream-start (FIXME: create id based on input ids) */
2757 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
2758 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
2761 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
2763 media_type = (audio_only) ? "audio/webm" : "video/webm";
2765 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
2767 ebml->caps = gst_caps_new_empty_simple (media_type);
2768 gst_pad_set_caps (mux->srcpad, ebml->caps);
2769 /* we start with a EBML header */
2770 doctype = mux->doctype;
2771 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2772 doctype, mux->doctype_version);
2773 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2775 /* the rest of the header is cached */
2776 gst_ebml_write_set_cache (ebml, 0x1000);
2778 /* start a segment */
2780 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2781 mux->segment_master = ebml->pos;
2783 if (!mux->ebml_write->streamable) {
2784 /* seekhead (table of contents) - we set the positions later */
2785 mux->seekhead_pos = ebml->pos;
2786 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2787 for (i = 0; seekhead_id[i] != 0; i++) {
2788 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2789 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2790 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2791 gst_ebml_write_master_finish (ebml, child);
2793 gst_ebml_write_master_finish (ebml, master);
2796 if (mux->ebml_write->streamable) {
2797 const GstTagList *tags;
2798 gboolean has_main_tags;
2801 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2802 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
2804 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
2805 guint64 master_tags, master_tag;
2807 GST_DEBUG_OBJECT (mux, "Writing tags");
2809 mux->tags_pos = ebml->pos;
2810 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2811 if (has_main_tags) {
2812 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2813 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2814 gst_ebml_write_master_finish (ebml, master_tag);
2816 gst_matroska_mux_write_streams_tags (mux);
2817 gst_ebml_write_master_finish (ebml, master_tags);
2822 mux->info_pos = ebml->pos;
2823 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2825 /* WebM does not support SegmentUID field on SegmentInfo */
2826 if (!mux->is_webm) {
2827 for (i = 0; i < 4; i++) {
2828 segment_uid[i] = g_random_int ();
2830 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2831 (guint8 *) segment_uid, 16);
2834 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2835 mux->duration_pos = ebml->pos;
2837 if (!mux->ebml_write->streamable) {
2838 for (collected = mux->collect->data; collected;
2839 collected = g_slist_next (collected)) {
2840 GstMatroskaPad *collect_pad;
2842 gint64 trackduration;
2844 collect_pad = (GstMatroskaPad *) collected->data;
2845 thepad = collect_pad->collect.pad;
2847 /* Query the total length of the track. */
2848 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2849 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2850 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2851 GST_TIME_ARGS (trackduration));
2852 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2853 duration = (GstClockTime) trackduration;
2857 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2858 gst_guint64_to_gdouble (duration) /
2859 gst_guint64_to_gdouble (mux->time_scale));
2861 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2862 "GStreamer matroskamux version " PACKAGE_VERSION);
2863 if (mux->writing_app && mux->writing_app[0]) {
2864 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2866 g_get_current_time (&time);
2867 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2868 gst_ebml_write_master_finish (ebml, master);
2871 mux->tracks_pos = ebml->pos;
2872 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2874 for (collected = mux->collect->data; collected;
2875 collected = g_slist_next (collected)) {
2876 GstMatroskaPad *collect_pad;
2878 collect_pad = (GstMatroskaPad *) collected->data;
2880 /* This will cause an error at a later time */
2881 if (collect_pad->track->codec_id == NULL)
2884 collect_pad->track->num = tracknum++;
2885 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2886 gst_matroska_mux_track_header (mux, collect_pad->track);
2887 gst_ebml_write_master_finish (ebml, child);
2888 /* some remaining pad/track setup */
2889 collect_pad->default_duration_scaled =
2890 gst_util_uint64_scale (collect_pad->track->default_duration,
2891 1, mux->time_scale);
2893 gst_ebml_write_master_finish (ebml, master);
2895 /* FIXME: Check if we get a TOC that is supported by Matroska
2896 * and clean up the code below */
2899 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2900 if (toc != NULL && !mux->ebml_write->streamable) {
2901 guint64 master_chapters = 0;
2902 GstTocEntry *toc_entry;
2903 GList *cur, *to_write = NULL;
2906 GST_DEBUG ("Writing chapters");
2908 /* check whether we have editions or chapters at the root level */
2909 toc_entry = toc->entries->data;
2911 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2912 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2913 gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
2915 /* aggregate all chapters without root edition */
2916 cur = gst_toc_get_entries (toc);
2917 while (cur != NULL) {
2918 toc_entry->subentries =
2919 g_list_prepend (toc_entry->subentries, cur->data);
2923 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2924 toc_entry->subentries->data), &start, NULL);
2925 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2926 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2927 toc_entry->subentries->data), NULL, &stop);
2928 gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
2930 to_write = g_list_append (to_write, toc_entry);
2933 to_write = toc->entries;
2936 /* finally write chapters */
2937 mux->chapters_pos = ebml->pos;
2940 while (cur != NULL) {
2941 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2946 /* close master element if any edition was written */
2947 if (G_LIKELY (master_chapters != 0))
2948 gst_ebml_write_master_finish (ebml, master_chapters);
2950 if (toc_entry != NULL) {
2951 g_list_free (toc_entry->subentries);
2952 toc_entry->subentries = NULL;
2953 gst_toc_entry_unref (toc_entry);
2954 g_list_free (to_write);
2959 /* lastly, flush the cache */
2960 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2964 gst_toc_unref (toc);
2968 /* TODO: more sensible tag mappings */
2971 const gchar *matroska_tagname;
2972 const gchar *gstreamer_tagname;
2974 gst_matroska_tag_conv[] = {
2976 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2977 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2978 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2979 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2980 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2981 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2982 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2983 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2984 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2985 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2986 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2987 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2988 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2989 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2990 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2993 /* Every stagefright implementation on android up to and including 6.0.1 is using
2994 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
2995 so before outputting tags and tag elements we better make sure that there are
2996 actually tags we are going to write */
2998 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3001 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3002 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3004 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3005 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3006 if (strcmp (tagname_gst, tag) == 0) {
3007 GValue src = { 0, };
3010 if (!gst_tag_list_copy_value (&src, list, tag))
3012 dest = gst_value_serialize (&src);
3014 g_value_unset (&src);
3026 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3029 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3031 guint64 simpletag_master;
3033 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3034 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3035 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3037 if (strcmp (tagname_gst, tag) == 0) {
3038 GValue src = { 0, };
3041 if (!gst_tag_list_copy_value (&src, list, tag))
3043 if ((dest = gst_value_serialize (&src))) {
3045 simpletag_master = gst_ebml_write_master_start (ebml,
3046 GST_MATROSKA_ID_SIMPLETAG);
3047 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3048 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3049 gst_ebml_write_master_finish (ebml, simpletag_master);
3052 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3054 g_value_unset (&src);
3061 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3063 guint64 master_tag, master_targets;
3066 ebml = mux->ebml_write;
3068 if (G_UNLIKELY (mpad->tags == NULL
3069 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3072 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3073 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3075 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3077 gst_ebml_write_master_finish (ebml, master_targets);
3078 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3079 gst_ebml_write_master_finish (ebml, master_tag);
3083 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3087 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3088 GstMatroskaPad *collect_pad;
3090 collect_pad = (GstMatroskaPad *) walk->data;
3092 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3097 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3101 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3102 GstMatroskaPad *collect_pad;
3104 collect_pad = (GstMatroskaPad *) walk->data;
3105 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3113 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3114 const GstTocEntry * entry, guint64 * master_tags)
3116 guint64 master_tag, master_targets;
3120 ebml = mux->ebml_write;
3122 if (G_UNLIKELY (entry->tags != NULL
3123 && !gst_matroska_mux_tag_list_is_empty (entry->tags))) {
3124 if (*master_tags == 0) {
3125 mux->tags_pos = ebml->pos;
3126 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3129 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3131 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3133 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
3134 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3135 g_ascii_strtoull (entry->uid, NULL, 10));
3137 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3138 g_ascii_strtoull (entry->uid, NULL, 10));
3140 gst_ebml_write_master_finish (ebml, master_targets);
3141 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
3142 gst_ebml_write_master_finish (ebml, master_tag);
3145 cur = entry->subentries;
3146 while (cur != NULL) {
3147 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
3154 * gst_matroska_mux_finish:
3155 * @mux: #GstMatroskaMux
3157 * Finish a new matroska file (write index etc...)
3160 gst_matroska_mux_finish (GstMatroskaMux * mux)
3162 GstEbmlWrite *ebml = mux->ebml_write;
3164 guint64 duration = 0;
3166 const GstTagList *tags;
3167 gboolean has_main_tags;
3169 /* finish last cluster */
3171 gst_ebml_write_master_finish (ebml, mux->cluster);
3175 if (mux->index != NULL) {
3177 guint64 master, pointentry_master, trackpos_master;
3179 mux->cues_pos = ebml->pos;
3180 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3181 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3183 for (n = 0; n < mux->num_indexes; n++) {
3184 GstMatroskaIndex *idx = &mux->index[n];
3186 pointentry_master = gst_ebml_write_master_start (ebml,
3187 GST_MATROSKA_ID_POINTENTRY);
3188 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3189 idx->time / mux->time_scale);
3190 trackpos_master = gst_ebml_write_master_start (ebml,
3191 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3192 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3193 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3194 idx->pos - mux->segment_master);
3195 gst_ebml_write_master_finish (ebml, trackpos_master);
3196 gst_ebml_write_master_finish (ebml, pointentry_master);
3199 gst_ebml_write_master_finish (ebml, master);
3200 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3204 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3205 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3207 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)
3208 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
3209 guint64 master_tags = 0, master_tag;
3214 GST_DEBUG_OBJECT (mux, "Writing tags");
3217 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3220 if (has_main_tags) {
3221 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3222 mux->tags_pos = ebml->pos;
3223 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3224 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3227 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3230 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
3234 gst_ebml_write_master_finish (ebml, master_tag);
3239 while (cur != NULL) {
3240 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
3246 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3247 mux->tags_pos = ebml->pos;
3248 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3250 gst_matroska_mux_write_streams_tags (mux);
3252 if (master_tags != 0)
3253 gst_ebml_write_master_finish (ebml, master_tags);
3256 /* update seekhead. We know that:
3257 * - a seekhead contains 5 entries.
3258 * - order of entries is as above.
3259 * - a seekhead has a 4-byte header + 8-byte length
3260 * - each entry is 2-byte master, 2-byte ID pointer,
3261 * 2-byte length pointer, all 8/1-byte length, 4-
3262 * byte ID and 8-byte length pointer, where the
3263 * length pointer starts at 20.
3264 * - all entries are local to the segment (so pos - segment_master).
3265 * - so each entry is at 12 + 20 + num * 28. */
3266 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3267 mux->info_pos - mux->segment_master);
3268 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3269 mux->tracks_pos - mux->segment_master);
3270 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
3271 && mux->chapters_pos > 0) {
3272 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3273 mux->chapters_pos - mux->segment_master);
3276 guint64 my_pos = ebml->pos;
3278 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3279 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3280 gst_ebml_write_seek (ebml, my_pos);
3282 if (mux->index != NULL) {
3283 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3284 mux->cues_pos - mux->segment_master);
3287 guint64 my_pos = ebml->pos;
3289 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3290 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3291 gst_ebml_write_seek (ebml, my_pos);
3295 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3296 mux->tags_pos - mux->segment_master);
3299 guint64 my_pos = ebml->pos;
3301 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3302 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3303 gst_ebml_write_seek (ebml, my_pos);
3307 * - first get the overall duration
3308 * (a released track may have left a duration in here)
3309 * - write some track header data for subtitles
3311 duration = mux->duration;
3313 for (collected = mux->collect->data; collected;
3314 collected = g_slist_next (collected)) {
3315 GstMatroskaPad *collect_pad;
3317 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3318 * since this means buffer without timestamps that is not possibile
3320 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3322 collect_pad = (GstMatroskaPad *) collected->data;
3324 GST_DEBUG_OBJECT (mux,
3325 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3326 " end ts %" GST_TIME_FORMAT, collect_pad,
3327 GST_TIME_ARGS (collect_pad->start_ts),
3328 GST_TIME_ARGS (collect_pad->end_ts));
3330 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3331 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3332 collected_duration =
3333 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3334 GST_DEBUG_OBJECT (collect_pad,
3335 "final track duration: %" GST_TIME_FORMAT,
3336 GST_TIME_ARGS (collected_duration));
3338 GST_WARNING_OBJECT (collect_pad, "unable to get final track duration");
3340 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3341 duration < collected_duration)
3342 duration = collected_duration;
3346 /* seek back (optional, but do anyway) */
3347 gst_ebml_write_seek (ebml, pos);
3349 /* update duration */
3350 if (duration != 0) {
3351 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3352 GST_TIME_ARGS (duration));
3353 pos = mux->ebml_write->pos;
3354 gst_ebml_write_seek (ebml, mux->duration_pos);
3355 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3356 gst_guint64_to_gdouble (duration) /
3357 gst_guint64_to_gdouble (mux->time_scale));
3358 gst_ebml_write_seek (ebml, pos);
3361 guint64 my_pos = ebml->pos;
3363 gst_ebml_write_seek (ebml, mux->duration_pos);
3364 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3365 gst_ebml_write_seek (ebml, my_pos);
3367 GST_DEBUG_OBJECT (mux, "finishing segment");
3368 /* finish segment - this also writes element length */
3369 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3373 * gst_matroska_mux_buffer_header:
3374 * @track: Track context.
3375 * @relative_timestamp: relative timestamp of the buffer
3376 * @flags: Buffer flags.
3378 * Create a buffer containing buffer header.
3380 * Returns: New buffer.
3383 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3384 gint16 relative_timestamp, int flags)
3387 guint8 *data = g_malloc (4);
3389 hdr = gst_buffer_new_wrapped (data, 4);
3390 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3391 data[0] = track->num | 0x80;
3392 /* time relative to clustertime */
3393 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3401 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3402 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3403 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3406 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3407 GstMatroskaPad * collect_pad, GstBuffer * buf)
3409 GstMatroskaTrackVideoContext *ctx =
3410 (GstMatroskaTrackVideoContext *) collect_pad->track;
3415 guint32 next_parse_offset;
3416 GstBuffer *ret = NULL;
3417 gboolean is_muxing_unit = FALSE;
3419 gst_buffer_map (buf, &map, GST_MAP_READ);
3424 gst_buffer_unmap (buf, &map);
3425 gst_buffer_unref (buf);
3429 /* Check if this buffer contains a picture or end-of-sequence packet */
3430 while (size >= 13) {
3431 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3432 gst_buffer_unmap (buf, &map);
3433 gst_buffer_unref (buf);
3437 parse_code = GST_READ_UINT8 (data + 4);
3438 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3439 if (ctx->dirac_unit) {
3440 gst_buffer_unref (ctx->dirac_unit);
3441 ctx->dirac_unit = NULL;
3443 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3444 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3445 is_muxing_unit = TRUE;
3449 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3451 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3454 data += next_parse_offset;
3455 size -= next_parse_offset;
3458 if (ctx->dirac_unit)
3459 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3461 ctx->dirac_unit = gst_buffer_ref (buf);
3463 gst_buffer_unmap (buf, &map);
3465 if (is_muxing_unit) {
3466 ret = gst_buffer_make_writable (ctx->dirac_unit);
3467 ctx->dirac_unit = NULL;
3468 gst_buffer_copy_into (ret, buf,
3469 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3470 gst_buffer_unref (buf);
3472 gst_buffer_unref (buf);
3480 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3484 GValue streamheader = { 0 };
3485 GValue bufval = { 0 };
3486 GstBuffer *streamheader_buffer;
3487 GstEbmlWrite *ebml = mux->ebml_write;
3489 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3490 caps = gst_caps_copy (mux->ebml_write->caps);
3491 s = gst_caps_get_structure (caps, 0);
3492 g_value_init (&streamheader, GST_TYPE_ARRAY);
3493 g_value_init (&bufval, GST_TYPE_BUFFER);
3494 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3495 gst_value_set_buffer (&bufval, streamheader_buffer);
3496 gst_value_array_append_value (&streamheader, &bufval);
3497 g_value_unset (&bufval);
3498 gst_structure_set_value (s, "streamheader", &streamheader);
3499 g_value_unset (&streamheader);
3500 gst_caps_replace (&ebml->caps, caps);
3501 gst_buffer_unref (streamheader_buffer);
3502 gst_pad_set_caps (mux->srcpad, caps);
3503 gst_caps_unref (caps);
3507 * gst_matroska_mux_write_data:
3508 * @mux: #GstMatroskaMux
3509 * @collect_pad: #GstMatroskaPad with the data
3511 * Write collected data (called from gst_matroska_mux_collected).
3513 * Returns: Result of the gst_pad_push issued to write the data.
3515 static GstFlowReturn
3516 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3519 GstEbmlWrite *ebml = mux->ebml_write;
3522 gboolean write_duration;
3523 gint16 relative_timestamp;
3524 gint64 relative_timestamp64;
3525 guint64 block_duration, duration_diff = 0;
3526 gboolean is_video_keyframe = FALSE;
3527 gboolean is_video_invisible = FALSE;
3528 GstMatroskamuxPad *pad;
3530 GstClockTime buffer_timestamp;
3531 GstAudioClippingMeta *cmeta = NULL;
3534 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3536 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3537 if (collect_pad->track->xiph_headers_to_skip > 0) {
3538 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3539 gst_buffer_unref (buf);
3540 --collect_pad->track->xiph_headers_to_skip;
3544 /* for dirac we have to queue up everything up to a picture unit */
3545 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) {
3546 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3549 } else if (!strcmp (collect_pad->track->codec_id,
3550 GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
3551 /* Remove the 'Frame container atom' header' */
3552 buf = gst_buffer_make_writable (buf);
3553 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
3557 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3559 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3560 * this would wreak havoc with time stored in matroska file */
3561 /* TODO: maybe calculate a timestamp by using the previous timestamp
3562 * and default duration */
3563 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
3564 GST_WARNING_OBJECT (collect_pad->collect.pad,
3565 "Invalid buffer timestamp; dropping buffer");
3566 gst_buffer_unref (buf);
3570 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
3571 && collect_pad->track->codec_delay) {
3572 /* All timestamps should include the codec delay */
3573 if (buffer_timestamp > collect_pad->track->codec_delay) {
3574 buffer_timestamp += collect_pad->track->codec_delay;
3576 buffer_timestamp = 0;
3577 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
3581 /* set the timestamp for outgoing buffers */
3582 ebml->timestamp = buffer_timestamp;
3584 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3585 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3586 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3587 GST_TIME_ARGS (buffer_timestamp));
3588 is_video_keyframe = TRUE;
3589 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3590 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3591 || !strcmp (collect_pad->track->codec_id,
3592 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3593 GST_LOG_OBJECT (mux,
3594 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3595 GST_TIME_ARGS (buffer_timestamp));
3596 is_video_invisible = TRUE;
3601 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3602 * or when we may be reaching the limit of the relative timestamp */
3603 if (mux->cluster_time +
3604 mux->max_cluster_duration < buffer_timestamp
3605 || is_video_keyframe || mux->force_key_unit_event) {
3606 if (!mux->ebml_write->streamable)
3607 gst_ebml_write_master_finish (ebml, mux->cluster);
3609 /* Forward the GstForceKeyUnit event after finishing the cluster */
3610 if (mux->force_key_unit_event) {
3611 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3612 mux->force_key_unit_event = NULL;
3615 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3616 mux->cluster_pos = ebml->pos;
3617 gst_ebml_write_set_cache (ebml, 0x20);
3619 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3620 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3621 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3622 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3623 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3624 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
3625 mux->cluster_time = buffer_timestamp;
3626 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3627 mux->prev_cluster_size);
3632 mux->cluster_pos = ebml->pos;
3633 gst_ebml_write_set_cache (ebml, 0x20);
3634 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3635 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3636 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3637 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
3638 mux->cluster_time = buffer_timestamp;
3641 /* We currently write index entries for all video tracks or for the audio
3642 * track in a single-track audio file. This could be improved by keeping the
3643 * index only for the *first* video track. */
3645 /* TODO: index is useful for every track, should contain the number of
3646 * the block in the cluster which contains the timestamp, should also work
3647 * for files with multiple audio tracks.
3649 if (!mux->ebml_write->streamable &&
3650 (is_video_keyframe ||
3651 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3652 (mux->num_streams == 1)))) {
3655 if (mux->min_index_interval != 0) {
3656 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3657 if (mux->index[last_idx].track == collect_pad->track->num)
3662 if (last_idx < 0 || mux->min_index_interval == 0 ||
3663 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
3664 >= mux->min_index_interval)) {
3665 GstMatroskaIndex *idx;
3667 if (mux->num_indexes % 32 == 0) {
3668 mux->index = g_renew (GstMatroskaIndex, mux->index,
3669 mux->num_indexes + 32);
3671 idx = &mux->index[mux->num_indexes++];
3673 idx->pos = mux->cluster_pos;
3674 idx->time = buffer_timestamp;
3675 idx->track = collect_pad->track->num;
3679 /* Check if the duration differs from the default duration. */
3680 write_duration = FALSE;
3682 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3683 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
3684 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
3686 /* small difference should be ok. */
3687 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3688 block_duration < collect_pad->default_duration_scaled - 1) {
3689 write_duration = TRUE;
3693 /* write the block, for doctype v2 use SimpleBlock if possible
3694 * one slice (*breath*).
3695 * FIXME: Need to do correct lacing! */
3696 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
3697 if (relative_timestamp64 >= 0) {
3698 /* round the timestamp */
3699 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3700 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3703 /* round the timestamp */
3704 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3705 relative_timestamp =
3706 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3710 if (is_video_invisible)
3713 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
3714 cmeta = gst_buffer_get_audio_clipping_meta (buf);
3715 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
3717 /* Start clipping is done via header and CodecDelay */
3718 if (cmeta && !cmeta->end)
3722 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
3723 if (is_video_keyframe)
3727 gst_matroska_mux_create_buffer_header (collect_pad->track,
3728 relative_timestamp, flags);
3729 gst_ebml_write_set_cache (ebml, 0x40);
3730 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3731 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3732 gst_ebml_write_buffer (ebml, hdr);
3733 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
3734 gst_ebml_write_buffer (ebml, buf);
3736 return gst_ebml_last_write_result (ebml);
3738 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3739 /* write and call order slightly unnatural,
3740 * but avoids seek and minizes pushing */
3741 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3743 gst_matroska_mux_create_buffer_header (collect_pad->track,
3744 relative_timestamp, flags);
3746 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3748 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
3750 /* Start clipping is done via header and CodecDelay */
3753 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
3754 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
3758 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3759 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3760 gst_ebml_write_buffer (ebml, hdr);
3761 gst_ebml_write_master_finish_full (ebml, blockgroup,
3762 gst_buffer_get_size (buf));
3763 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
3764 gst_ebml_write_buffer (ebml, buf);
3766 return gst_ebml_last_write_result (ebml);
3771 * gst_matroska_mux_handle_buffer:
3772 * @pads: #GstCollectPads
3773 * @uuser_data: #GstMatroskaMux
3775 * Collectpads callback.
3777 * Returns: #GstFlowReturn
3779 static GstFlowReturn
3780 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
3781 GstBuffer * buf, gpointer user_data)
3783 GstClockTime buffer_timestamp;
3784 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3785 GstEbmlWrite *ebml = mux->ebml_write;
3786 GstMatroskaPad *best;
3787 GstFlowReturn ret = GST_FLOW_OK;
3788 GST_DEBUG_OBJECT (mux, "Collected pads");
3790 /* start with a header */
3791 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3792 if (mux->collect->data == NULL) {
3793 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3794 ("No input streams configured"));
3795 return GST_FLOW_ERROR;
3797 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3798 gst_ebml_start_streamheader (ebml);
3799 gst_matroska_mux_start (mux);
3800 gst_matroska_mux_stop_streamheader (mux);
3801 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3804 /* provided with stream to write from */
3805 best = (GstMatroskaPad *) data;
3807 /* if there is no best pad, we have reached EOS */
3809 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
3810 if (!mux->ebml_write->streamable) {
3811 gst_matroska_mux_finish (mux);
3813 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3815 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3820 if (best->track->codec_id == NULL) {
3821 GST_ERROR_OBJECT (best->collect.pad, "No codec-id for pad");
3822 ret = GST_FLOW_NOT_NEGOTIATED;
3826 /* if we have a best stream, should also have a buffer */
3829 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
3831 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3832 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3833 GST_TIME_ARGS (buffer_timestamp),
3834 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3836 /* make note of first and last encountered timestamps, so we can calculate
3837 * the actual duration later when we send an updated header on eos */
3838 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
3839 GstClockTime start_ts = buffer_timestamp;
3840 GstClockTime end_ts = start_ts;
3842 if (GST_BUFFER_DURATION_IS_VALID (buf))
3843 end_ts += GST_BUFFER_DURATION (buf);
3844 else if (best->track->default_duration)
3845 end_ts += best->track->default_duration;
3847 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3848 best->end_ts = end_ts;
3850 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3851 start_ts < best->start_ts))
3852 best->start_ts = start_ts;
3855 /* write one buffer */
3856 ret = gst_matroska_mux_write_data (mux, best, buf);
3864 * gst_matroska_mux_change_state:
3865 * @element: #GstMatroskaMux
3866 * @transition: State change transition.
3868 * Change the muxer state.
3870 * Returns: #GstStateChangeReturn
3872 static GstStateChangeReturn
3873 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3875 GstStateChangeReturn ret;
3876 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3878 switch (transition) {
3879 case GST_STATE_CHANGE_NULL_TO_READY:
3881 case GST_STATE_CHANGE_READY_TO_PAUSED:
3882 gst_collect_pads_start (mux->collect);
3884 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3886 case GST_STATE_CHANGE_PAUSED_TO_READY:
3887 gst_collect_pads_stop (mux->collect);
3893 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3895 switch (transition) {
3896 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3898 case GST_STATE_CHANGE_PAUSED_TO_READY:
3899 gst_matroska_mux_reset (GST_ELEMENT (mux));
3901 case GST_STATE_CHANGE_READY_TO_NULL:
3911 gst_matroska_mux_set_property (GObject * object,
3912 guint prop_id, const GValue * value, GParamSpec * pspec)
3914 GstMatroskaMux *mux;
3916 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3917 mux = GST_MATROSKA_MUX (object);
3920 case PROP_WRITING_APP:
3921 if (!g_value_get_string (value)) {
3922 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3925 g_free (mux->writing_app);
3926 mux->writing_app = g_value_dup_string (value);
3928 case PROP_DOCTYPE_VERSION:
3929 mux->doctype_version = g_value_get_int (value);
3931 case PROP_MIN_INDEX_INTERVAL:
3932 mux->min_index_interval = g_value_get_int64 (value);
3934 case PROP_STREAMABLE:
3935 mux->ebml_write->streamable = g_value_get_boolean (value);
3937 case PROP_TIMECODESCALE:
3938 mux->time_scale = g_value_get_int64 (value);
3941 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3947 gst_matroska_mux_get_property (GObject * object,
3948 guint prop_id, GValue * value, GParamSpec * pspec)
3950 GstMatroskaMux *mux;
3952 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3953 mux = GST_MATROSKA_MUX (object);
3956 case PROP_WRITING_APP:
3957 g_value_set_string (value, mux->writing_app);
3959 case PROP_DOCTYPE_VERSION:
3960 g_value_set_int (value, mux->doctype_version);
3962 case PROP_MIN_INDEX_INTERVAL:
3963 g_value_set_int64 (value, mux->min_index_interval);
3965 case PROP_STREAMABLE:
3966 g_value_set_boolean (value, mux->ebml_write->streamable);
3968 case PROP_TIMECODESCALE:
3969 g_value_set_int64 (value, mux->time_scale);
3972 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);