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>
57 #include "matroska-mux.h"
58 #include "matroska-ids.h"
60 #define GST_MATROSKA_MUX_CHAPLANG "und"
62 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
63 #define GST_CAT_DEFAULT matroskamux_debug
70 ARG_MIN_INDEX_INTERVAL,
74 #define DEFAULT_DOCTYPE_VERSION 2
75 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
76 #define DEFAULT_MIN_INDEX_INTERVAL 0
77 #define DEFAULT_STREAMABLE FALSE
79 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
80 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
82 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
85 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
88 #define COMMON_VIDEO_CAPS \
89 "width = (int) [ 16, 4096 ], " \
90 "height = (int) [ 16, 4096 ] "
93 * * require codec data, etc as needed
96 static GstStaticPadTemplate videosink_templ =
97 GST_STATIC_PAD_TEMPLATE ("video_%u",
100 GST_STATIC_CAPS ("video/mpeg, "
101 "mpegversion = (int) { 1, 2, 4 }, "
102 "systemstream = (boolean) false, "
103 COMMON_VIDEO_CAPS "; "
104 "video/x-h264, stream-format=avc, alignment=au, "
105 COMMON_VIDEO_CAPS "; "
106 "video/x-h265, stream-format=hvc1, alignment=au, "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS "; "
119 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS "; "
123 "video/x-pn-realvideo, "
124 "rmversion = (int) [1, 4], "
125 COMMON_VIDEO_CAPS "; "
127 COMMON_VIDEO_CAPS "; "
129 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
130 COMMON_VIDEO_CAPS "; "
131 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
134 #define COMMON_AUDIO_CAPS \
135 "channels = (int) [ 1, MAX ], " \
136 "rate = (int) [ 1, MAX ]"
139 * * require codec data, etc as needed
141 static GstStaticPadTemplate audiosink_templ =
142 GST_STATIC_PAD_TEMPLATE ("audio_%u",
145 GST_STATIC_CAPS ("audio/mpeg, "
146 "mpegversion = (int) 1, "
147 "layer = (int) [ 1, 3 ], "
148 COMMON_AUDIO_CAPS "; "
150 "mpegversion = (int) { 2, 4 }, "
151 "stream-format = (string) raw, "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
160 COMMON_AUDIO_CAPS "; "
162 COMMON_AUDIO_CAPS "; "
164 COMMON_AUDIO_CAPS "; "
166 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
167 "layout = (string) interleaved, "
168 COMMON_AUDIO_CAPS ";"
170 "width = (int) { 8, 16, 24 }, "
171 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
172 "audio/x-pn-realaudio, "
173 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
174 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
175 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
176 COMMON_AUDIO_CAPS ";"
178 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
180 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
182 "layout = (string)dvi, "
183 "block_align = (int)[64, 8192], "
184 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
186 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
189 static GstStaticPadTemplate subtitlesink_templ =
190 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
193 GST_STATIC_CAPS ("subtitle/x-kate; "
194 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
195 "application/x-usf; subpicture/x-dvd; "
196 "application/x-subtitle-unknown")
199 static gpointer parent_class; /* NULL */
201 /* Matroska muxer destructor */
202 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
203 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
207 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
208 GstCollectData * data, GstBuffer * buf, gpointer user_data);
209 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
210 GstCollectData * data, GstEvent * event, gpointer user_data);
213 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
214 GstObject * parent, GstEvent * event);
215 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
216 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
217 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
219 /* gst internal change state handler */
220 static GstStateChangeReturn
221 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
223 /* gobject bla bla */
224 static void gst_matroska_mux_set_property (GObject * object,
225 guint prop_id, const GValue * value, GParamSpec * pspec);
226 static void gst_matroska_mux_get_property (GObject * object,
227 guint prop_id, GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_reset (GstElement * element);
233 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
235 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
246 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
249 /* Cannot use boilerplate macros here because we need the full init function
250 * signature with the additional class argument, so we use the right template
251 * for the sink caps */
253 gst_matroska_mux_get_type (void)
255 static GType object_type; /* 0 */
257 if (object_type == 0) {
258 static const GTypeInfo object_info = {
259 sizeof (GstMatroskaMuxClass),
260 NULL, /* base_init */
261 NULL, /* base_finalize */
262 (GClassInitFunc) gst_matroska_mux_class_init,
263 NULL, /* class_finalize */
264 NULL, /* class_data */
265 sizeof (GstMatroskaMux),
267 (GInstanceInitFunc) gst_matroska_mux_init
269 const GInterfaceInfo iface_info = { NULL };
271 object_type = g_type_register_static (GST_TYPE_ELEMENT,
272 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
274 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
275 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
282 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
284 GObjectClass *gobject_class;
285 GstElementClass *gstelement_class;
287 gobject_class = (GObjectClass *) klass;
288 gstelement_class = (GstElementClass *) klass;
290 gst_element_class_add_pad_template (gstelement_class,
291 gst_static_pad_template_get (&videosink_templ));
292 gst_element_class_add_pad_template (gstelement_class,
293 gst_static_pad_template_get (&audiosink_templ));
294 gst_element_class_add_pad_template (gstelement_class,
295 gst_static_pad_template_get (&subtitlesink_templ));
296 gst_element_class_add_pad_template (gstelement_class,
297 gst_static_pad_template_get (&src_templ));
298 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
300 "Muxes video/audio/subtitle streams into a matroska stream",
301 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
303 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
306 gobject_class->finalize = gst_matroska_mux_finalize;
308 gobject_class->get_property = gst_matroska_mux_get_property;
309 gobject_class->set_property = gst_matroska_mux_set_property;
311 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
312 g_param_spec_string ("writing-app", "Writing application.",
313 "The name the application that creates the matroska file.",
314 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
316 g_param_spec_int ("version", "DocType version",
317 "This parameter determines what Matroska features can be used.",
318 1, 2, DEFAULT_DOCTYPE_VERSION,
319 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
320 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
321 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
322 "entries", "An index entry is created every so many nanoseconds.",
323 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
324 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
325 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
326 g_param_spec_boolean ("streamable", "Determines whether output should "
327 "be streamable", "If set to true, the output should be as if it is "
328 "to be streamed and hence no indexes written or duration written.",
330 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
332 gstelement_class->change_state =
333 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
334 gstelement_class->request_new_pad =
335 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
336 gstelement_class->release_pad =
337 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
339 parent_class = g_type_class_peek_parent (klass);
343 * Start of pad option handler code
345 #define DEFAULT_PAD_FRAME_DURATION TRUE
346 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
351 PROP_PAD_FRAME_DURATION
357 gboolean frame_duration;
358 gboolean frame_duration_user;
361 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
364 gst_matroskamux_pad_get_type (void)
366 static GType type = 0;
368 if (G_UNLIKELY (type == 0)) {
369 type = g_type_register_static_simple (GST_TYPE_PAD,
370 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
371 (GClassInitFunc) gst_matroskamux_pad_class_init,
372 sizeof (GstMatroskamuxPad), NULL, 0);
377 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
378 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
379 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
380 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
383 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
384 GValue * value, GParamSpec * pspec)
386 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
389 case PROP_PAD_FRAME_DURATION:
390 g_value_set_boolean (value, pad->frame_duration);
393 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
400 const GValue * value, GParamSpec * pspec)
402 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
405 case PROP_PAD_FRAME_DURATION:
406 pad->frame_duration = g_value_get_boolean (value);
407 pad->frame_duration_user = TRUE;
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
416 gst_matroskamux_pad_class_init (GstPadClass * klass)
418 GObjectClass *gobject_class = (GObjectClass *) klass;
420 gobject_class->set_property = gst_matroskamux_pad_set_property;
421 gobject_class->get_property = gst_matroskamux_pad_get_property;
423 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
424 g_param_spec_boolean ("frame-duration", "Frame duration",
425 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
426 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
430 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
432 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
433 pad->frame_duration_user = FALSE;
437 * End of pad option handler code
441 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
443 GstPadTemplate *templ;
446 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
447 mux->srcpad = gst_pad_new_from_template (templ, "src");
449 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
450 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
451 gst_pad_use_fixed_caps (mux->srcpad);
453 mux->collect = gst_collect_pads_new ();
454 gst_collect_pads_set_clip_function (mux->collect,
455 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
456 gst_collect_pads_set_buffer_function (mux->collect,
457 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
458 gst_collect_pads_set_event_function (mux->collect,
459 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
461 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
462 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
464 /* property defaults */
465 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
466 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
467 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
468 mux->streamable = DEFAULT_STREAMABLE;
470 /* initialize internal variables */
472 mux->num_streams = 0;
473 mux->num_a_streams = 0;
474 mux->num_t_streams = 0;
475 mux->num_v_streams = 0;
477 /* create used uid list */
478 mux->used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
480 /* initialize remaining variables */
481 gst_matroska_mux_reset (GST_ELEMENT (mux));
486 * gst_matroska_mux_finalize:
487 * @object: #GstMatroskaMux that should be finalized.
489 * Finalize matroska muxer.
492 gst_matroska_mux_finalize (GObject * object)
494 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
496 gst_event_replace (&mux->force_key_unit_event, NULL);
498 gst_object_unref (mux->collect);
499 gst_object_unref (mux->ebml_write);
500 if (mux->writing_app)
501 g_free (mux->writing_app);
503 g_array_free (mux->used_uids, TRUE);
505 G_OBJECT_CLASS (parent_class)->finalize (object);
510 * gst_matroska_mux_create_uid:
511 * @mux: #GstMatroskaMux to generate UID for.
513 * Generate new unused track UID.
515 * Returns: New track UID.
518 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
525 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
526 for (i = 0; i < mux->used_uids->len; i++) {
527 if (g_array_index (mux->used_uids, guint64, i) == uid) {
532 g_array_append_val (mux->used_uids, uid);
540 * gst_matroska_pad_reset:
541 * @collect_pad: the #GstMatroskaPad
543 * Reset and/or release resources of a matroska collect pad.
546 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
549 GstMatroskaTrackType type = 0;
551 /* free track information */
552 if (collect_pad->track != NULL) {
553 /* retrieve for optional later use */
554 name = collect_pad->track->name;
555 type = collect_pad->track->type;
556 /* extra for video */
557 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
558 GstMatroskaTrackVideoContext *ctx =
559 (GstMatroskaTrackVideoContext *) collect_pad->track;
561 if (ctx->dirac_unit) {
562 gst_buffer_unref (ctx->dirac_unit);
563 ctx->dirac_unit = NULL;
566 g_free (collect_pad->track->codec_id);
567 g_free (collect_pad->track->codec_name);
569 g_free (collect_pad->track->name);
570 g_free (collect_pad->track->language);
571 g_free (collect_pad->track->codec_priv);
572 g_free (collect_pad->track);
573 collect_pad->track = NULL;
576 if (!full && type != 0) {
577 GstMatroskaTrackContext *context;
579 /* create a fresh context */
581 case GST_MATROSKA_TRACK_TYPE_VIDEO:
582 context = (GstMatroskaTrackContext *)
583 g_new0 (GstMatroskaTrackVideoContext, 1);
585 case GST_MATROSKA_TRACK_TYPE_AUDIO:
586 context = (GstMatroskaTrackContext *)
587 g_new0 (GstMatroskaTrackAudioContext, 1);
589 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
590 context = (GstMatroskaTrackContext *)
591 g_new0 (GstMatroskaTrackSubtitleContext, 1);
594 g_assert_not_reached ();
598 context->type = type;
599 context->name = name;
600 /* TODO: check default values for the context */
601 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
602 collect_pad->track = context;
603 collect_pad->duration = 0;
604 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
605 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
610 * gst_matroska_pad_free:
611 * @collect_pad: the #GstMatroskaPad
613 * Release resources of a matroska collect pad.
616 gst_matroska_pad_free (GstPad * collect_pad)
618 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
623 * gst_matroska_mux_reset:
624 * @element: #GstMatroskaMux that should be reseted.
626 * Reset matroska muxer back to initial state.
629 gst_matroska_mux_reset (GstElement * element)
631 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
634 /* reset EBML write */
635 gst_ebml_write_reset (mux->ebml_write);
638 mux->state = GST_MATROSKA_MUX_STATE_START;
640 /* clean up existing streams */
642 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
643 GstMatroskaPad *collect_pad;
645 collect_pad = (GstMatroskaPad *) walk->data;
647 /* reset collect pad to pristine state */
648 gst_matroska_pad_reset (collect_pad, FALSE);
652 mux->num_indexes = 0;
657 mux->time_scale = GST_MSECOND;
658 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
663 mux->cluster_time = 0;
664 mux->cluster_pos = 0;
665 mux->prev_cluster_size = 0;
668 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
673 gst_toc_setter_reset (GST_TOC_SETTER (mux));
675 mux->chapters_pos = 0;
677 /* clear used uids */
678 if (mux->used_uids->len > 0) {
679 g_array_remove_range (mux->used_uids, 0, mux->used_uids->len);
684 * gst_matroska_mux_handle_src_event:
685 * @pad: Pad which received the event.
686 * @event: Received event.
688 * handle events - copied from oggmux without understanding
690 * Returns: #TRUE on success.
693 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
698 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
702 /* disable seeking for now */
708 return gst_pad_event_default (pad, parent, event);
713 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
715 if (context->codec_priv != NULL) {
716 g_free (context->codec_priv);
717 context->codec_priv = NULL;
718 context->codec_priv_size = 0;
723 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
733 /* produce comma-separated list in hex format */
734 for (i = 0; i < 16; ++i) {
736 /* replicate vobsub's slightly off RGB conversion calculation */
737 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
738 u = ((col >> 8) & 0xff) - 128;
739 v = (col & 0xff) - 128;
740 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
741 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
742 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
743 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
746 sclut = g_strjoinv (",", clutv);
748 /* build codec private; only palette for now */
749 gst_matroska_mux_free_codec_priv (context);
750 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
751 /* include terminating 0 */
752 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
754 for (i = 0; i < 16; ++i) {
761 * gst_matroska_mux_handle_sink_event:
762 * @pad: Pad which received the event.
763 * @event: Received event.
765 * handle events - informational ones like tags
767 * Returns: #TRUE on success.
770 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
771 GstCollectData * data, GstEvent * event, gpointer user_data)
773 GstMatroskaPad *collect_pad;
774 GstMatroskaTrackContext *context;
780 mux = GST_MATROSKA_MUX (user_data);
781 collect_pad = (GstMatroskaPad *) data;
783 context = collect_pad->track;
786 switch (GST_EVENT_TYPE (event)) {
787 case GST_EVENT_CAPS:{
790 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
791 gst_event_parse_caps (event, &caps);
793 ret = collect_pad->capsfunc (pad, caps);
794 gst_event_unref (event);
801 GST_DEBUG_OBJECT (mux, "received tag event");
802 gst_event_parse_tag (event, &list);
804 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
805 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
806 const gchar *lang_code;
808 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
810 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
811 context->language = g_strdup (lang_code);
813 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
818 /* FIXME: what about stream-specific tags? */
819 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
820 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
822 gst_event_unref (event);
823 /* handled this, don't want collectpads to forward it downstream */
829 GstToc *toc, *old_toc;
831 if (mux->chapters_pos > 0)
834 GST_DEBUG_OBJECT (mux, "received toc event");
835 gst_event_parse_toc (event, &toc, NULL);
838 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
839 if (old_toc != NULL) {
841 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
842 gst_toc_unref (old_toc);
845 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
849 gst_event_unref (event);
850 /* handled this, don't want collectpads to forward it downstream */
854 case GST_EVENT_CUSTOM_DOWNSTREAM:{
855 const GstStructure *structure;
857 structure = gst_event_get_structure (event);
858 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
859 gst_event_replace (&mux->force_key_unit_event, NULL);
860 mux->force_key_unit_event = event;
862 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
863 !strcmp ("dvd-spu-clut-change",
864 gst_structure_get_string (structure, "event"))) {
869 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
870 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
871 GST_DEBUG_OBJECT (pad, "... discarding");
874 /* first transform event data into table form */
875 for (i = 0; i < 16; i++) {
876 g_snprintf (name, sizeof (name), "clut%02d", i);
877 if (!gst_structure_get_int (structure, name, &value)) {
878 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
879 "contain %s field", name);
885 /* transform into private data for stream; text form */
886 gst_matroska_mux_build_vobsub_private (context, clut);
895 return gst_collect_pads_event_default (pads, data, event, FALSE);
901 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
904 g_assert (context && id);
905 if (context->codec_id)
906 g_free (context->codec_id);
907 context->codec_id = g_strdup (id);
911 * gst_matroska_mux_video_pad_setcaps:
912 * @pad: Pad which got the caps.
915 * Setcaps function for video sink pad.
917 * Returns: #TRUE on success.
920 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
922 GstMatroskaTrackContext *context = NULL;
923 GstMatroskaTrackVideoContext *videocontext;
925 GstMatroskaPad *collect_pad;
926 GstStructure *structure;
927 const gchar *mimetype;
928 const gchar *interlace_mode;
929 const GValue *value = NULL;
930 GstBuffer *codec_buf = NULL;
931 gint width, height, pixel_width, pixel_height;
934 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
937 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
938 g_assert (collect_pad);
939 context = collect_pad->track;
941 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
942 videocontext = (GstMatroskaTrackVideoContext *) context;
944 /* gst -> matroska ID'ing */
945 structure = gst_caps_get_structure (caps, 0);
947 mimetype = gst_structure_get_name (structure);
949 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
950 if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
951 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
953 if (!strcmp (mimetype, "video/x-theora")) {
954 /* we'll extract the details later from the theora identification header */
958 /* get general properties */
959 /* spec says it is mandatory */
960 if (!gst_structure_get_int (structure, "width", &width) ||
961 !gst_structure_get_int (structure, "height", &height))
964 videocontext->pixel_width = width;
965 videocontext->pixel_height = height;
967 /* set vp8 defaults or let user override it */
968 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
969 && (!strcmp (mimetype, "video/x-vp8")
970 || !strcmp (mimetype, "video/x-vp9")))
971 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
972 DEFAULT_PAD_FRAME_DURATION_VP8;
974 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
975 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
977 context->default_duration =
978 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
979 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
980 GST_TIME_ARGS (context->default_duration));
982 context->default_duration = 0;
984 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
985 &pixel_width, &pixel_height)) {
986 if (pixel_width > pixel_height) {
987 videocontext->display_width = width * pixel_width / pixel_height;
988 videocontext->display_height = height;
989 } else if (pixel_width < pixel_height) {
990 videocontext->display_width = width;
991 videocontext->display_height = height * pixel_height / pixel_width;
993 videocontext->display_width = 0;
994 videocontext->display_height = 0;
997 videocontext->display_width = 0;
998 videocontext->display_height = 0;
1003 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1004 videocontext->fourcc = 0;
1006 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1007 * data and other settings
1011 /* extract codec_data, may turn out needed */
1012 value = gst_structure_get_value (structure, "codec_data");
1014 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1017 if (!strcmp (mimetype, "video/x-raw")) {
1019 gst_matroska_mux_set_codec_id (context,
1020 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1021 fstr = gst_structure_get_string (structure, "format");
1022 if (fstr && strlen (fstr) == 4)
1023 videocontext->fourcc = GST_STR_FOURCC (fstr);
1024 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1025 ||!strcmp (mimetype, "video/x-divx")
1026 || !strcmp (mimetype, "video/x-dv")
1027 || !strcmp (mimetype, "video/x-h263")
1028 || !strcmp (mimetype, "video/x-msmpeg")
1029 || !strcmp (mimetype, "video/x-wmv")
1030 || !strcmp (mimetype, "image/jpeg")) {
1031 gst_riff_strf_vids *bih;
1032 gint size = sizeof (gst_riff_strf_vids);
1035 if (!strcmp (mimetype, "video/x-huffyuv"))
1036 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1037 else if (!strcmp (mimetype, "video/x-dv"))
1038 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1039 else if (!strcmp (mimetype, "video/x-h263"))
1040 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1041 else if (!strcmp (mimetype, "video/x-divx")) {
1044 gst_structure_get_int (structure, "divxversion", &divxversion);
1045 switch (divxversion) {
1047 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1050 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1053 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1056 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1059 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1060 switch (msmpegversion) {
1062 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1065 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1071 } else if (!strcmp (mimetype, "video/x-wmv")) {
1075 fstr = gst_structure_get_string (structure, "format");
1076 if (fstr && strlen (fstr) == 4) {
1077 fourcc = GST_STR_FOURCC (fstr);
1078 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1079 if (wmvversion == 2) {
1080 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1081 } else if (wmvversion == 1) {
1082 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1083 } else if (wmvversion == 3) {
1084 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1087 } else if (!strcmp (mimetype, "image/jpeg")) {
1088 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1094 bih = g_new0 (gst_riff_strf_vids, 1);
1095 GST_WRITE_UINT32_LE (&bih->size, size);
1096 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1097 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1098 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1099 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1100 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1101 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1102 videocontext->pixel_height * 3);
1104 /* process codec private/initialization data, if any */
1106 size += gst_buffer_get_size (codec_buf);
1107 bih = g_realloc (bih, size);
1108 GST_WRITE_UINT32_LE (&bih->size, size);
1109 gst_buffer_extract (codec_buf, 0,
1110 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1113 gst_matroska_mux_set_codec_id (context,
1114 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1115 gst_matroska_mux_free_codec_priv (context);
1116 context->codec_priv = (gpointer) bih;
1117 context->codec_priv_size = size;
1118 } else if (!strcmp (mimetype, "video/x-h264")) {
1119 gst_matroska_mux_set_codec_id (context,
1120 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1121 gst_matroska_mux_free_codec_priv (context);
1122 /* Create avcC header */
1123 if (codec_buf != NULL) {
1124 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1125 context->codec_priv = g_malloc0 (context->codec_priv_size);
1126 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1128 } else if (!strcmp (mimetype, "video/x-h265")) {
1129 gst_matroska_mux_set_codec_id (context,
1130 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1131 gst_matroska_mux_free_codec_priv (context);
1132 /* Create hvcC header */
1133 if (codec_buf != NULL) {
1134 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1135 context->codec_priv = g_malloc0 (context->codec_priv_size);
1136 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1138 } else if (!strcmp (mimetype, "video/x-theora")) {
1139 const GValue *streamheader;
1141 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1143 gst_matroska_mux_free_codec_priv (context);
1145 streamheader = gst_structure_get_value (structure, "streamheader");
1146 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1147 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1148 ("theora stream headers missing or malformed"));
1151 } else if (!strcmp (mimetype, "video/x-dirac")) {
1152 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1153 } else if (!strcmp (mimetype, "video/x-vp8")) {
1154 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1155 } else if (!strcmp (mimetype, "video/x-vp9")) {
1156 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1157 } else if (!strcmp (mimetype, "video/mpeg")) {
1160 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1161 switch (mpegversion) {
1163 gst_matroska_mux_set_codec_id (context,
1164 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1167 gst_matroska_mux_set_codec_id (context,
1168 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1171 gst_matroska_mux_set_codec_id (context,
1172 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1178 /* global headers may be in codec data */
1179 if (codec_buf != NULL) {
1180 gst_matroska_mux_free_codec_priv (context);
1181 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1182 context->codec_priv = g_malloc0 (context->codec_priv_size);
1183 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1185 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1187 /* can only make it here if preceding case verified it was version 3 */
1188 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1189 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1191 const GValue *mdpr_data;
1193 gst_structure_get_int (structure, "rmversion", &rmversion);
1194 switch (rmversion) {
1196 gst_matroska_mux_set_codec_id (context,
1197 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1200 gst_matroska_mux_set_codec_id (context,
1201 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1204 gst_matroska_mux_set_codec_id (context,
1205 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1208 gst_matroska_mux_set_codec_id (context,
1209 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1215 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1216 if (mdpr_data != NULL) {
1217 guint8 *priv_data = NULL;
1218 guint priv_data_size = 0;
1220 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1222 priv_data_size = gst_buffer_get_size (codec_data_buf);
1223 priv_data = g_malloc0 (priv_data_size);
1225 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1227 gst_matroska_mux_free_codec_priv (context);
1228 context->codec_priv = priv_data;
1229 context->codec_priv_size = priv_data_size;
1238 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1239 GST_PAD_NAME (pad), caps);
1244 /* N > 0 to expect a particular number of headers, negative if the
1245 number of headers is variable */
1247 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1248 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1250 GstBuffer **buf = NULL;
1253 guint bufi, i, offset, priv_data_size;
1255 if (streamheader == NULL)
1256 goto no_stream_headers;
1258 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1261 bufarr = g_value_peek_pointer (streamheader);
1262 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1264 if (N > 0 && bufarr->len != N)
1267 context->xiph_headers_to_skip = bufarr->len;
1269 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1270 for (i = 0; i < bufarr->len; i++) {
1271 GValue *bufval = &g_array_index (bufarr, GValue, i);
1273 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1275 goto wrong_content_type;
1278 buf[i] = g_value_peek_pointer (bufval);
1282 if (bufarr->len > 0) {
1283 for (i = 0; i < bufarr->len - 1; i++) {
1284 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1288 for (i = 0; i < bufarr->len; ++i) {
1289 priv_data_size += gst_buffer_get_size (buf[i]);
1292 priv_data = g_malloc0 (priv_data_size);
1294 priv_data[0] = bufarr->len - 1;
1297 if (bufarr->len > 0) {
1298 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1299 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1300 priv_data[offset++] = 0xff;
1302 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1306 for (i = 0; i < bufarr->len; ++i) {
1307 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1308 offset += gst_buffer_get_size (buf[i]);
1311 gst_matroska_mux_free_codec_priv (context);
1312 context->codec_priv = priv_data;
1313 context->codec_priv_size = priv_data_size;
1316 *p_buf0 = gst_buffer_ref (buf[0]);
1325 GST_WARNING ("required streamheaders missing in sink caps!");
1330 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1331 G_VALUE_TYPE_NAME (streamheader));
1336 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1341 GST_WARNING ("streamheaders array does not contain GstBuffers");
1347 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1348 GstMatroskaTrackContext * context)
1350 GstBuffer *buf0 = NULL;
1352 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1355 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1356 GST_WARNING ("First vorbis header too small, ignoring");
1358 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1359 GstMatroskaTrackAudioContext *audiocontext;
1363 gst_buffer_map (buf0, &map, GST_MAP_READ);
1364 hdr = map.data + 1 + 6 + 4;
1365 audiocontext = (GstMatroskaTrackAudioContext *) context;
1366 audiocontext->channels = GST_READ_UINT8 (hdr);
1367 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1368 gst_buffer_unmap (buf0, &map);
1373 gst_buffer_unref (buf0);
1379 theora_streamheader_to_codecdata (const GValue * streamheader,
1380 GstMatroskaTrackContext * context)
1382 GstBuffer *buf0 = NULL;
1384 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1387 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1388 GST_WARNING ("First theora header too small, ignoring");
1389 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1390 GST_WARNING ("First header not a theora identification header, ignoring");
1392 GstMatroskaTrackVideoContext *videocontext;
1393 guint fps_num, fps_denom, par_num, par_denom;
1397 gst_buffer_map (buf0, &map, GST_MAP_READ);
1398 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1400 videocontext = (GstMatroskaTrackVideoContext *) context;
1401 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1402 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1403 hdr += 3 + 3 + 1 + 1;
1404 fps_num = GST_READ_UINT32_BE (hdr);
1405 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1406 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1407 fps_denom, fps_num);
1409 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1410 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1411 if (par_num > 0 && par_num > 0) {
1412 if (par_num > par_denom) {
1413 videocontext->display_width =
1414 videocontext->pixel_width * par_num / par_denom;
1415 videocontext->display_height = videocontext->pixel_height;
1416 } else if (par_num < par_denom) {
1417 videocontext->display_width = videocontext->pixel_width;
1418 videocontext->display_height =
1419 videocontext->pixel_height * par_denom / par_num;
1421 videocontext->display_width = 0;
1422 videocontext->display_height = 0;
1425 videocontext->display_width = 0;
1426 videocontext->display_height = 0;
1430 gst_buffer_unmap (buf0, &map);
1434 gst_buffer_unref (buf0);
1440 kate_streamheader_to_codecdata (const GValue * streamheader,
1441 GstMatroskaTrackContext * context)
1443 GstBuffer *buf0 = NULL;
1445 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1448 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1449 GST_WARNING ("First kate header too small, ignoring");
1450 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1451 GST_WARNING ("First header not a kate identification header, ignoring");
1455 gst_buffer_unref (buf0);
1461 flac_streamheader_to_codecdata (const GValue * streamheader,
1462 GstMatroskaTrackContext * context)
1469 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1470 GST_WARNING ("No or invalid streamheader field in the caps");
1474 bufarr = g_value_peek_pointer (streamheader);
1475 if (bufarr->len < 2) {
1476 GST_WARNING ("Too few headers in streamheader field");
1480 context->xiph_headers_to_skip = bufarr->len + 1;
1482 bufval = &g_array_index (bufarr, GValue, 0);
1483 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1484 GST_WARNING ("streamheaders array does not contain GstBuffers");
1488 buffer = g_value_peek_pointer (bufval);
1490 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1491 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1492 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1493 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1494 GST_WARNING ("Invalid streamheader for FLAC");
1498 gst_matroska_mux_free_codec_priv (context);
1499 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1500 context->codec_priv = g_malloc (context->codec_priv_size);
1501 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1503 for (i = 1; i < bufarr->len; i++) {
1505 bufval = &g_array_index (bufarr, GValue, i);
1507 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1508 gst_matroska_mux_free_codec_priv (context);
1509 GST_WARNING ("streamheaders array does not contain GstBuffers");
1513 buffer = g_value_peek_pointer (bufval);
1515 old_size = context->codec_priv_size;
1516 context->codec_priv_size += gst_buffer_get_size (buffer);
1518 context->codec_priv = g_realloc (context->codec_priv,
1519 context->codec_priv_size);
1520 gst_buffer_extract (buffer, 0,
1521 (guint8 *) context->codec_priv + old_size, -1);
1528 speex_streamheader_to_codecdata (const GValue * streamheader,
1529 GstMatroskaTrackContext * context)
1536 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1537 GST_WARNING ("No or invalid streamheader field in the caps");
1541 bufarr = g_value_peek_pointer (streamheader);
1542 if (bufarr->len != 2) {
1543 GST_WARNING ("Too few headers in streamheader field");
1547 context->xiph_headers_to_skip = bufarr->len + 1;
1549 bufval = &g_array_index (bufarr, GValue, 0);
1550 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1551 GST_WARNING ("streamheaders array does not contain GstBuffers");
1555 buffer = g_value_peek_pointer (bufval);
1557 if (gst_buffer_get_size (buffer) < 80
1558 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1559 GST_WARNING ("Invalid streamheader for Speex");
1563 gst_matroska_mux_free_codec_priv (context);
1564 context->codec_priv_size = gst_buffer_get_size (buffer);
1565 context->codec_priv = g_malloc (context->codec_priv_size);
1566 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1568 bufval = &g_array_index (bufarr, GValue, 1);
1570 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1571 gst_matroska_mux_free_codec_priv (context);
1572 GST_WARNING ("streamheaders array does not contain GstBuffers");
1576 buffer = g_value_peek_pointer (bufval);
1578 old_size = context->codec_priv_size;
1579 context->codec_priv_size += gst_buffer_get_size (buffer);
1580 context->codec_priv = g_realloc (context->codec_priv,
1581 context->codec_priv_size);
1582 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1587 static const gchar *
1588 aac_codec_data_to_codec_id (GstBuffer * buf)
1590 const gchar *result;
1593 /* default to MAIN */
1596 if (gst_buffer_get_size (buf) >= 2) {
1597 gst_buffer_extract (buf, 0, &profile, 1);
1615 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1624 * gst_matroska_mux_audio_pad_setcaps:
1625 * @pad: Pad which got the caps.
1628 * Setcaps function for audio sink pad.
1630 * Returns: #TRUE on success.
1633 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1635 GstMatroskaTrackContext *context = NULL;
1636 GstMatroskaTrackAudioContext *audiocontext;
1637 GstMatroskaMux *mux;
1638 GstMatroskaPad *collect_pad;
1639 const gchar *mimetype;
1640 gint samplerate = 0, channels = 0;
1641 GstStructure *structure;
1642 const GValue *codec_data = NULL;
1643 GstBuffer *buf = NULL;
1644 const gchar *stream_format = NULL;
1646 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1649 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1650 g_assert (collect_pad);
1651 context = collect_pad->track;
1653 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1654 audiocontext = (GstMatroskaTrackAudioContext *) context;
1656 structure = gst_caps_get_structure (caps, 0);
1657 mimetype = gst_structure_get_name (structure);
1660 gst_structure_get_int (structure, "rate", &samplerate);
1661 gst_structure_get_int (structure, "channels", &channels);
1663 audiocontext->samplerate = samplerate;
1664 audiocontext->channels = channels;
1665 audiocontext->bitdepth = 0;
1666 context->default_duration = 0;
1668 codec_data = gst_structure_get_value (structure, "codec_data");
1670 buf = gst_value_get_buffer (codec_data);
1672 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1673 * data and other settings
1677 if (!strcmp (mimetype, "audio/mpeg")) {
1678 gint mpegversion = 0;
1680 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1681 switch (mpegversion) {
1687 gst_structure_get_int (structure, "layer", &layer);
1689 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1690 GST_WARNING_OBJECT (mux,
1691 "Unable to determine MPEG audio version, assuming 1");
1697 else if (layer == 2)
1699 else if (version == 2)
1704 context->default_duration =
1705 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1709 gst_matroska_mux_set_codec_id (context,
1710 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1713 gst_matroska_mux_set_codec_id (context,
1714 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1717 gst_matroska_mux_set_codec_id (context,
1718 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1727 stream_format = gst_structure_get_string (structure, "stream-format");
1728 /* check this is raw aac */
1729 if (stream_format) {
1730 if (strcmp (stream_format, "raw") != 0) {
1731 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1735 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1740 if (mpegversion == 2)
1742 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1743 aac_codec_data_to_codec_id (buf));
1744 else if (mpegversion == 4)
1746 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1747 aac_codec_data_to_codec_id (buf));
1749 g_assert_not_reached ();
1751 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1758 } else if (!strcmp (mimetype, "audio/x-raw")) {
1761 gst_audio_info_init (&info);
1762 if (!gst_audio_info_from_caps (&info, caps)) {
1763 GST_DEBUG_OBJECT (mux,
1764 "broken caps, rejected by gst_audio_info_from_caps");
1768 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1769 case GST_AUDIO_FORMAT_U8:
1770 case GST_AUDIO_FORMAT_S16BE:
1771 case GST_AUDIO_FORMAT_S16LE:
1772 case GST_AUDIO_FORMAT_S24BE:
1773 case GST_AUDIO_FORMAT_S24LE:
1774 case GST_AUDIO_FORMAT_S32BE:
1775 case GST_AUDIO_FORMAT_S32LE:
1776 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1777 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1780 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1781 gst_matroska_mux_set_codec_id (context,
1782 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1784 gst_matroska_mux_set_codec_id (context,
1785 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1787 case GST_AUDIO_FORMAT_F32LE:
1788 case GST_AUDIO_FORMAT_F64LE:
1789 gst_matroska_mux_set_codec_id (context,
1790 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1794 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1798 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1799 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1800 const GValue *streamheader;
1802 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1804 gst_matroska_mux_free_codec_priv (context);
1806 streamheader = gst_structure_get_value (structure, "streamheader");
1807 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1808 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1809 ("vorbis stream headers missing or malformed"));
1812 } else if (!strcmp (mimetype, "audio/x-flac")) {
1813 const GValue *streamheader;
1815 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1817 gst_matroska_mux_free_codec_priv (context);
1819 streamheader = gst_structure_get_value (structure, "streamheader");
1820 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1821 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1822 ("flac stream headers missing or malformed"));
1825 } else if (!strcmp (mimetype, "audio/x-speex")) {
1826 const GValue *streamheader;
1828 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1829 gst_matroska_mux_free_codec_priv (context);
1831 streamheader = gst_structure_get_value (structure, "streamheader");
1832 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1833 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1834 ("speex stream headers missing or malformed"));
1837 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1838 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1839 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1840 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1841 } else if (!strcmp (mimetype, "audio/x-dts")) {
1842 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1843 } else if (!strcmp (mimetype, "audio/x-tta")) {
1846 /* TTA frame duration */
1847 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1849 gst_structure_get_int (structure, "width", &width);
1850 audiocontext->bitdepth = width;
1851 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1853 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1855 const GValue *mdpr_data;
1857 gst_structure_get_int (structure, "raversion", &raversion);
1858 switch (raversion) {
1860 gst_matroska_mux_set_codec_id (context,
1861 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1864 gst_matroska_mux_set_codec_id (context,
1865 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1868 gst_matroska_mux_set_codec_id (context,
1869 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1875 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1876 if (mdpr_data != NULL) {
1877 guint8 *priv_data = NULL;
1878 guint priv_data_size = 0;
1880 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1882 priv_data_size = gst_buffer_get_size (codec_data_buf);
1883 priv_data = g_malloc0 (priv_data_size);
1885 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1887 gst_matroska_mux_free_codec_priv (context);
1889 context->codec_priv = priv_data;
1890 context->codec_priv_size = priv_data_size;
1893 } else if (!strcmp (mimetype, "audio/x-wma")
1894 || !strcmp (mimetype, "audio/x-alaw")
1895 || !strcmp (mimetype, "audio/x-mulaw")
1896 || !strcmp (mimetype, "audio/x-adpcm")) {
1898 guint codec_priv_size;
1903 if (samplerate == 0 || channels == 0) {
1904 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1908 if (!strcmp (mimetype, "audio/x-wma")) {
1912 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1913 || !gst_structure_get_int (structure, "block_align", &block_align)
1914 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1915 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1920 switch (wmaversion) {
1922 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1925 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1928 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1931 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1935 if (gst_structure_get_int (structure, "depth", &depth))
1936 audiocontext->bitdepth = depth;
1937 } else if (!strcmp (mimetype, "audio/x-alaw")
1938 || !strcmp (mimetype, "audio/x-mulaw")) {
1939 audiocontext->bitdepth = 8;
1940 if (!strcmp (mimetype, "audio/x-alaw"))
1941 format = GST_RIFF_WAVE_FORMAT_ALAW;
1943 format = GST_RIFF_WAVE_FORMAT_MULAW;
1945 block_align = channels;
1946 bitrate = block_align * samplerate;
1947 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
1950 layout = gst_structure_get_string (structure, "layout");
1952 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
1956 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
1957 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
1961 if (!strcmp (layout, "dvi")) {
1962 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
1963 } else if (!strcmp (layout, "g726")) {
1964 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
1965 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
1966 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
1970 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
1975 g_assert (format != 0);
1977 codec_priv_size = WAVEFORMATEX_SIZE;
1979 codec_priv_size += gst_buffer_get_size (buf);
1981 /* serialize waveformatex structure */
1982 codec_priv = g_malloc0 (codec_priv_size);
1983 GST_WRITE_UINT16_LE (codec_priv, format);
1984 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1985 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1986 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1987 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1988 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1990 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1992 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1994 /* process codec private/initialization data, if any */
1996 gst_buffer_extract (buf, 0,
1997 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2000 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2001 gst_matroska_mux_free_codec_priv (context);
2002 context->codec_priv = (gpointer) codec_priv;
2003 context->codec_priv_size = codec_priv_size;
2011 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2012 GST_PAD_NAME (pad), caps);
2017 /* we probably don't have the data at start,
2018 * so have to reserve (a maximum) space to write this at the end.
2019 * bit spacy, but some formats can hold quite some */
2020 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2023 * gst_matroska_mux_subtitle_pad_setcaps:
2024 * @pad: Pad which got the caps.
2027 * Setcaps function for subtitle sink pad.
2029 * Returns: #TRUE on success.
2032 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2034 /* There is now (at least) one such alement (kateenc), and I'm going
2035 to handle it here and claim it works when it can be piped back
2036 through GStreamer and VLC */
2038 GstMatroskaTrackContext *context = NULL;
2039 GstMatroskaTrackSubtitleContext *scontext;
2040 GstMatroskaMux *mux;
2041 GstMatroskaPad *collect_pad;
2042 const gchar *mimetype;
2043 GstStructure *structure;
2044 const GValue *value = NULL;
2045 GstBuffer *buf = NULL;
2046 gboolean ret = TRUE;
2048 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2051 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2052 g_assert (collect_pad);
2053 context = collect_pad->track;
2055 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2056 scontext = (GstMatroskaTrackSubtitleContext *) context;
2058 structure = gst_caps_get_structure (caps, 0);
2059 mimetype = gst_structure_get_name (structure);
2062 scontext->check_utf8 = 1;
2063 scontext->invalid_utf8 = 0;
2064 context->default_duration = 0;
2066 if (!strcmp (mimetype, "subtitle/x-kate")) {
2067 const GValue *streamheader;
2069 gst_matroska_mux_set_codec_id (context,
2070 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2072 gst_matroska_mux_free_codec_priv (context);
2074 streamheader = gst_structure_get_value (structure, "streamheader");
2075 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2076 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2077 ("kate stream headers missing or malformed"));
2081 } else if (!strcmp (mimetype, "text/x-raw")) {
2082 gst_matroska_mux_set_codec_id (context,
2083 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2084 } else if (!strcmp (mimetype, "application/x-ssa")) {
2085 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2086 } else if (!strcmp (mimetype, "application/x-ass")) {
2087 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2088 } else if (!strcmp (mimetype, "application/x-usf")) {
2089 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2090 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2091 gst_matroska_mux_set_codec_id (context,
2092 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2098 /* maybe some private data, e.g. vobsub */
2099 value = gst_structure_get_value (structure, "codec_data");
2101 buf = gst_value_get_buffer (value);
2104 guint8 *priv_data = NULL;
2106 gst_buffer_map (buf, &map, GST_MAP_READ);
2108 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2109 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2110 " exceeded maximum (%d); discarding", pad,
2111 SUBTITLE_MAX_CODEC_PRIVATE);
2112 gst_buffer_unmap (buf, &map);
2116 gst_matroska_mux_free_codec_priv (context);
2118 priv_data = g_malloc0 (map.size);
2119 memcpy (priv_data, map.data, map.size);
2120 context->codec_priv = priv_data;
2121 context->codec_priv_size = map.size;
2122 gst_buffer_unmap (buf, &map);
2125 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2126 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2135 * gst_matroska_mux_request_new_pad:
2136 * @element: #GstMatroskaMux.
2137 * @templ: #GstPadTemplate.
2138 * @pad_name: New pad name.
2140 * Request pad function for sink templates.
2142 * Returns: New #GstPad.
2145 gst_matroska_mux_request_new_pad (GstElement * element,
2146 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2148 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2149 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2150 GstMatroskaPad *collect_pad;
2151 GstMatroskamuxPad *newpad;
2153 const gchar *pad_name = NULL;
2154 GstMatroskaCapsFunc capsfunc = NULL;
2155 GstMatroskaTrackContext *context = NULL;
2157 gboolean locked = TRUE;
2160 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2161 /* don't mix named and unnamed pads, if the pad already exists we fail when
2162 * trying to add it */
2163 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2164 pad_name = req_name;
2166 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2169 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2170 context = (GstMatroskaTrackContext *)
2171 g_new0 (GstMatroskaTrackAudioContext, 1);
2172 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2173 context->name = g_strdup ("Audio");
2174 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2175 /* don't mix named and unnamed pads, if the pad already exists we fail when
2176 * trying to add it */
2177 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2178 pad_name = req_name;
2180 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2183 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2184 context = (GstMatroskaTrackContext *)
2185 g_new0 (GstMatroskaTrackVideoContext, 1);
2186 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2187 context->name = g_strdup ("Video");
2188 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2189 /* don't mix named and unnamed pads, if the pad already exists we fail when
2190 * trying to add it */
2191 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2192 pad_name = req_name;
2194 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2197 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2198 context = (GstMatroskaTrackContext *)
2199 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2200 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2201 context->name = g_strdup ("Subtitle");
2202 /* setcaps may only provide proper one a lot later */
2203 id = g_strdup ("S_SUB_UNKNOWN");
2206 GST_WARNING_OBJECT (mux, "This is not our template!");
2210 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2211 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2214 gst_matroskamux_pad_init (newpad);
2215 collect_pad = (GstMatroskaPad *)
2216 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2217 sizeof (GstMatroskamuxPad),
2218 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2220 collect_pad->track = context;
2221 gst_matroska_pad_reset (collect_pad, FALSE);
2222 collect_pad->track->codec_id = id;
2224 collect_pad->capsfunc = capsfunc;
2225 gst_pad_set_active (GST_PAD (newpad), TRUE);
2226 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2227 goto pad_add_failed;
2231 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2233 return GST_PAD (newpad);
2238 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2239 gst_object_unref (newpad);
2245 * gst_matroska_mux_release_pad:
2246 * @element: #GstMatroskaMux.
2247 * @pad: Pad to release.
2249 * Release a previously requested pad.
2252 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2254 GstMatroskaMux *mux;
2257 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2259 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2260 GstCollectData *cdata = (GstCollectData *) walk->data;
2261 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2263 if (cdata->pad == pad) {
2264 GstClockTime min_dur; /* observed minimum duration */
2266 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2267 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2268 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2269 if (collect_pad->duration < min_dur)
2270 collect_pad->duration = min_dur;
2273 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2274 mux->duration < collect_pad->duration)
2275 mux->duration = collect_pad->duration;
2281 gst_collect_pads_remove_pad (mux->collect, pad);
2282 if (gst_element_remove_pad (element, pad))
2288 * gst_matroska_mux_track_header:
2289 * @mux: #GstMatroskaMux
2290 * @context: Tack context.
2292 * Write a track header.
2295 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2296 GstMatroskaTrackContext * context)
2298 GstEbmlWrite *ebml = mux->ebml_write;
2301 /* TODO: check if everything necessary is written and check default values */
2303 /* track type goes before the type-specific stuff */
2304 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2305 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2307 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2308 gst_matroska_mux_create_uid (mux));
2309 if (context->default_duration) {
2310 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2311 context->default_duration);
2313 if (context->language) {
2314 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2318 /* FIXME: until we have a nice way of getting the codecname
2319 * out of the caps, I'm not going to enable this. Too much
2320 * (useless, double, boring) work... */
2321 /* TODO: Use value from tags if any */
2322 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2323 context->codec_name); */
2324 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2326 /* type-specific stuff */
2327 switch (context->type) {
2328 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2329 GstMatroskaTrackVideoContext *videocontext =
2330 (GstMatroskaTrackVideoContext *) context;
2332 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2333 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2334 videocontext->pixel_width);
2335 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2336 videocontext->pixel_height);
2337 if (videocontext->display_width && videocontext->display_height) {
2338 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2339 videocontext->display_width);
2340 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2341 videocontext->display_height);
2343 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2344 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2345 if (videocontext->fourcc) {
2346 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2348 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2349 (gpointer) & fcc_le, 4);
2351 gst_ebml_write_master_finish (ebml, master);
2356 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2357 GstMatroskaTrackAudioContext *audiocontext =
2358 (GstMatroskaTrackAudioContext *) context;
2360 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2361 if (audiocontext->samplerate != 8000)
2362 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2363 audiocontext->samplerate);
2364 if (audiocontext->channels != 1)
2365 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2366 audiocontext->channels);
2367 if (audiocontext->bitdepth) {
2368 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2369 audiocontext->bitdepth);
2371 gst_ebml_write_master_finish (ebml, master);
2376 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2380 /* doesn't need type-specific data */
2384 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2385 if (context->codec_priv)
2386 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2387 context->codec_priv, context->codec_priv_size);
2392 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2394 guint64 title_master;
2397 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2399 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2400 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2401 GST_MATROSKA_MUX_CHAPLANG);
2403 gst_ebml_write_master_finish (ebml, title_master);
2407 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2408 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2409 guint64 * master_edition)
2411 guint64 uid, master_chapteratom;
2413 GstTocEntry *cur_entry;
2418 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2420 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2422 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2423 /* create uid for the parent */
2424 uid = gst_matroska_mux_create_uid ();
2425 g_free (edition->uid);
2426 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2429 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2431 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2432 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2433 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2434 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2437 uid = gst_matroska_mux_create_uid ();
2438 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2440 master_chapteratom =
2441 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2442 g_free (entry->uid);
2443 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2444 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2445 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2446 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2447 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2448 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2450 cur = entry->subentries;
2451 while (cur != NULL) {
2452 cur_entry = cur->data;
2453 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2458 if (G_LIKELY (entry->tags != NULL)) {
2459 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2461 for (i = 0; i < count; ++i) {
2462 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2463 gst_matroska_mux_write_chapter_title (title, ebml);
2467 /* remove title tag */
2468 if (G_LIKELY (count > 0))
2469 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2472 gst_ebml_write_master_finish (ebml, master_chapteratom);
2476 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2477 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2479 guint64 master_edition = 0;
2481 GstTocEntry *subentry;
2483 cur = gst_toc_entry_get_sub_entries (entry);
2484 while (cur != NULL) {
2485 subentry = cur->data;
2486 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2492 if (G_LIKELY (master_edition != 0))
2493 gst_ebml_write_master_finish (ebml, master_edition);
2498 * gst_matroska_mux_start:
2499 * @mux: #GstMatroskaMux
2501 * Start a new matroska file (write headers etc...)
2504 gst_matroska_mux_start (GstMatroskaMux * mux)
2506 GstEbmlWrite *ebml = mux->ebml_write;
2507 const gchar *doctype;
2508 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2509 GST_MATROSKA_ID_TRACKS,
2510 GST_MATROSKA_ID_CHAPTERS,
2511 GST_MATROSKA_ID_CUES,
2512 GST_MATROSKA_ID_TAGS,
2515 const gchar *media_type;
2516 gboolean audio_only;
2517 guint64 master, child;
2521 GstClockTime duration = 0;
2522 guint32 segment_uid[4];
2523 GTimeVal time = { 0, 0 };
2529 /* if not streaming, check if downstream is seekable */
2530 if (!mux->streamable) {
2534 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2535 if (gst_pad_peer_query (mux->srcpad, query)) {
2536 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2537 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2539 /* have to assume seeking is supported if query not handled downstream */
2540 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2544 mux->streamable = TRUE;
2545 g_object_notify (G_OBJECT (mux), "streamable");
2546 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2547 "streamable=false. Will ignore that and create streamable output "
2550 gst_query_unref (query);
2553 /* stream-start (FIXME: create id based on input ids) */
2554 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
2555 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
2558 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
2560 media_type = (audio_only) ? "audio/webm" : "video/webm";
2562 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
2564 ebml->caps = gst_caps_new_empty_simple (media_type);
2565 gst_pad_set_caps (mux->srcpad, ebml->caps);
2566 /* we start with a EBML header */
2567 doctype = mux->doctype;
2568 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2569 doctype, mux->doctype_version);
2570 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2572 /* the rest of the header is cached */
2573 gst_ebml_write_set_cache (ebml, 0x1000);
2575 /* start a segment */
2577 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2578 mux->segment_master = ebml->pos;
2580 if (!mux->streamable) {
2581 /* seekhead (table of contents) - we set the positions later */
2582 mux->seekhead_pos = ebml->pos;
2583 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2584 for (i = 0; seekhead_id[i] != 0; i++) {
2585 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2586 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2587 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2588 gst_ebml_write_master_finish (ebml, child);
2590 gst_ebml_write_master_finish (ebml, master);
2593 if (mux->streamable) {
2594 const GstTagList *tags;
2597 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2599 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2600 guint64 master_tags, master_tag;
2602 GST_DEBUG_OBJECT (mux, "Writing tags");
2604 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2605 mux->tags_pos = ebml->pos;
2606 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2607 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2608 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2609 gst_ebml_write_master_finish (ebml, master_tag);
2610 gst_ebml_write_master_finish (ebml, master_tags);
2615 mux->info_pos = ebml->pos;
2616 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2618 /* WebM does not support SegmentUID field on SegmentInfo */
2619 if (!mux->is_webm) {
2620 for (i = 0; i < 4; i++) {
2621 segment_uid[i] = g_random_int ();
2623 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2624 (guint8 *) segment_uid, 16);
2627 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2628 mux->duration_pos = ebml->pos;
2630 if (!mux->streamable) {
2631 for (collected = mux->collect->data; collected;
2632 collected = g_slist_next (collected)) {
2633 GstMatroskaPad *collect_pad;
2635 gint64 trackduration;
2637 collect_pad = (GstMatroskaPad *) collected->data;
2638 thepad = collect_pad->collect.pad;
2640 /* Query the total length of the track. */
2641 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2642 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2643 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2644 GST_TIME_ARGS (trackduration));
2645 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2646 duration = (GstClockTime) trackduration;
2650 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2651 gst_guint64_to_gdouble (duration) /
2652 gst_guint64_to_gdouble (mux->time_scale));
2654 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2655 "GStreamer plugin version " PACKAGE_VERSION);
2656 if (mux->writing_app && mux->writing_app[0]) {
2657 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2659 g_get_current_time (&time);
2660 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2661 gst_ebml_write_master_finish (ebml, master);
2664 mux->tracks_pos = ebml->pos;
2665 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2667 for (collected = mux->collect->data; collected;
2668 collected = g_slist_next (collected)) {
2669 GstMatroskaPad *collect_pad;
2672 collect_pad = (GstMatroskaPad *) collected->data;
2673 thepad = collect_pad->collect.pad;
2675 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2676 collect_pad->track->codec_id != 0) {
2677 collect_pad->track->num = tracknum++;
2678 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2679 gst_matroska_mux_track_header (mux, collect_pad->track);
2680 gst_ebml_write_master_finish (ebml, child);
2681 /* some remaining pad/track setup */
2682 collect_pad->default_duration_scaled =
2683 gst_util_uint64_scale (collect_pad->track->default_duration,
2684 1, mux->time_scale);
2687 gst_ebml_write_master_finish (ebml, master);
2689 /* FIXME: Check if we get a TOC that is supported by Matroska
2690 * and clean up the code below */
2693 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2694 if (toc != NULL && !mux->streamable) {
2695 guint64 master_chapters = 0;
2696 GstTocEntry *toc_entry;
2697 GList *cur, *to_write = NULL;
2700 GST_DEBUG ("Writing chapters");
2702 /* check whether we have editions or chapters at the root level */
2703 toc_entry = toc->entries->data;
2705 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2706 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2707 gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
2709 /* aggregate all chapters without root edition */
2710 cur = gst_toc_get_entries (toc);
2711 while (cur != NULL) {
2712 toc_entry->subentries =
2713 g_list_prepend (toc_entry->subentries, cur->data);
2717 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2718 toc_entry->subentries->data), &start, NULL);
2719 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2720 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2721 toc_entry->subentries->data), NULL, &stop);
2722 gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
2724 to_write = g_list_append (to_write, toc_entry);
2727 to_write = toc->entries;
2730 /* finally write chapters */
2731 mux->chapters_pos = ebml->pos;
2734 while (cur != NULL) {
2735 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2740 /* close master element if any edition was written */
2741 if (G_LIKELY (master_chapters != 0))
2742 gst_ebml_write_master_finish (ebml, master_chapters);
2744 if (toc_entry != NULL) {
2745 g_list_free (toc_entry->subentries);
2746 toc_entry->subentries = NULL;
2747 gst_toc_entry_unref (toc_entry);
2748 g_list_free (to_write);
2753 /* lastly, flush the cache */
2754 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2758 gst_toc_unref (toc);
2763 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2766 /* TODO: more sensible tag mappings */
2769 const gchar *matroska_tagname;
2770 const gchar *gstreamer_tagname;
2774 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2775 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2776 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2777 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2778 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2779 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2780 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2781 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2782 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2783 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2784 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2785 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2786 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2787 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2788 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2790 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2792 guint64 simpletag_master;
2794 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2795 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2796 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2798 if (strcmp (tagname_gst, tag) == 0) {
2799 GValue src = { 0, };
2802 if (!gst_tag_list_copy_value (&src, list, tag))
2804 if ((dest = gst_value_serialize (&src))) {
2806 simpletag_master = gst_ebml_write_master_start (ebml,
2807 GST_MATROSKA_ID_SIMPLETAG);
2808 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2809 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2810 gst_ebml_write_master_finish (ebml, simpletag_master);
2813 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2815 g_value_unset (&src);
2823 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2824 const GstTocEntry * entry, guint64 * master_tags)
2826 guint64 master_tag, master_targets;
2830 ebml = mux->ebml_write;
2832 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2833 if (*master_tags == 0) {
2834 mux->tags_pos = ebml->pos;
2835 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2838 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2840 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2842 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2843 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2844 g_ascii_strtoull (entry->uid, NULL, 10));
2846 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2847 g_ascii_strtoull (entry->uid, NULL, 10));
2849 gst_ebml_write_master_finish (ebml, master_targets);
2850 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2851 gst_ebml_write_master_finish (ebml, master_tag);
2854 cur = entry->subentries;
2855 while (cur != NULL) {
2856 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2863 * gst_matroska_mux_finish:
2864 * @mux: #GstMatroskaMux
2866 * Finish a new matroska file (write index etc...)
2869 gst_matroska_mux_finish (GstMatroskaMux * mux)
2871 GstEbmlWrite *ebml = mux->ebml_write;
2873 guint64 duration = 0;
2875 const GstTagList *tags;
2877 /* finish last cluster */
2879 gst_ebml_write_master_finish (ebml, mux->cluster);
2883 if (mux->index != NULL) {
2885 guint64 master, pointentry_master, trackpos_master;
2887 mux->cues_pos = ebml->pos;
2888 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2889 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2891 for (n = 0; n < mux->num_indexes; n++) {
2892 GstMatroskaIndex *idx = &mux->index[n];
2894 pointentry_master = gst_ebml_write_master_start (ebml,
2895 GST_MATROSKA_ID_POINTENTRY);
2896 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2897 idx->time / mux->time_scale);
2898 trackpos_master = gst_ebml_write_master_start (ebml,
2899 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2900 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2902 idx->pos - mux->segment_master);
2903 gst_ebml_write_master_finish (ebml, trackpos_master);
2904 gst_ebml_write_master_finish (ebml, pointentry_master);
2907 gst_ebml_write_master_finish (ebml, master);
2908 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2912 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2914 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2915 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2916 guint64 master_tags = 0, master_tag;
2921 GST_DEBUG_OBJECT (mux, "Writing tags");
2924 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2928 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2929 mux->tags_pos = ebml->pos;
2930 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2931 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2934 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2937 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2941 gst_ebml_write_master_finish (ebml, master_tag);
2946 while (cur != NULL) {
2947 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2953 if (master_tags != 0)
2954 gst_ebml_write_master_finish (ebml, master_tags);
2957 /* update seekhead. We know that:
2958 * - a seekhead contains 5 entries.
2959 * - order of entries is as above.
2960 * - a seekhead has a 4-byte header + 8-byte length
2961 * - each entry is 2-byte master, 2-byte ID pointer,
2962 * 2-byte length pointer, all 8/1-byte length, 4-
2963 * byte ID and 8-byte length pointer, where the
2964 * length pointer starts at 20.
2965 * - all entries are local to the segment (so pos - segment_master).
2966 * - so each entry is at 12 + 20 + num * 28. */
2967 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2968 mux->info_pos - mux->segment_master);
2969 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2970 mux->tracks_pos - mux->segment_master);
2971 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2972 && mux->chapters_pos > 0) {
2973 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2974 mux->chapters_pos - mux->segment_master);
2977 guint64 my_pos = ebml->pos;
2979 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2980 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2981 gst_ebml_write_seek (ebml, my_pos);
2983 if (mux->index != NULL) {
2984 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2985 mux->cues_pos - mux->segment_master);
2988 guint64 my_pos = ebml->pos;
2990 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2991 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2992 gst_ebml_write_seek (ebml, my_pos);
2996 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
2997 mux->tags_pos - mux->segment_master);
3000 guint64 my_pos = ebml->pos;
3002 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3003 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3004 gst_ebml_write_seek (ebml, my_pos);
3008 * - first get the overall duration
3009 * (a released track may have left a duration in here)
3010 * - write some track header data for subtitles
3012 duration = mux->duration;
3014 for (collected = mux->collect->data; collected;
3015 collected = g_slist_next (collected)) {
3016 GstMatroskaPad *collect_pad;
3017 GstClockTime min_duration; /* observed minimum duration */
3019 collect_pad = (GstMatroskaPad *) collected->data;
3021 GST_DEBUG_OBJECT (mux,
3022 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3023 " end ts %" GST_TIME_FORMAT, collect_pad,
3024 GST_TIME_ARGS (collect_pad->start_ts),
3025 GST_TIME_ARGS (collect_pad->end_ts));
3027 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3028 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3030 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3031 if (collect_pad->duration < min_duration)
3032 collect_pad->duration = min_duration;
3033 GST_DEBUG_OBJECT (collect_pad,
3034 "final track duration: %" GST_TIME_FORMAT,
3035 GST_TIME_ARGS (collect_pad->duration));
3038 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
3039 duration < collect_pad->duration)
3040 duration = collect_pad->duration;
3043 /* seek back (optional, but do anyway) */
3044 gst_ebml_write_seek (ebml, pos);
3046 /* update duration */
3047 if (duration != 0) {
3048 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3049 GST_TIME_ARGS (duration));
3050 pos = mux->ebml_write->pos;
3051 gst_ebml_write_seek (ebml, mux->duration_pos);
3052 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3053 gst_guint64_to_gdouble (duration) /
3054 gst_guint64_to_gdouble (mux->time_scale));
3055 gst_ebml_write_seek (ebml, pos);
3058 guint64 my_pos = ebml->pos;
3060 gst_ebml_write_seek (ebml, mux->duration_pos);
3061 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3062 gst_ebml_write_seek (ebml, my_pos);
3064 GST_DEBUG_OBJECT (mux, "finishing segment");
3065 /* finish segment - this also writes element length */
3066 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3070 * gst_matroska_mux_buffer_header:
3071 * @track: Track context.
3072 * @relative_timestamp: relative timestamp of the buffer
3073 * @flags: Buffer flags.
3075 * Create a buffer containing buffer header.
3077 * Returns: New buffer.
3080 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3081 gint16 relative_timestamp, int flags)
3084 guint8 *data = g_malloc (4);
3086 hdr = gst_buffer_new_wrapped (data, 4);
3087 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3088 data[0] = track->num | 0x80;
3089 /* time relative to clustertime */
3090 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3098 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3099 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3100 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3103 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3104 GstMatroskaPad * collect_pad, GstBuffer * buf)
3106 GstMatroskaTrackVideoContext *ctx =
3107 (GstMatroskaTrackVideoContext *) collect_pad->track;
3112 guint32 next_parse_offset;
3113 GstBuffer *ret = NULL;
3114 gboolean is_muxing_unit = FALSE;
3116 gst_buffer_map (buf, &map, GST_MAP_READ);
3121 gst_buffer_unmap (buf, &map);
3122 gst_buffer_unref (buf);
3126 /* Check if this buffer contains a picture or end-of-sequence packet */
3127 while (size >= 13) {
3128 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3129 gst_buffer_unmap (buf, &map);
3130 gst_buffer_unref (buf);
3134 parse_code = GST_READ_UINT8 (data + 4);
3135 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3136 if (ctx->dirac_unit) {
3137 gst_buffer_unref (ctx->dirac_unit);
3138 ctx->dirac_unit = NULL;
3140 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3141 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3142 is_muxing_unit = TRUE;
3146 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3148 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3151 data += next_parse_offset;
3152 size -= next_parse_offset;
3155 if (ctx->dirac_unit)
3156 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3158 ctx->dirac_unit = gst_buffer_ref (buf);
3160 gst_buffer_unmap (buf, &map);
3162 if (is_muxing_unit) {
3163 ret = gst_buffer_make_writable (ctx->dirac_unit);
3164 ctx->dirac_unit = NULL;
3165 gst_buffer_copy_into (ret, buf,
3166 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3167 gst_buffer_unref (buf);
3169 gst_buffer_unref (buf);
3177 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3181 GValue streamheader = { 0 };
3182 GValue bufval = { 0 };
3183 GstBuffer *streamheader_buffer;
3184 GstEbmlWrite *ebml = mux->ebml_write;
3186 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3187 caps = gst_caps_copy (mux->ebml_write->caps);
3188 s = gst_caps_get_structure (caps, 0);
3189 g_value_init (&streamheader, GST_TYPE_ARRAY);
3190 g_value_init (&bufval, GST_TYPE_BUFFER);
3191 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3192 gst_value_set_buffer (&bufval, streamheader_buffer);
3193 gst_value_array_append_value (&streamheader, &bufval);
3194 g_value_unset (&bufval);
3195 gst_structure_set_value (s, "streamheader", &streamheader);
3196 g_value_unset (&streamheader);
3197 gst_caps_replace (&ebml->caps, caps);
3198 gst_buffer_unref (streamheader_buffer);
3199 gst_pad_set_caps (mux->srcpad, caps);
3200 gst_caps_unref (caps);
3204 * gst_matroska_mux_write_data:
3205 * @mux: #GstMatroskaMux
3206 * @collect_pad: #GstMatroskaPad with the data
3208 * Write collected data (called from gst_matroska_mux_collected).
3210 * Returns: Result of the gst_pad_push issued to write the data.
3212 static GstFlowReturn
3213 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3216 GstEbmlWrite *ebml = mux->ebml_write;
3219 gboolean write_duration;
3220 gint16 relative_timestamp;
3221 gint64 relative_timestamp64;
3222 guint64 block_duration;
3223 gboolean is_video_keyframe = FALSE;
3224 gboolean is_video_invisible = FALSE;
3225 GstMatroskamuxPad *pad;
3229 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3231 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3232 if (collect_pad->track->xiph_headers_to_skip > 0) {
3233 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3234 gst_buffer_unref (buf);
3235 --collect_pad->track->xiph_headers_to_skip;
3239 /* for dirac we have to queue up everything up to a picture unit */
3240 if (collect_pad->track->codec_id != NULL &&
3241 strcmp (collect_pad->track->codec_id,
3242 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3243 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3248 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3249 * this would wreak havoc with time stored in matroska file */
3250 /* TODO: maybe calculate a timestamp by using the previous timestamp
3251 * and default duration */
3252 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3253 GST_WARNING_OBJECT (collect_pad->collect.pad,
3254 "Invalid buffer timestamp; dropping buffer");
3255 gst_buffer_unref (buf);
3259 /* set the timestamp for outgoing buffers */
3260 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3262 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3263 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3264 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3265 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3266 is_video_keyframe = TRUE;
3267 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3268 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3269 || !strcmp (collect_pad->track->codec_id,
3270 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3271 GST_LOG_OBJECT (mux,
3272 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3273 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3274 is_video_invisible = TRUE;
3279 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3280 * or when we may be reaching the limit of the relative timestamp */
3281 if (mux->cluster_time +
3282 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3283 || is_video_keyframe || mux->force_key_unit_event) {
3284 if (!mux->streamable)
3285 gst_ebml_write_master_finish (ebml, mux->cluster);
3287 /* Forward the GstForceKeyUnit event after finishing the cluster */
3288 if (mux->force_key_unit_event) {
3289 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3290 mux->force_key_unit_event = NULL;
3293 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3294 mux->cluster_pos = ebml->pos;
3295 gst_ebml_write_set_cache (ebml, 0x20);
3297 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3298 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3299 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3301 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3302 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3304 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3305 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3306 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3307 mux->prev_cluster_size);
3312 mux->cluster_pos = ebml->pos;
3313 gst_ebml_write_set_cache (ebml, 0x20);
3314 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3315 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3316 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3317 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3318 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3321 /* update duration of this track */
3322 if (GST_BUFFER_DURATION_IS_VALID (buf))
3323 collect_pad->duration += GST_BUFFER_DURATION (buf);
3325 /* We currently write index entries for all video tracks or for the audio
3326 * track in a single-track audio file. This could be improved by keeping the
3327 * index only for the *first* video track. */
3329 /* TODO: index is useful for every track, should contain the number of
3330 * the block in the cluster which contains the timestamp, should also work
3331 * for files with multiple audio tracks.
3333 if (!mux->streamable &&
3334 (is_video_keyframe ||
3335 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3336 (mux->num_streams == 1)))) {
3339 if (mux->min_index_interval != 0) {
3340 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3341 if (mux->index[last_idx].track == collect_pad->track->num)
3346 if (last_idx < 0 || mux->min_index_interval == 0 ||
3347 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3348 >= mux->min_index_interval)) {
3349 GstMatroskaIndex *idx;
3351 if (mux->num_indexes % 32 == 0) {
3352 mux->index = g_renew (GstMatroskaIndex, mux->index,
3353 mux->num_indexes + 32);
3355 idx = &mux->index[mux->num_indexes++];
3357 idx->pos = mux->cluster_pos;
3358 idx->time = GST_BUFFER_TIMESTAMP (buf);
3359 idx->track = collect_pad->track->num;
3363 /* Check if the duration differs from the default duration. */
3364 write_duration = FALSE;
3366 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3367 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3368 1, mux->time_scale);
3370 /* small difference should be ok. */
3371 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3372 block_duration < collect_pad->default_duration_scaled - 1) {
3373 write_duration = TRUE;
3377 /* write the block, for doctype v2 use SimpleBlock if possible
3378 * one slice (*breath*).
3379 * FIXME: Need to do correct lacing! */
3380 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3381 if (relative_timestamp64 >= 0) {
3382 /* round the timestamp */
3383 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3384 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3387 /* round the timestamp */
3388 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3389 relative_timestamp =
3390 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3394 if (is_video_invisible)
3397 if (mux->doctype_version > 1 && !write_duration) {
3398 if (is_video_keyframe)
3402 gst_matroska_mux_create_buffer_header (collect_pad->track,
3403 relative_timestamp, flags);
3404 gst_ebml_write_set_cache (ebml, 0x40);
3405 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3406 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3407 gst_ebml_write_buffer (ebml, hdr);
3408 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3409 gst_ebml_write_buffer (ebml, buf);
3411 return gst_ebml_last_write_result (ebml);
3413 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3414 /* write and call order slightly unnatural,
3415 * but avoids seek and minizes pushing */
3416 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3418 gst_matroska_mux_create_buffer_header (collect_pad->track,
3419 relative_timestamp, flags);
3421 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3422 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3423 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3424 gst_ebml_write_buffer (ebml, hdr);
3425 gst_ebml_write_master_finish_full (ebml, blockgroup,
3426 gst_buffer_get_size (buf));
3427 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3428 gst_ebml_write_buffer (ebml, buf);
3430 return gst_ebml_last_write_result (ebml);
3435 * gst_matroska_mux_handle_buffer:
3436 * @pads: #GstCollectPads
3437 * @uuser_data: #GstMatroskaMux
3439 * Collectpads callback.
3441 * Returns: #GstFlowReturn
3443 static GstFlowReturn
3444 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
3445 GstBuffer * buf, gpointer user_data)
3447 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3448 GstEbmlWrite *ebml = mux->ebml_write;
3449 GstMatroskaPad *best;
3450 GstFlowReturn ret = GST_FLOW_OK;
3452 GST_DEBUG_OBJECT (mux, "Collected pads");
3454 /* start with a header */
3455 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3456 if (mux->collect->data == NULL) {
3457 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3458 ("No input streams configured"));
3459 return GST_FLOW_ERROR;
3461 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3462 gst_ebml_start_streamheader (ebml);
3463 gst_matroska_mux_start (mux);
3464 gst_matroska_mux_stop_streamheader (mux);
3465 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3468 /* provided with stream to write from */
3469 best = (GstMatroskaPad *) data;
3471 /* if there is no best pad, we have reached EOS */
3473 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
3474 if (!mux->streamable) {
3475 gst_matroska_mux_finish (mux);
3477 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3479 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3484 /* if we have a best stream, should also have a buffer */
3487 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3488 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3489 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3490 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3492 /* make note of first and last encountered timestamps, so we can calculate
3493 * the actual duration later when we send an updated header on eos */
3494 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3495 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3496 GstClockTime end_ts = start_ts;
3498 if (GST_BUFFER_DURATION_IS_VALID (buf))
3499 end_ts += GST_BUFFER_DURATION (buf);
3500 else if (best->track->default_duration)
3501 end_ts += best->track->default_duration;
3503 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3504 best->end_ts = end_ts;
3506 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3507 start_ts < best->start_ts))
3508 best->start_ts = start_ts;
3511 /* write one buffer */
3512 ret = gst_matroska_mux_write_data (mux, best, buf);
3520 * gst_matroska_mux_change_state:
3521 * @element: #GstMatroskaMux
3522 * @transition: State change transition.
3524 * Change the muxer state.
3526 * Returns: #GstStateChangeReturn
3528 static GstStateChangeReturn
3529 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3531 GstStateChangeReturn ret;
3532 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3534 switch (transition) {
3535 case GST_STATE_CHANGE_NULL_TO_READY:
3537 case GST_STATE_CHANGE_READY_TO_PAUSED:
3538 gst_collect_pads_start (mux->collect);
3540 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3542 case GST_STATE_CHANGE_PAUSED_TO_READY:
3543 gst_collect_pads_stop (mux->collect);
3549 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3551 switch (transition) {
3552 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3554 case GST_STATE_CHANGE_PAUSED_TO_READY:
3555 gst_matroska_mux_reset (GST_ELEMENT (mux));
3557 case GST_STATE_CHANGE_READY_TO_NULL:
3567 gst_matroska_mux_set_property (GObject * object,
3568 guint prop_id, const GValue * value, GParamSpec * pspec)
3570 GstMatroskaMux *mux;
3572 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3573 mux = GST_MATROSKA_MUX (object);
3576 case ARG_WRITING_APP:
3577 if (!g_value_get_string (value)) {
3578 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3581 g_free (mux->writing_app);
3582 mux->writing_app = g_value_dup_string (value);
3584 case ARG_DOCTYPE_VERSION:
3585 mux->doctype_version = g_value_get_int (value);
3587 case ARG_MIN_INDEX_INTERVAL:
3588 mux->min_index_interval = g_value_get_int64 (value);
3590 case ARG_STREAMABLE:
3591 mux->streamable = g_value_get_boolean (value);
3594 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3600 gst_matroska_mux_get_property (GObject * object,
3601 guint prop_id, GValue * value, GParamSpec * pspec)
3603 GstMatroskaMux *mux;
3605 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3606 mux = GST_MATROSKA_MUX (object);
3609 case ARG_WRITING_APP:
3610 g_value_set_string (value, mux->writing_app);
3612 case ARG_DOCTYPE_VERSION:
3613 g_value_set_int (value, mux->doctype_version);
3615 case ARG_MIN_INDEX_INTERVAL:
3616 g_value_set_int64 (value, mux->min_index_interval);
3618 case ARG_STREAMABLE:
3619 g_value_set_boolean (value, mux->streamable);
3622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);