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, GRAY8, BGR, RGB }, "
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 "; "
165 COMMON_AUDIO_CAPS "; "
167 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
168 "layout = (string) interleaved, "
169 COMMON_AUDIO_CAPS ";"
171 "width = (int) { 8, 16, 24 }, "
172 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
173 "audio/x-pn-realaudio, "
174 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
175 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
176 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
177 COMMON_AUDIO_CAPS ";"
179 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
181 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
183 "layout = (string)dvi, "
184 "block_align = (int)[64, 8192], "
185 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
187 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
190 static GstStaticPadTemplate subtitlesink_templ =
191 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
194 GST_STATIC_CAPS ("subtitle/x-kate; "
195 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
196 "application/x-usf; subpicture/x-dvd; "
197 "application/x-subtitle-unknown")
200 static gpointer parent_class; /* NULL */
202 /* Matroska muxer destructor */
203 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
204 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
205 static void gst_matroska_mux_finalize (GObject * object);
207 /* Pads collected callback */
208 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
209 GstCollectData * data, GstBuffer * buf, gpointer user_data);
210 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
211 GstCollectData * data, GstEvent * event, gpointer user_data);
214 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
215 GstObject * parent, GstEvent * event);
216 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
217 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
218 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
220 /* gst internal change state handler */
221 static GstStateChangeReturn
222 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
224 /* gobject bla bla */
225 static void gst_matroska_mux_set_property (GObject * object,
226 guint prop_id, const GValue * value, GParamSpec * pspec);
227 static void gst_matroska_mux_get_property (GObject * object,
228 guint prop_id, GValue * value, GParamSpec * pspec);
231 static void gst_matroska_mux_reset (GstElement * element);
234 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
236 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
237 GstMatroskaTrackContext * context);
238 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
239 GstMatroskaTrackContext * context);
240 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
241 GstMatroskaTrackContext * context);
242 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
243 GstMatroskaTrackContext * context);
244 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
245 GstMatroskaTrackContext * context);
247 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
250 /* Cannot use boilerplate macros here because we need the full init function
251 * signature with the additional class argument, so we use the right template
252 * for the sink caps */
254 gst_matroska_mux_get_type (void)
256 static GType object_type; /* 0 */
258 if (object_type == 0) {
259 static const GTypeInfo object_info = {
260 sizeof (GstMatroskaMuxClass),
261 NULL, /* base_init */
262 NULL, /* base_finalize */
263 (GClassInitFunc) gst_matroska_mux_class_init,
264 NULL, /* class_finalize */
265 NULL, /* class_data */
266 sizeof (GstMatroskaMux),
268 (GInstanceInitFunc) gst_matroska_mux_init
270 const GInterfaceInfo iface_info = { NULL };
272 object_type = g_type_register_static (GST_TYPE_ELEMENT,
273 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
275 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
276 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
283 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
285 GObjectClass *gobject_class;
286 GstElementClass *gstelement_class;
288 gobject_class = (GObjectClass *) klass;
289 gstelement_class = (GstElementClass *) klass;
291 gst_element_class_add_pad_template (gstelement_class,
292 gst_static_pad_template_get (&videosink_templ));
293 gst_element_class_add_pad_template (gstelement_class,
294 gst_static_pad_template_get (&audiosink_templ));
295 gst_element_class_add_pad_template (gstelement_class,
296 gst_static_pad_template_get (&subtitlesink_templ));
297 gst_element_class_add_pad_template (gstelement_class,
298 gst_static_pad_template_get (&src_templ));
299 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
301 "Muxes video/audio/subtitle streams into a matroska stream",
302 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
304 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
307 gobject_class->finalize = gst_matroska_mux_finalize;
309 gobject_class->get_property = gst_matroska_mux_get_property;
310 gobject_class->set_property = gst_matroska_mux_set_property;
312 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
313 g_param_spec_string ("writing-app", "Writing application.",
314 "The name the application that creates the matroska file.",
315 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
316 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
317 g_param_spec_int ("version", "DocType version",
318 "This parameter determines what Matroska features can be used.",
319 1, 2, DEFAULT_DOCTYPE_VERSION,
320 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
321 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
322 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
323 "entries", "An index entry is created every so many nanoseconds.",
324 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
325 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
326 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
327 g_param_spec_boolean ("streamable", "Determines whether output should "
328 "be streamable", "If set to true, the output should be as if it is "
329 "to be streamed and hence no indexes written or duration written.",
331 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
333 gstelement_class->change_state =
334 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
335 gstelement_class->request_new_pad =
336 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
337 gstelement_class->release_pad =
338 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
340 parent_class = g_type_class_peek_parent (klass);
344 * Start of pad option handler code
346 #define DEFAULT_PAD_FRAME_DURATION TRUE
347 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
352 PROP_PAD_FRAME_DURATION
358 gboolean frame_duration;
359 gboolean frame_duration_user;
362 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
365 gst_matroskamux_pad_get_type (void)
367 static GType type = 0;
369 if (G_UNLIKELY (type == 0)) {
370 type = g_type_register_static_simple (GST_TYPE_PAD,
371 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
372 (GClassInitFunc) gst_matroskamux_pad_class_init,
373 sizeof (GstMatroskamuxPad), NULL, 0);
378 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
379 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
380 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
381 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
384 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
385 GValue * value, GParamSpec * pspec)
387 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
390 case PROP_PAD_FRAME_DURATION:
391 g_value_set_boolean (value, pad->frame_duration);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
401 const GValue * value, GParamSpec * pspec)
403 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
406 case PROP_PAD_FRAME_DURATION:
407 pad->frame_duration = g_value_get_boolean (value);
408 pad->frame_duration_user = TRUE;
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 gst_matroskamux_pad_class_init (GstPadClass * klass)
419 GObjectClass *gobject_class = (GObjectClass *) klass;
421 gobject_class->set_property = gst_matroskamux_pad_set_property;
422 gobject_class->get_property = gst_matroskamux_pad_get_property;
424 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
425 g_param_spec_boolean ("frame-duration", "Frame duration",
426 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
427 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
431 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
433 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
434 pad->frame_duration_user = FALSE;
438 * End of pad option handler code
442 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
444 GstPadTemplate *templ;
447 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
448 mux->srcpad = gst_pad_new_from_template (templ, "src");
450 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
451 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
452 gst_pad_use_fixed_caps (mux->srcpad);
454 mux->collect = gst_collect_pads_new ();
455 gst_collect_pads_set_clip_function (mux->collect,
456 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
457 gst_collect_pads_set_buffer_function (mux->collect,
458 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
459 gst_collect_pads_set_event_function (mux->collect,
460 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
462 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
463 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
465 /* property defaults */
466 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
467 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
468 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
469 mux->streamable = DEFAULT_STREAMABLE;
471 /* initialize internal variables */
473 mux->num_streams = 0;
474 mux->num_a_streams = 0;
475 mux->num_t_streams = 0;
476 mux->num_v_streams = 0;
478 /* create used uid list */
479 mux->used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
481 /* initialize remaining variables */
482 gst_matroska_mux_reset (GST_ELEMENT (mux));
487 * gst_matroska_mux_finalize:
488 * @object: #GstMatroskaMux that should be finalized.
490 * Finalize matroska muxer.
493 gst_matroska_mux_finalize (GObject * object)
495 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
497 gst_event_replace (&mux->force_key_unit_event, NULL);
499 gst_object_unref (mux->collect);
500 gst_object_unref (mux->ebml_write);
501 if (mux->writing_app)
502 g_free (mux->writing_app);
504 g_array_free (mux->used_uids, TRUE);
506 G_OBJECT_CLASS (parent_class)->finalize (object);
511 * gst_matroska_mux_create_uid:
512 * @mux: #GstMatroskaMux to generate UID for.
514 * Generate new unused track UID.
516 * Returns: New track UID.
519 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
526 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
527 for (i = 0; i < mux->used_uids->len; i++) {
528 if (g_array_index (mux->used_uids, guint64, i) == uid) {
533 g_array_append_val (mux->used_uids, uid);
541 * gst_matroska_pad_reset:
542 * @collect_pad: the #GstMatroskaPad
544 * Reset and/or release resources of a matroska collect pad.
547 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
550 GstMatroskaTrackType type = 0;
552 /* free track information */
553 if (collect_pad->track != NULL) {
554 /* retrieve for optional later use */
555 name = collect_pad->track->name;
556 type = collect_pad->track->type;
557 /* extra for video */
558 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
559 GstMatroskaTrackVideoContext *ctx =
560 (GstMatroskaTrackVideoContext *) collect_pad->track;
562 if (ctx->dirac_unit) {
563 gst_buffer_unref (ctx->dirac_unit);
564 ctx->dirac_unit = NULL;
567 g_free (collect_pad->track->codec_id);
568 g_free (collect_pad->track->codec_name);
570 g_free (collect_pad->track->name);
571 g_free (collect_pad->track->language);
572 g_free (collect_pad->track->codec_priv);
573 g_free (collect_pad->track);
574 collect_pad->track = NULL;
577 if (!full && type != 0) {
578 GstMatroskaTrackContext *context;
580 /* create a fresh context */
582 case GST_MATROSKA_TRACK_TYPE_VIDEO:
583 context = (GstMatroskaTrackContext *)
584 g_new0 (GstMatroskaTrackVideoContext, 1);
586 case GST_MATROSKA_TRACK_TYPE_AUDIO:
587 context = (GstMatroskaTrackContext *)
588 g_new0 (GstMatroskaTrackAudioContext, 1);
590 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
591 context = (GstMatroskaTrackContext *)
592 g_new0 (GstMatroskaTrackSubtitleContext, 1);
595 g_assert_not_reached ();
599 context->type = type;
600 context->name = name;
601 /* TODO: check default values for the context */
602 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
603 collect_pad->track = context;
604 collect_pad->duration = 0;
605 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
606 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
611 * gst_matroska_pad_free:
612 * @collect_pad: the #GstMatroskaPad
614 * Release resources of a matroska collect pad.
617 gst_matroska_pad_free (GstPad * collect_pad)
619 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
624 * gst_matroska_mux_reset:
625 * @element: #GstMatroskaMux that should be reseted.
627 * Reset matroska muxer back to initial state.
630 gst_matroska_mux_reset (GstElement * element)
632 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
635 /* reset EBML write */
636 gst_ebml_write_reset (mux->ebml_write);
639 mux->state = GST_MATROSKA_MUX_STATE_START;
641 /* clean up existing streams */
643 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
644 GstMatroskaPad *collect_pad;
646 collect_pad = (GstMatroskaPad *) walk->data;
648 /* reset collect pad to pristine state */
649 gst_matroska_pad_reset (collect_pad, FALSE);
653 mux->num_indexes = 0;
658 mux->time_scale = GST_MSECOND;
659 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
664 mux->cluster_time = 0;
665 mux->cluster_pos = 0;
666 mux->prev_cluster_size = 0;
669 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
674 gst_toc_setter_reset (GST_TOC_SETTER (mux));
676 mux->chapters_pos = 0;
678 /* clear used uids */
679 if (mux->used_uids->len > 0) {
680 g_array_remove_range (mux->used_uids, 0, mux->used_uids->len);
685 * gst_matroska_mux_handle_src_event:
686 * @pad: Pad which received the event.
687 * @event: Received event.
689 * handle events - copied from oggmux without understanding
691 * Returns: #TRUE on success.
694 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
699 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
703 /* disable seeking for now */
709 return gst_pad_event_default (pad, parent, event);
714 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
716 if (context->codec_priv != NULL) {
717 g_free (context->codec_priv);
718 context->codec_priv = NULL;
719 context->codec_priv_size = 0;
724 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
734 /* produce comma-separated list in hex format */
735 for (i = 0; i < 16; ++i) {
737 /* replicate vobsub's slightly off RGB conversion calculation */
738 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
739 u = ((col >> 8) & 0xff) - 128;
740 v = (col & 0xff) - 128;
741 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
742 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
743 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
744 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
747 sclut = g_strjoinv (",", clutv);
749 /* build codec private; only palette for now */
750 gst_matroska_mux_free_codec_priv (context);
751 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
752 /* include terminating 0 */
753 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
755 for (i = 0; i < 16; ++i) {
762 * gst_matroska_mux_handle_sink_event:
763 * @pad: Pad which received the event.
764 * @event: Received event.
766 * handle events - informational ones like tags
768 * Returns: #TRUE on success.
771 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
772 GstCollectData * data, GstEvent * event, gpointer user_data)
774 GstMatroskaPad *collect_pad;
775 GstMatroskaTrackContext *context;
781 mux = GST_MATROSKA_MUX (user_data);
782 collect_pad = (GstMatroskaPad *) data;
784 context = collect_pad->track;
787 switch (GST_EVENT_TYPE (event)) {
788 case GST_EVENT_CAPS:{
791 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
792 gst_event_parse_caps (event, &caps);
794 ret = collect_pad->capsfunc (pad, caps);
795 gst_event_unref (event);
802 GST_DEBUG_OBJECT (mux, "received tag event");
803 gst_event_parse_tag (event, &list);
805 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
806 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
807 const gchar *lang_code;
809 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
811 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
812 context->language = g_strdup (lang_code);
814 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
819 /* FIXME: what about stream-specific tags? */
820 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
821 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
823 gst_event_unref (event);
824 /* handled this, don't want collectpads to forward it downstream */
830 GstToc *toc, *old_toc;
832 if (mux->chapters_pos > 0)
835 GST_DEBUG_OBJECT (mux, "received toc event");
836 gst_event_parse_toc (event, &toc, NULL);
839 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
840 if (old_toc != NULL) {
842 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
843 gst_toc_unref (old_toc);
846 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
850 gst_event_unref (event);
851 /* handled this, don't want collectpads to forward it downstream */
855 case GST_EVENT_CUSTOM_DOWNSTREAM:
856 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
857 const GstStructure *structure;
859 structure = gst_event_get_structure (event);
860 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
861 gst_event_replace (&mux->force_key_unit_event, NULL);
862 mux->force_key_unit_event = event;
864 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
865 !strcmp ("dvd-spu-clut-change",
866 gst_structure_get_string (structure, "event"))) {
871 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
872 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
873 GST_DEBUG_OBJECT (pad, "... discarding");
876 /* first transform event data into table form */
877 for (i = 0; i < 16; i++) {
878 g_snprintf (name, sizeof (name), "clut%02d", i);
879 if (!gst_structure_get_int (structure, name, &value)) {
880 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
881 "contain %s field", name);
887 /* transform into private data for stream; text form */
888 gst_matroska_mux_build_vobsub_private (context, clut);
897 return gst_collect_pads_event_default (pads, data, event, FALSE);
903 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
906 g_assert (context && id);
907 if (context->codec_id)
908 g_free (context->codec_id);
909 context->codec_id = g_strdup (id);
913 * gst_matroska_mux_video_pad_setcaps:
914 * @pad: Pad which got the caps.
917 * Setcaps function for video sink pad.
919 * Returns: #TRUE on success.
922 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
924 GstMatroskaTrackContext *context = NULL;
925 GstMatroskaTrackVideoContext *videocontext;
927 GstMatroskaPad *collect_pad;
928 GstStructure *structure;
929 const gchar *mimetype;
930 const gchar *interlace_mode;
931 const GValue *value = NULL;
932 GstBuffer *codec_buf = NULL;
933 gint width, height, pixel_width, pixel_height;
936 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
939 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
940 g_assert (collect_pad);
941 context = collect_pad->track;
943 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
944 videocontext = (GstMatroskaTrackVideoContext *) context;
946 /* gst -> matroska ID'ing */
947 structure = gst_caps_get_structure (caps, 0);
949 mimetype = gst_structure_get_name (structure);
951 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
952 if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
953 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
955 if (!strcmp (mimetype, "video/x-theora")) {
956 /* we'll extract the details later from the theora identification header */
960 /* get general properties */
961 /* spec says it is mandatory */
962 if (!gst_structure_get_int (structure, "width", &width) ||
963 !gst_structure_get_int (structure, "height", &height))
966 videocontext->pixel_width = width;
967 videocontext->pixel_height = height;
969 /* set vp8 defaults or let user override it */
970 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
971 && (!strcmp (mimetype, "video/x-vp8")
972 || !strcmp (mimetype, "video/x-vp9")))
973 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
974 DEFAULT_PAD_FRAME_DURATION_VP8;
976 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
977 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
979 context->default_duration =
980 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
981 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
982 GST_TIME_ARGS (context->default_duration));
984 context->default_duration = 0;
986 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
987 &pixel_width, &pixel_height)) {
988 if (pixel_width > pixel_height) {
989 videocontext->display_width = width * pixel_width / pixel_height;
990 videocontext->display_height = height;
991 } else if (pixel_width < pixel_height) {
992 videocontext->display_width = width;
993 videocontext->display_height = height * pixel_height / pixel_width;
995 videocontext->display_width = 0;
996 videocontext->display_height = 0;
999 videocontext->display_width = 0;
1000 videocontext->display_height = 0;
1005 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1006 videocontext->fourcc = 0;
1008 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1009 * data and other settings
1013 /* extract codec_data, may turn out needed */
1014 value = gst_structure_get_value (structure, "codec_data");
1016 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1019 if (!strcmp (mimetype, "video/x-raw")) {
1021 gst_matroska_mux_set_codec_id (context,
1022 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1023 fstr = gst_structure_get_string (structure, "format");
1024 if (fstr && strlen (fstr) == 4)
1025 videocontext->fourcc = GST_STR_FOURCC (fstr);
1026 else if (!strcmp (fstr, "GRAY8"))
1027 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1028 else if (!strcmp (fstr, "BGR"))
1029 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1030 else if (!strcmp (fstr, "RGB"))
1031 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1032 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1033 ||!strcmp (mimetype, "video/x-divx")
1034 || !strcmp (mimetype, "video/x-dv")
1035 || !strcmp (mimetype, "video/x-h263")
1036 || !strcmp (mimetype, "video/x-msmpeg")
1037 || !strcmp (mimetype, "video/x-wmv")
1038 || !strcmp (mimetype, "image/jpeg")) {
1039 gst_riff_strf_vids *bih;
1040 gint size = sizeof (gst_riff_strf_vids);
1043 if (!strcmp (mimetype, "video/x-huffyuv"))
1044 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1045 else if (!strcmp (mimetype, "video/x-dv"))
1046 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1047 else if (!strcmp (mimetype, "video/x-h263"))
1048 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1049 else if (!strcmp (mimetype, "video/x-divx")) {
1052 gst_structure_get_int (structure, "divxversion", &divxversion);
1053 switch (divxversion) {
1055 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1058 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1061 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1064 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1067 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1068 switch (msmpegversion) {
1070 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1073 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1079 } else if (!strcmp (mimetype, "video/x-wmv")) {
1083 fstr = gst_structure_get_string (structure, "format");
1084 if (fstr && strlen (fstr) == 4) {
1085 fourcc = GST_STR_FOURCC (fstr);
1086 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1087 if (wmvversion == 2) {
1088 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1089 } else if (wmvversion == 1) {
1090 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1091 } else if (wmvversion == 3) {
1092 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1095 } else if (!strcmp (mimetype, "image/jpeg")) {
1096 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1102 bih = g_new0 (gst_riff_strf_vids, 1);
1103 GST_WRITE_UINT32_LE (&bih->size, size);
1104 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1105 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1106 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1107 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1108 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1109 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1110 videocontext->pixel_height * 3);
1112 /* process codec private/initialization data, if any */
1114 size += gst_buffer_get_size (codec_buf);
1115 bih = g_realloc (bih, size);
1116 GST_WRITE_UINT32_LE (&bih->size, size);
1117 gst_buffer_extract (codec_buf, 0,
1118 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1121 gst_matroska_mux_set_codec_id (context,
1122 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1123 gst_matroska_mux_free_codec_priv (context);
1124 context->codec_priv = (gpointer) bih;
1125 context->codec_priv_size = size;
1126 } else if (!strcmp (mimetype, "video/x-h264")) {
1127 gst_matroska_mux_set_codec_id (context,
1128 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1129 gst_matroska_mux_free_codec_priv (context);
1130 /* Create avcC header */
1131 if (codec_buf != NULL) {
1132 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1133 context->codec_priv = g_malloc0 (context->codec_priv_size);
1134 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1136 } else if (!strcmp (mimetype, "video/x-h265")) {
1137 gst_matroska_mux_set_codec_id (context,
1138 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1139 gst_matroska_mux_free_codec_priv (context);
1140 /* Create hvcC header */
1141 if (codec_buf != NULL) {
1142 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1143 context->codec_priv = g_malloc0 (context->codec_priv_size);
1144 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1146 } else if (!strcmp (mimetype, "video/x-theora")) {
1147 const GValue *streamheader;
1149 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1151 gst_matroska_mux_free_codec_priv (context);
1153 streamheader = gst_structure_get_value (structure, "streamheader");
1154 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1155 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1156 ("theora stream headers missing or malformed"));
1159 } else if (!strcmp (mimetype, "video/x-dirac")) {
1160 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1161 } else if (!strcmp (mimetype, "video/x-vp8")) {
1162 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1163 } else if (!strcmp (mimetype, "video/x-vp9")) {
1164 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1165 } else if (!strcmp (mimetype, "video/mpeg")) {
1168 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1169 switch (mpegversion) {
1171 gst_matroska_mux_set_codec_id (context,
1172 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1175 gst_matroska_mux_set_codec_id (context,
1176 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1179 gst_matroska_mux_set_codec_id (context,
1180 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1186 /* global headers may be in codec data */
1187 if (codec_buf != NULL) {
1188 gst_matroska_mux_free_codec_priv (context);
1189 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1190 context->codec_priv = g_malloc0 (context->codec_priv_size);
1191 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1193 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1195 /* can only make it here if preceding case verified it was version 3 */
1196 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1197 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1199 const GValue *mdpr_data;
1201 gst_structure_get_int (structure, "rmversion", &rmversion);
1202 switch (rmversion) {
1204 gst_matroska_mux_set_codec_id (context,
1205 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1208 gst_matroska_mux_set_codec_id (context,
1209 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1212 gst_matroska_mux_set_codec_id (context,
1213 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1216 gst_matroska_mux_set_codec_id (context,
1217 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1223 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1224 if (mdpr_data != NULL) {
1225 guint8 *priv_data = NULL;
1226 guint priv_data_size = 0;
1228 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1230 priv_data_size = gst_buffer_get_size (codec_data_buf);
1231 priv_data = g_malloc0 (priv_data_size);
1233 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1235 gst_matroska_mux_free_codec_priv (context);
1236 context->codec_priv = priv_data;
1237 context->codec_priv_size = priv_data_size;
1246 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1247 GST_PAD_NAME (pad), caps);
1252 /* N > 0 to expect a particular number of headers, negative if the
1253 number of headers is variable */
1255 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1256 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1258 GstBuffer **buf = NULL;
1261 guint bufi, i, offset, priv_data_size;
1263 if (streamheader == NULL)
1264 goto no_stream_headers;
1266 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1269 bufarr = g_value_peek_pointer (streamheader);
1270 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1272 if (N > 0 && bufarr->len != N)
1275 context->xiph_headers_to_skip = bufarr->len;
1277 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1278 for (i = 0; i < bufarr->len; i++) {
1279 GValue *bufval = &g_array_index (bufarr, GValue, i);
1281 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1283 goto wrong_content_type;
1286 buf[i] = g_value_peek_pointer (bufval);
1290 if (bufarr->len > 0) {
1291 for (i = 0; i < bufarr->len - 1; i++) {
1292 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1296 for (i = 0; i < bufarr->len; ++i) {
1297 priv_data_size += gst_buffer_get_size (buf[i]);
1300 priv_data = g_malloc0 (priv_data_size);
1302 priv_data[0] = bufarr->len - 1;
1305 if (bufarr->len > 0) {
1306 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1307 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1308 priv_data[offset++] = 0xff;
1310 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1314 for (i = 0; i < bufarr->len; ++i) {
1315 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1316 offset += gst_buffer_get_size (buf[i]);
1319 gst_matroska_mux_free_codec_priv (context);
1320 context->codec_priv = priv_data;
1321 context->codec_priv_size = priv_data_size;
1324 *p_buf0 = gst_buffer_ref (buf[0]);
1333 GST_WARNING ("required streamheaders missing in sink caps!");
1338 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1339 G_VALUE_TYPE_NAME (streamheader));
1344 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1349 GST_WARNING ("streamheaders array does not contain GstBuffers");
1355 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1356 GstMatroskaTrackContext * context)
1358 GstBuffer *buf0 = NULL;
1360 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1363 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1364 GST_WARNING ("First vorbis header too small, ignoring");
1366 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1367 GstMatroskaTrackAudioContext *audiocontext;
1371 gst_buffer_map (buf0, &map, GST_MAP_READ);
1372 hdr = map.data + 1 + 6 + 4;
1373 audiocontext = (GstMatroskaTrackAudioContext *) context;
1374 audiocontext->channels = GST_READ_UINT8 (hdr);
1375 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1376 gst_buffer_unmap (buf0, &map);
1381 gst_buffer_unref (buf0);
1387 theora_streamheader_to_codecdata (const GValue * streamheader,
1388 GstMatroskaTrackContext * context)
1390 GstBuffer *buf0 = NULL;
1392 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1395 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1396 GST_WARNING ("First theora header too small, ignoring");
1397 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1398 GST_WARNING ("First header not a theora identification header, ignoring");
1400 GstMatroskaTrackVideoContext *videocontext;
1401 guint fps_num, fps_denom, par_num, par_denom;
1405 gst_buffer_map (buf0, &map, GST_MAP_READ);
1406 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1408 videocontext = (GstMatroskaTrackVideoContext *) context;
1409 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1410 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1411 hdr += 3 + 3 + 1 + 1;
1412 fps_num = GST_READ_UINT32_BE (hdr);
1413 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1414 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1415 fps_denom, fps_num);
1417 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1418 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1419 if (par_num > 0 && par_num > 0) {
1420 if (par_num > par_denom) {
1421 videocontext->display_width =
1422 videocontext->pixel_width * par_num / par_denom;
1423 videocontext->display_height = videocontext->pixel_height;
1424 } else if (par_num < par_denom) {
1425 videocontext->display_width = videocontext->pixel_width;
1426 videocontext->display_height =
1427 videocontext->pixel_height * par_denom / par_num;
1429 videocontext->display_width = 0;
1430 videocontext->display_height = 0;
1433 videocontext->display_width = 0;
1434 videocontext->display_height = 0;
1438 gst_buffer_unmap (buf0, &map);
1442 gst_buffer_unref (buf0);
1448 kate_streamheader_to_codecdata (const GValue * streamheader,
1449 GstMatroskaTrackContext * context)
1451 GstBuffer *buf0 = NULL;
1453 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1456 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1457 GST_WARNING ("First kate header too small, ignoring");
1458 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1459 GST_WARNING ("First header not a kate identification header, ignoring");
1463 gst_buffer_unref (buf0);
1469 flac_streamheader_to_codecdata (const GValue * streamheader,
1470 GstMatroskaTrackContext * context)
1477 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1478 GST_WARNING ("No or invalid streamheader field in the caps");
1482 bufarr = g_value_peek_pointer (streamheader);
1483 if (bufarr->len < 2) {
1484 GST_WARNING ("Too few headers in streamheader field");
1488 context->xiph_headers_to_skip = bufarr->len + 1;
1490 bufval = &g_array_index (bufarr, GValue, 0);
1491 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1492 GST_WARNING ("streamheaders array does not contain GstBuffers");
1496 buffer = g_value_peek_pointer (bufval);
1498 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1499 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1500 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1501 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1502 GST_WARNING ("Invalid streamheader for FLAC");
1506 gst_matroska_mux_free_codec_priv (context);
1507 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1508 context->codec_priv = g_malloc (context->codec_priv_size);
1509 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1511 for (i = 1; i < bufarr->len; i++) {
1513 bufval = &g_array_index (bufarr, GValue, i);
1515 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1516 gst_matroska_mux_free_codec_priv (context);
1517 GST_WARNING ("streamheaders array does not contain GstBuffers");
1521 buffer = g_value_peek_pointer (bufval);
1523 old_size = context->codec_priv_size;
1524 context->codec_priv_size += gst_buffer_get_size (buffer);
1526 context->codec_priv = g_realloc (context->codec_priv,
1527 context->codec_priv_size);
1528 gst_buffer_extract (buffer, 0,
1529 (guint8 *) context->codec_priv + old_size, -1);
1536 speex_streamheader_to_codecdata (const GValue * streamheader,
1537 GstMatroskaTrackContext * context)
1544 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1545 GST_WARNING ("No or invalid streamheader field in the caps");
1549 bufarr = g_value_peek_pointer (streamheader);
1550 if (bufarr->len != 2) {
1551 GST_WARNING ("Too few headers in streamheader field");
1555 context->xiph_headers_to_skip = bufarr->len + 1;
1557 bufval = &g_array_index (bufarr, GValue, 0);
1558 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1559 GST_WARNING ("streamheaders array does not contain GstBuffers");
1563 buffer = g_value_peek_pointer (bufval);
1565 if (gst_buffer_get_size (buffer) < 80
1566 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1567 GST_WARNING ("Invalid streamheader for Speex");
1571 gst_matroska_mux_free_codec_priv (context);
1572 context->codec_priv_size = gst_buffer_get_size (buffer);
1573 context->codec_priv = g_malloc (context->codec_priv_size);
1574 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1576 bufval = &g_array_index (bufarr, GValue, 1);
1578 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1579 gst_matroska_mux_free_codec_priv (context);
1580 GST_WARNING ("streamheaders array does not contain GstBuffers");
1584 buffer = g_value_peek_pointer (bufval);
1586 old_size = context->codec_priv_size;
1587 context->codec_priv_size += gst_buffer_get_size (buffer);
1588 context->codec_priv = g_realloc (context->codec_priv,
1589 context->codec_priv_size);
1590 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1595 static const gchar *
1596 aac_codec_data_to_codec_id (GstBuffer * buf)
1598 const gchar *result;
1601 /* default to MAIN */
1604 if (gst_buffer_get_size (buf) >= 2) {
1605 gst_buffer_extract (buf, 0, &profile, 1);
1623 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1632 * gst_matroska_mux_audio_pad_setcaps:
1633 * @pad: Pad which got the caps.
1636 * Setcaps function for audio sink pad.
1638 * Returns: #TRUE on success.
1641 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1643 GstMatroskaTrackContext *context = NULL;
1644 GstMatroskaTrackAudioContext *audiocontext;
1645 GstMatroskaMux *mux;
1646 GstMatroskaPad *collect_pad;
1647 const gchar *mimetype;
1648 gint samplerate = 0, channels = 0;
1649 GstStructure *structure;
1650 const GValue *codec_data = NULL;
1651 GstBuffer *buf = NULL;
1652 const gchar *stream_format = NULL;
1654 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1657 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1658 g_assert (collect_pad);
1659 context = collect_pad->track;
1661 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1662 audiocontext = (GstMatroskaTrackAudioContext *) context;
1664 structure = gst_caps_get_structure (caps, 0);
1665 mimetype = gst_structure_get_name (structure);
1668 gst_structure_get_int (structure, "rate", &samplerate);
1669 gst_structure_get_int (structure, "channels", &channels);
1671 audiocontext->samplerate = samplerate;
1672 audiocontext->channels = channels;
1673 audiocontext->bitdepth = 0;
1674 context->default_duration = 0;
1676 codec_data = gst_structure_get_value (structure, "codec_data");
1678 buf = gst_value_get_buffer (codec_data);
1680 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1681 * data and other settings
1685 if (!strcmp (mimetype, "audio/mpeg")) {
1686 gint mpegversion = 0;
1688 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1689 switch (mpegversion) {
1695 gst_structure_get_int (structure, "layer", &layer);
1697 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1698 GST_WARNING_OBJECT (mux,
1699 "Unable to determine MPEG audio version, assuming 1");
1705 else if (layer == 2)
1707 else if (version == 2)
1712 context->default_duration =
1713 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1717 gst_matroska_mux_set_codec_id (context,
1718 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1721 gst_matroska_mux_set_codec_id (context,
1722 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1725 gst_matroska_mux_set_codec_id (context,
1726 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1735 stream_format = gst_structure_get_string (structure, "stream-format");
1736 /* check this is raw aac */
1737 if (stream_format) {
1738 if (strcmp (stream_format, "raw") != 0) {
1739 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1743 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1748 if (mpegversion == 2)
1750 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1751 aac_codec_data_to_codec_id (buf));
1752 else if (mpegversion == 4)
1754 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1755 aac_codec_data_to_codec_id (buf));
1757 g_assert_not_reached ();
1759 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1766 } else if (!strcmp (mimetype, "audio/x-raw")) {
1769 gst_audio_info_init (&info);
1770 if (!gst_audio_info_from_caps (&info, caps)) {
1771 GST_DEBUG_OBJECT (mux,
1772 "broken caps, rejected by gst_audio_info_from_caps");
1776 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1777 case GST_AUDIO_FORMAT_U8:
1778 case GST_AUDIO_FORMAT_S16BE:
1779 case GST_AUDIO_FORMAT_S16LE:
1780 case GST_AUDIO_FORMAT_S24BE:
1781 case GST_AUDIO_FORMAT_S24LE:
1782 case GST_AUDIO_FORMAT_S32BE:
1783 case GST_AUDIO_FORMAT_S32LE:
1784 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1785 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1788 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1789 gst_matroska_mux_set_codec_id (context,
1790 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1792 gst_matroska_mux_set_codec_id (context,
1793 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1795 case GST_AUDIO_FORMAT_F32LE:
1796 case GST_AUDIO_FORMAT_F64LE:
1797 gst_matroska_mux_set_codec_id (context,
1798 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1802 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1806 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1807 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1808 const GValue *streamheader;
1810 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1812 gst_matroska_mux_free_codec_priv (context);
1814 streamheader = gst_structure_get_value (structure, "streamheader");
1815 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1816 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1817 ("vorbis stream headers missing or malformed"));
1820 } else if (!strcmp (mimetype, "audio/x-flac")) {
1821 const GValue *streamheader;
1823 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1825 gst_matroska_mux_free_codec_priv (context);
1827 streamheader = gst_structure_get_value (structure, "streamheader");
1828 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1829 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1830 ("flac stream headers missing or malformed"));
1833 } else if (!strcmp (mimetype, "audio/x-speex")) {
1834 const GValue *streamheader;
1836 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1837 gst_matroska_mux_free_codec_priv (context);
1839 streamheader = gst_structure_get_value (structure, "streamheader");
1840 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1841 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1842 ("speex stream headers missing or malformed"));
1845 } else if (!strcmp (mimetype, "audio/x-opus")) {
1846 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
1847 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1848 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1849 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1850 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1851 } else if (!strcmp (mimetype, "audio/x-dts")) {
1852 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1853 } else if (!strcmp (mimetype, "audio/x-tta")) {
1856 /* TTA frame duration */
1857 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1859 gst_structure_get_int (structure, "width", &width);
1860 audiocontext->bitdepth = width;
1861 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1863 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1865 const GValue *mdpr_data;
1867 gst_structure_get_int (structure, "raversion", &raversion);
1868 switch (raversion) {
1870 gst_matroska_mux_set_codec_id (context,
1871 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1874 gst_matroska_mux_set_codec_id (context,
1875 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1878 gst_matroska_mux_set_codec_id (context,
1879 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1885 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1886 if (mdpr_data != NULL) {
1887 guint8 *priv_data = NULL;
1888 guint priv_data_size = 0;
1890 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1892 priv_data_size = gst_buffer_get_size (codec_data_buf);
1893 priv_data = g_malloc0 (priv_data_size);
1895 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1897 gst_matroska_mux_free_codec_priv (context);
1899 context->codec_priv = priv_data;
1900 context->codec_priv_size = priv_data_size;
1903 } else if (!strcmp (mimetype, "audio/x-wma")
1904 || !strcmp (mimetype, "audio/x-alaw")
1905 || !strcmp (mimetype, "audio/x-mulaw")
1906 || !strcmp (mimetype, "audio/x-adpcm")) {
1908 guint codec_priv_size;
1913 if (samplerate == 0 || channels == 0) {
1914 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1918 if (!strcmp (mimetype, "audio/x-wma")) {
1922 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1923 || !gst_structure_get_int (structure, "block_align", &block_align)
1924 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1925 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1930 switch (wmaversion) {
1932 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1935 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1938 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1941 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1945 if (gst_structure_get_int (structure, "depth", &depth))
1946 audiocontext->bitdepth = depth;
1947 } else if (!strcmp (mimetype, "audio/x-alaw")
1948 || !strcmp (mimetype, "audio/x-mulaw")) {
1949 audiocontext->bitdepth = 8;
1950 if (!strcmp (mimetype, "audio/x-alaw"))
1951 format = GST_RIFF_WAVE_FORMAT_ALAW;
1953 format = GST_RIFF_WAVE_FORMAT_MULAW;
1955 block_align = channels;
1956 bitrate = block_align * samplerate;
1957 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
1960 layout = gst_structure_get_string (structure, "layout");
1962 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
1966 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
1967 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
1971 if (!strcmp (layout, "dvi")) {
1972 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
1973 } else if (!strcmp (layout, "g726")) {
1974 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
1975 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
1976 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
1980 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
1985 g_assert (format != 0);
1987 codec_priv_size = WAVEFORMATEX_SIZE;
1989 codec_priv_size += gst_buffer_get_size (buf);
1991 /* serialize waveformatex structure */
1992 codec_priv = g_malloc0 (codec_priv_size);
1993 GST_WRITE_UINT16_LE (codec_priv, format);
1994 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1995 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1996 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1997 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1998 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2000 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2002 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2004 /* process codec private/initialization data, if any */
2006 gst_buffer_extract (buf, 0,
2007 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2010 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2011 gst_matroska_mux_free_codec_priv (context);
2012 context->codec_priv = (gpointer) codec_priv;
2013 context->codec_priv_size = codec_priv_size;
2021 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2022 GST_PAD_NAME (pad), caps);
2027 /* we probably don't have the data at start,
2028 * so have to reserve (a maximum) space to write this at the end.
2029 * bit spacy, but some formats can hold quite some */
2030 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2033 * gst_matroska_mux_subtitle_pad_setcaps:
2034 * @pad: Pad which got the caps.
2037 * Setcaps function for subtitle sink pad.
2039 * Returns: #TRUE on success.
2042 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2044 /* There is now (at least) one such alement (kateenc), and I'm going
2045 to handle it here and claim it works when it can be piped back
2046 through GStreamer and VLC */
2048 GstMatroskaTrackContext *context = NULL;
2049 GstMatroskaTrackSubtitleContext *scontext;
2050 GstMatroskaMux *mux;
2051 GstMatroskaPad *collect_pad;
2052 const gchar *mimetype;
2053 GstStructure *structure;
2054 const GValue *value = NULL;
2055 GstBuffer *buf = NULL;
2056 gboolean ret = TRUE;
2058 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2061 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2062 g_assert (collect_pad);
2063 context = collect_pad->track;
2065 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2066 scontext = (GstMatroskaTrackSubtitleContext *) context;
2068 structure = gst_caps_get_structure (caps, 0);
2069 mimetype = gst_structure_get_name (structure);
2072 scontext->check_utf8 = 1;
2073 scontext->invalid_utf8 = 0;
2074 context->default_duration = 0;
2076 if (!strcmp (mimetype, "subtitle/x-kate")) {
2077 const GValue *streamheader;
2079 gst_matroska_mux_set_codec_id (context,
2080 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2082 gst_matroska_mux_free_codec_priv (context);
2084 streamheader = gst_structure_get_value (structure, "streamheader");
2085 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2086 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2087 ("kate stream headers missing or malformed"));
2091 } else if (!strcmp (mimetype, "text/x-raw")) {
2092 gst_matroska_mux_set_codec_id (context,
2093 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2094 } else if (!strcmp (mimetype, "application/x-ssa")) {
2095 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2096 } else if (!strcmp (mimetype, "application/x-ass")) {
2097 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2098 } else if (!strcmp (mimetype, "application/x-usf")) {
2099 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2100 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2101 gst_matroska_mux_set_codec_id (context,
2102 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2108 /* maybe some private data, e.g. vobsub */
2109 value = gst_structure_get_value (structure, "codec_data");
2111 buf = gst_value_get_buffer (value);
2114 guint8 *priv_data = NULL;
2116 gst_buffer_map (buf, &map, GST_MAP_READ);
2118 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2119 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2120 " exceeded maximum (%d); discarding", pad,
2121 SUBTITLE_MAX_CODEC_PRIVATE);
2122 gst_buffer_unmap (buf, &map);
2126 gst_matroska_mux_free_codec_priv (context);
2128 priv_data = g_malloc0 (map.size);
2129 memcpy (priv_data, map.data, map.size);
2130 context->codec_priv = priv_data;
2131 context->codec_priv_size = map.size;
2132 gst_buffer_unmap (buf, &map);
2135 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2136 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2145 * gst_matroska_mux_request_new_pad:
2146 * @element: #GstMatroskaMux.
2147 * @templ: #GstPadTemplate.
2148 * @pad_name: New pad name.
2150 * Request pad function for sink templates.
2152 * Returns: New #GstPad.
2155 gst_matroska_mux_request_new_pad (GstElement * element,
2156 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2158 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2159 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2160 GstMatroskaPad *collect_pad;
2161 GstMatroskamuxPad *newpad;
2163 const gchar *pad_name = NULL;
2164 GstMatroskaCapsFunc capsfunc = NULL;
2165 GstMatroskaTrackContext *context = NULL;
2167 gboolean locked = TRUE;
2170 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2171 /* don't mix named and unnamed pads, if the pad already exists we fail when
2172 * trying to add it */
2173 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2174 pad_name = req_name;
2176 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2179 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2180 context = (GstMatroskaTrackContext *)
2181 g_new0 (GstMatroskaTrackAudioContext, 1);
2182 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2183 context->name = g_strdup ("Audio");
2184 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2185 /* don't mix named and unnamed pads, if the pad already exists we fail when
2186 * trying to add it */
2187 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2188 pad_name = req_name;
2190 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2193 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2194 context = (GstMatroskaTrackContext *)
2195 g_new0 (GstMatroskaTrackVideoContext, 1);
2196 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2197 context->name = g_strdup ("Video");
2198 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2199 /* don't mix named and unnamed pads, if the pad already exists we fail when
2200 * trying to add it */
2201 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2202 pad_name = req_name;
2204 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2207 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2208 context = (GstMatroskaTrackContext *)
2209 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2210 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2211 context->name = g_strdup ("Subtitle");
2212 /* setcaps may only provide proper one a lot later */
2213 id = g_strdup ("S_SUB_UNKNOWN");
2216 GST_WARNING_OBJECT (mux, "This is not our template!");
2220 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2221 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2224 gst_matroskamux_pad_init (newpad);
2225 collect_pad = (GstMatroskaPad *)
2226 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2227 sizeof (GstMatroskamuxPad),
2228 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2230 collect_pad->track = context;
2231 gst_matroska_pad_reset (collect_pad, FALSE);
2232 collect_pad->track->codec_id = id;
2234 collect_pad->capsfunc = capsfunc;
2235 gst_pad_set_active (GST_PAD (newpad), TRUE);
2236 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2237 goto pad_add_failed;
2241 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2243 return GST_PAD (newpad);
2248 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2249 gst_object_unref (newpad);
2255 * gst_matroska_mux_release_pad:
2256 * @element: #GstMatroskaMux.
2257 * @pad: Pad to release.
2259 * Release a previously requested pad.
2262 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2264 GstMatroskaMux *mux;
2267 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2269 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2270 GstCollectData *cdata = (GstCollectData *) walk->data;
2271 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2273 if (cdata->pad == pad) {
2274 GstClockTime min_dur; /* observed minimum duration */
2276 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2277 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2278 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2279 if (collect_pad->duration < min_dur)
2280 collect_pad->duration = min_dur;
2283 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2284 mux->duration < collect_pad->duration)
2285 mux->duration = collect_pad->duration;
2291 gst_collect_pads_remove_pad (mux->collect, pad);
2292 if (gst_element_remove_pad (element, pad))
2298 * gst_matroska_mux_track_header:
2299 * @mux: #GstMatroskaMux
2300 * @context: Tack context.
2302 * Write a track header.
2305 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2306 GstMatroskaTrackContext * context)
2308 GstEbmlWrite *ebml = mux->ebml_write;
2311 /* TODO: check if everything necessary is written and check default values */
2313 /* track type goes before the type-specific stuff */
2314 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2315 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2317 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2318 gst_matroska_mux_create_uid (mux));
2319 if (context->default_duration) {
2320 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2321 context->default_duration);
2323 if (context->language) {
2324 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2328 /* FIXME: until we have a nice way of getting the codecname
2329 * out of the caps, I'm not going to enable this. Too much
2330 * (useless, double, boring) work... */
2331 /* TODO: Use value from tags if any */
2332 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2333 context->codec_name); */
2334 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2336 /* type-specific stuff */
2337 switch (context->type) {
2338 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2339 GstMatroskaTrackVideoContext *videocontext =
2340 (GstMatroskaTrackVideoContext *) context;
2342 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2343 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2344 videocontext->pixel_width);
2345 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2346 videocontext->pixel_height);
2347 if (videocontext->display_width && videocontext->display_height) {
2348 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2349 videocontext->display_width);
2350 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2351 videocontext->display_height);
2353 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2354 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2355 if (videocontext->fourcc) {
2356 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2358 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2359 (gpointer) & fcc_le, 4);
2361 gst_ebml_write_master_finish (ebml, master);
2366 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2367 GstMatroskaTrackAudioContext *audiocontext =
2368 (GstMatroskaTrackAudioContext *) context;
2370 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2371 if (audiocontext->samplerate != 8000)
2372 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2373 audiocontext->samplerate);
2374 if (audiocontext->channels != 1)
2375 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2376 audiocontext->channels);
2377 if (audiocontext->bitdepth) {
2378 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2379 audiocontext->bitdepth);
2381 gst_ebml_write_master_finish (ebml, master);
2386 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2390 /* doesn't need type-specific data */
2394 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2395 if (context->codec_priv)
2396 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2397 context->codec_priv, context->codec_priv_size);
2402 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2404 guint64 title_master;
2407 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2409 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2410 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2411 GST_MATROSKA_MUX_CHAPLANG);
2413 gst_ebml_write_master_finish (ebml, title_master);
2417 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2418 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2419 guint64 * master_edition)
2421 guint64 uid, master_chapteratom;
2423 GstTocEntry *cur_entry;
2428 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2430 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2432 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2433 /* create uid for the parent */
2434 uid = gst_matroska_mux_create_uid ();
2435 g_free (edition->uid);
2436 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2439 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2441 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2442 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2443 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2444 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2447 uid = gst_matroska_mux_create_uid ();
2448 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2450 master_chapteratom =
2451 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2452 g_free (entry->uid);
2453 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2454 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2455 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2456 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2457 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2458 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2460 cur = entry->subentries;
2461 while (cur != NULL) {
2462 cur_entry = cur->data;
2463 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2468 if (G_LIKELY (entry->tags != NULL)) {
2469 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2471 for (i = 0; i < count; ++i) {
2472 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2473 gst_matroska_mux_write_chapter_title (title, ebml);
2477 /* remove title tag */
2478 if (G_LIKELY (count > 0))
2479 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2482 gst_ebml_write_master_finish (ebml, master_chapteratom);
2486 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2487 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2489 guint64 master_edition = 0;
2491 GstTocEntry *subentry;
2493 cur = gst_toc_entry_get_sub_entries (entry);
2494 while (cur != NULL) {
2495 subentry = cur->data;
2496 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2502 if (G_LIKELY (master_edition != 0))
2503 gst_ebml_write_master_finish (ebml, master_edition);
2508 * gst_matroska_mux_start:
2509 * @mux: #GstMatroskaMux
2511 * Start a new matroska file (write headers etc...)
2514 gst_matroska_mux_start (GstMatroskaMux * mux)
2516 GstEbmlWrite *ebml = mux->ebml_write;
2517 const gchar *doctype;
2518 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2519 GST_MATROSKA_ID_TRACKS,
2520 GST_MATROSKA_ID_CHAPTERS,
2521 GST_MATROSKA_ID_CUES,
2522 GST_MATROSKA_ID_TAGS,
2525 const gchar *media_type;
2526 gboolean audio_only;
2527 guint64 master, child;
2531 GstClockTime duration = 0;
2532 guint32 segment_uid[4];
2533 GTimeVal time = { 0, 0 };
2539 /* if not streaming, check if downstream is seekable */
2540 if (!mux->streamable) {
2544 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2545 if (gst_pad_peer_query (mux->srcpad, query)) {
2546 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2547 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2549 /* have to assume seeking is supported if query not handled downstream */
2550 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2554 mux->streamable = TRUE;
2555 g_object_notify (G_OBJECT (mux), "streamable");
2556 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2557 "streamable=false. Will ignore that and create streamable output "
2560 gst_query_unref (query);
2563 /* stream-start (FIXME: create id based on input ids) */
2564 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
2565 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
2568 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
2570 media_type = (audio_only) ? "audio/webm" : "video/webm";
2572 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
2574 ebml->caps = gst_caps_new_empty_simple (media_type);
2575 gst_pad_set_caps (mux->srcpad, ebml->caps);
2576 /* we start with a EBML header */
2577 doctype = mux->doctype;
2578 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2579 doctype, mux->doctype_version);
2580 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2582 /* the rest of the header is cached */
2583 gst_ebml_write_set_cache (ebml, 0x1000);
2585 /* start a segment */
2587 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2588 mux->segment_master = ebml->pos;
2590 if (!mux->streamable) {
2591 /* seekhead (table of contents) - we set the positions later */
2592 mux->seekhead_pos = ebml->pos;
2593 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2594 for (i = 0; seekhead_id[i] != 0; i++) {
2595 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2596 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2597 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2598 gst_ebml_write_master_finish (ebml, child);
2600 gst_ebml_write_master_finish (ebml, master);
2603 if (mux->streamable) {
2604 const GstTagList *tags;
2607 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2609 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2610 guint64 master_tags, master_tag;
2612 GST_DEBUG_OBJECT (mux, "Writing tags");
2614 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2615 mux->tags_pos = ebml->pos;
2616 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2617 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2618 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2619 gst_ebml_write_master_finish (ebml, master_tag);
2620 gst_ebml_write_master_finish (ebml, master_tags);
2625 mux->info_pos = ebml->pos;
2626 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2628 /* WebM does not support SegmentUID field on SegmentInfo */
2629 if (!mux->is_webm) {
2630 for (i = 0; i < 4; i++) {
2631 segment_uid[i] = g_random_int ();
2633 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2634 (guint8 *) segment_uid, 16);
2637 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2638 mux->duration_pos = ebml->pos;
2640 if (!mux->streamable) {
2641 for (collected = mux->collect->data; collected;
2642 collected = g_slist_next (collected)) {
2643 GstMatroskaPad *collect_pad;
2645 gint64 trackduration;
2647 collect_pad = (GstMatroskaPad *) collected->data;
2648 thepad = collect_pad->collect.pad;
2650 /* Query the total length of the track. */
2651 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2652 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2653 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2654 GST_TIME_ARGS (trackduration));
2655 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2656 duration = (GstClockTime) trackduration;
2660 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2661 gst_guint64_to_gdouble (duration) /
2662 gst_guint64_to_gdouble (mux->time_scale));
2664 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2665 "GStreamer plugin version " PACKAGE_VERSION);
2666 if (mux->writing_app && mux->writing_app[0]) {
2667 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2669 g_get_current_time (&time);
2670 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2671 gst_ebml_write_master_finish (ebml, master);
2674 mux->tracks_pos = ebml->pos;
2675 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2677 for (collected = mux->collect->data; collected;
2678 collected = g_slist_next (collected)) {
2679 GstMatroskaPad *collect_pad;
2682 collect_pad = (GstMatroskaPad *) collected->data;
2683 thepad = collect_pad->collect.pad;
2685 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2686 collect_pad->track->codec_id != 0) {
2687 collect_pad->track->num = tracknum++;
2688 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2689 gst_matroska_mux_track_header (mux, collect_pad->track);
2690 gst_ebml_write_master_finish (ebml, child);
2691 /* some remaining pad/track setup */
2692 collect_pad->default_duration_scaled =
2693 gst_util_uint64_scale (collect_pad->track->default_duration,
2694 1, mux->time_scale);
2697 gst_ebml_write_master_finish (ebml, master);
2699 /* FIXME: Check if we get a TOC that is supported by Matroska
2700 * and clean up the code below */
2703 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2704 if (toc != NULL && !mux->streamable) {
2705 guint64 master_chapters = 0;
2706 GstTocEntry *toc_entry;
2707 GList *cur, *to_write = NULL;
2710 GST_DEBUG ("Writing chapters");
2712 /* check whether we have editions or chapters at the root level */
2713 toc_entry = toc->entries->data;
2715 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2716 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2717 gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
2719 /* aggregate all chapters without root edition */
2720 cur = gst_toc_get_entries (toc);
2721 while (cur != NULL) {
2722 toc_entry->subentries =
2723 g_list_prepend (toc_entry->subentries, cur->data);
2727 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2728 toc_entry->subentries->data), &start, NULL);
2729 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2730 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2731 toc_entry->subentries->data), NULL, &stop);
2732 gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
2734 to_write = g_list_append (to_write, toc_entry);
2737 to_write = toc->entries;
2740 /* finally write chapters */
2741 mux->chapters_pos = ebml->pos;
2744 while (cur != NULL) {
2745 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2750 /* close master element if any edition was written */
2751 if (G_LIKELY (master_chapters != 0))
2752 gst_ebml_write_master_finish (ebml, master_chapters);
2754 if (toc_entry != NULL) {
2755 g_list_free (toc_entry->subentries);
2756 toc_entry->subentries = NULL;
2757 gst_toc_entry_unref (toc_entry);
2758 g_list_free (to_write);
2763 /* lastly, flush the cache */
2764 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2768 gst_toc_unref (toc);
2773 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2776 /* TODO: more sensible tag mappings */
2779 const gchar *matroska_tagname;
2780 const gchar *gstreamer_tagname;
2784 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2785 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2786 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2787 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2788 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2789 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2790 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2791 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2792 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2793 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2794 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2795 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2796 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2797 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2798 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2800 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2802 guint64 simpletag_master;
2804 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2805 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2806 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2808 if (strcmp (tagname_gst, tag) == 0) {
2809 GValue src = { 0, };
2812 if (!gst_tag_list_copy_value (&src, list, tag))
2814 if ((dest = gst_value_serialize (&src))) {
2816 simpletag_master = gst_ebml_write_master_start (ebml,
2817 GST_MATROSKA_ID_SIMPLETAG);
2818 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2819 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2820 gst_ebml_write_master_finish (ebml, simpletag_master);
2823 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2825 g_value_unset (&src);
2833 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2834 const GstTocEntry * entry, guint64 * master_tags)
2836 guint64 master_tag, master_targets;
2840 ebml = mux->ebml_write;
2842 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2843 if (*master_tags == 0) {
2844 mux->tags_pos = ebml->pos;
2845 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2848 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2850 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2852 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2853 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2854 g_ascii_strtoull (entry->uid, NULL, 10));
2856 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2857 g_ascii_strtoull (entry->uid, NULL, 10));
2859 gst_ebml_write_master_finish (ebml, master_targets);
2860 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2861 gst_ebml_write_master_finish (ebml, master_tag);
2864 cur = entry->subentries;
2865 while (cur != NULL) {
2866 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2873 * gst_matroska_mux_finish:
2874 * @mux: #GstMatroskaMux
2876 * Finish a new matroska file (write index etc...)
2879 gst_matroska_mux_finish (GstMatroskaMux * mux)
2881 GstEbmlWrite *ebml = mux->ebml_write;
2883 guint64 duration = 0;
2885 const GstTagList *tags;
2887 /* finish last cluster */
2889 gst_ebml_write_master_finish (ebml, mux->cluster);
2893 if (mux->index != NULL) {
2895 guint64 master, pointentry_master, trackpos_master;
2897 mux->cues_pos = ebml->pos;
2898 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2899 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2901 for (n = 0; n < mux->num_indexes; n++) {
2902 GstMatroskaIndex *idx = &mux->index[n];
2904 pointentry_master = gst_ebml_write_master_start (ebml,
2905 GST_MATROSKA_ID_POINTENTRY);
2906 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2907 idx->time / mux->time_scale);
2908 trackpos_master = gst_ebml_write_master_start (ebml,
2909 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2910 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2911 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2912 idx->pos - mux->segment_master);
2913 gst_ebml_write_master_finish (ebml, trackpos_master);
2914 gst_ebml_write_master_finish (ebml, pointentry_master);
2917 gst_ebml_write_master_finish (ebml, master);
2918 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2922 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2924 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2925 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2926 guint64 master_tags = 0, master_tag;
2931 GST_DEBUG_OBJECT (mux, "Writing tags");
2934 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2938 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2939 mux->tags_pos = ebml->pos;
2940 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2941 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2944 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2947 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2951 gst_ebml_write_master_finish (ebml, master_tag);
2956 while (cur != NULL) {
2957 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2963 if (master_tags != 0)
2964 gst_ebml_write_master_finish (ebml, master_tags);
2967 /* update seekhead. We know that:
2968 * - a seekhead contains 5 entries.
2969 * - order of entries is as above.
2970 * - a seekhead has a 4-byte header + 8-byte length
2971 * - each entry is 2-byte master, 2-byte ID pointer,
2972 * 2-byte length pointer, all 8/1-byte length, 4-
2973 * byte ID and 8-byte length pointer, where the
2974 * length pointer starts at 20.
2975 * - all entries are local to the segment (so pos - segment_master).
2976 * - so each entry is at 12 + 20 + num * 28. */
2977 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2978 mux->info_pos - mux->segment_master);
2979 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2980 mux->tracks_pos - mux->segment_master);
2981 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2982 && mux->chapters_pos > 0) {
2983 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2984 mux->chapters_pos - mux->segment_master);
2987 guint64 my_pos = ebml->pos;
2989 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2990 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2991 gst_ebml_write_seek (ebml, my_pos);
2993 if (mux->index != NULL) {
2994 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2995 mux->cues_pos - mux->segment_master);
2998 guint64 my_pos = ebml->pos;
3000 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3001 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3002 gst_ebml_write_seek (ebml, my_pos);
3006 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3007 mux->tags_pos - mux->segment_master);
3010 guint64 my_pos = ebml->pos;
3012 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3013 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3014 gst_ebml_write_seek (ebml, my_pos);
3018 * - first get the overall duration
3019 * (a released track may have left a duration in here)
3020 * - write some track header data for subtitles
3022 duration = mux->duration;
3024 for (collected = mux->collect->data; collected;
3025 collected = g_slist_next (collected)) {
3026 GstMatroskaPad *collect_pad;
3027 GstClockTime min_duration; /* observed minimum duration */
3029 collect_pad = (GstMatroskaPad *) collected->data;
3031 GST_DEBUG_OBJECT (mux,
3032 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3033 " end ts %" GST_TIME_FORMAT, collect_pad,
3034 GST_TIME_ARGS (collect_pad->start_ts),
3035 GST_TIME_ARGS (collect_pad->end_ts));
3037 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3038 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3040 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3041 if (collect_pad->duration < min_duration)
3042 collect_pad->duration = min_duration;
3043 GST_DEBUG_OBJECT (collect_pad,
3044 "final track duration: %" GST_TIME_FORMAT,
3045 GST_TIME_ARGS (collect_pad->duration));
3048 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
3049 duration < collect_pad->duration)
3050 duration = collect_pad->duration;
3053 /* seek back (optional, but do anyway) */
3054 gst_ebml_write_seek (ebml, pos);
3056 /* update duration */
3057 if (duration != 0) {
3058 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3059 GST_TIME_ARGS (duration));
3060 pos = mux->ebml_write->pos;
3061 gst_ebml_write_seek (ebml, mux->duration_pos);
3062 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3063 gst_guint64_to_gdouble (duration) /
3064 gst_guint64_to_gdouble (mux->time_scale));
3065 gst_ebml_write_seek (ebml, pos);
3068 guint64 my_pos = ebml->pos;
3070 gst_ebml_write_seek (ebml, mux->duration_pos);
3071 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3072 gst_ebml_write_seek (ebml, my_pos);
3074 GST_DEBUG_OBJECT (mux, "finishing segment");
3075 /* finish segment - this also writes element length */
3076 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3080 * gst_matroska_mux_buffer_header:
3081 * @track: Track context.
3082 * @relative_timestamp: relative timestamp of the buffer
3083 * @flags: Buffer flags.
3085 * Create a buffer containing buffer header.
3087 * Returns: New buffer.
3090 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3091 gint16 relative_timestamp, int flags)
3094 guint8 *data = g_malloc (4);
3096 hdr = gst_buffer_new_wrapped (data, 4);
3097 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3098 data[0] = track->num | 0x80;
3099 /* time relative to clustertime */
3100 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3108 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3109 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3110 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3113 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3114 GstMatroskaPad * collect_pad, GstBuffer * buf)
3116 GstMatroskaTrackVideoContext *ctx =
3117 (GstMatroskaTrackVideoContext *) collect_pad->track;
3122 guint32 next_parse_offset;
3123 GstBuffer *ret = NULL;
3124 gboolean is_muxing_unit = FALSE;
3126 gst_buffer_map (buf, &map, GST_MAP_READ);
3131 gst_buffer_unmap (buf, &map);
3132 gst_buffer_unref (buf);
3136 /* Check if this buffer contains a picture or end-of-sequence packet */
3137 while (size >= 13) {
3138 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3139 gst_buffer_unmap (buf, &map);
3140 gst_buffer_unref (buf);
3144 parse_code = GST_READ_UINT8 (data + 4);
3145 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3146 if (ctx->dirac_unit) {
3147 gst_buffer_unref (ctx->dirac_unit);
3148 ctx->dirac_unit = NULL;
3150 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3151 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3152 is_muxing_unit = TRUE;
3156 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3158 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3161 data += next_parse_offset;
3162 size -= next_parse_offset;
3165 if (ctx->dirac_unit)
3166 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3168 ctx->dirac_unit = gst_buffer_ref (buf);
3170 gst_buffer_unmap (buf, &map);
3172 if (is_muxing_unit) {
3173 ret = gst_buffer_make_writable (ctx->dirac_unit);
3174 ctx->dirac_unit = NULL;
3175 gst_buffer_copy_into (ret, buf,
3176 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3177 gst_buffer_unref (buf);
3179 gst_buffer_unref (buf);
3187 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3191 GValue streamheader = { 0 };
3192 GValue bufval = { 0 };
3193 GstBuffer *streamheader_buffer;
3194 GstEbmlWrite *ebml = mux->ebml_write;
3196 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3197 caps = gst_caps_copy (mux->ebml_write->caps);
3198 s = gst_caps_get_structure (caps, 0);
3199 g_value_init (&streamheader, GST_TYPE_ARRAY);
3200 g_value_init (&bufval, GST_TYPE_BUFFER);
3201 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3202 gst_value_set_buffer (&bufval, streamheader_buffer);
3203 gst_value_array_append_value (&streamheader, &bufval);
3204 g_value_unset (&bufval);
3205 gst_structure_set_value (s, "streamheader", &streamheader);
3206 g_value_unset (&streamheader);
3207 gst_caps_replace (&ebml->caps, caps);
3208 gst_buffer_unref (streamheader_buffer);
3209 gst_pad_set_caps (mux->srcpad, caps);
3210 gst_caps_unref (caps);
3214 * gst_matroska_mux_write_data:
3215 * @mux: #GstMatroskaMux
3216 * @collect_pad: #GstMatroskaPad with the data
3218 * Write collected data (called from gst_matroska_mux_collected).
3220 * Returns: Result of the gst_pad_push issued to write the data.
3222 static GstFlowReturn
3223 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3226 GstEbmlWrite *ebml = mux->ebml_write;
3229 gboolean write_duration;
3230 gint16 relative_timestamp;
3231 gint64 relative_timestamp64;
3232 guint64 block_duration;
3233 gboolean is_video_keyframe = FALSE;
3234 gboolean is_video_invisible = FALSE;
3235 GstMatroskamuxPad *pad;
3239 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3241 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3242 if (collect_pad->track->xiph_headers_to_skip > 0) {
3243 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3244 gst_buffer_unref (buf);
3245 --collect_pad->track->xiph_headers_to_skip;
3249 /* for dirac we have to queue up everything up to a picture unit */
3250 if (collect_pad->track->codec_id != NULL &&
3251 strcmp (collect_pad->track->codec_id,
3252 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3253 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3258 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3259 * this would wreak havoc with time stored in matroska file */
3260 /* TODO: maybe calculate a timestamp by using the previous timestamp
3261 * and default duration */
3262 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3263 GST_WARNING_OBJECT (collect_pad->collect.pad,
3264 "Invalid buffer timestamp; dropping buffer");
3265 gst_buffer_unref (buf);
3269 /* set the timestamp for outgoing buffers */
3270 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3272 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3273 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3274 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3275 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3276 is_video_keyframe = TRUE;
3277 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3278 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3279 || !strcmp (collect_pad->track->codec_id,
3280 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3281 GST_LOG_OBJECT (mux,
3282 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3283 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3284 is_video_invisible = TRUE;
3289 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3290 * or when we may be reaching the limit of the relative timestamp */
3291 if (mux->cluster_time +
3292 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3293 || is_video_keyframe || mux->force_key_unit_event) {
3294 if (!mux->streamable)
3295 gst_ebml_write_master_finish (ebml, mux->cluster);
3297 /* Forward the GstForceKeyUnit event after finishing the cluster */
3298 if (mux->force_key_unit_event) {
3299 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3300 mux->force_key_unit_event = NULL;
3303 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3304 mux->cluster_pos = ebml->pos;
3305 gst_ebml_write_set_cache (ebml, 0x20);
3307 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3308 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3309 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3311 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3312 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3314 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3315 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3316 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3317 mux->prev_cluster_size);
3322 mux->cluster_pos = ebml->pos;
3323 gst_ebml_write_set_cache (ebml, 0x20);
3324 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3325 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3326 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3327 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3328 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3331 /* update duration of this track */
3332 if (GST_BUFFER_DURATION_IS_VALID (buf))
3333 collect_pad->duration += GST_BUFFER_DURATION (buf);
3335 /* We currently write index entries for all video tracks or for the audio
3336 * track in a single-track audio file. This could be improved by keeping the
3337 * index only for the *first* video track. */
3339 /* TODO: index is useful for every track, should contain the number of
3340 * the block in the cluster which contains the timestamp, should also work
3341 * for files with multiple audio tracks.
3343 if (!mux->streamable &&
3344 (is_video_keyframe ||
3345 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3346 (mux->num_streams == 1)))) {
3349 if (mux->min_index_interval != 0) {
3350 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3351 if (mux->index[last_idx].track == collect_pad->track->num)
3356 if (last_idx < 0 || mux->min_index_interval == 0 ||
3357 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3358 >= mux->min_index_interval)) {
3359 GstMatroskaIndex *idx;
3361 if (mux->num_indexes % 32 == 0) {
3362 mux->index = g_renew (GstMatroskaIndex, mux->index,
3363 mux->num_indexes + 32);
3365 idx = &mux->index[mux->num_indexes++];
3367 idx->pos = mux->cluster_pos;
3368 idx->time = GST_BUFFER_TIMESTAMP (buf);
3369 idx->track = collect_pad->track->num;
3373 /* Check if the duration differs from the default duration. */
3374 write_duration = FALSE;
3376 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3377 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3378 1, mux->time_scale);
3380 /* small difference should be ok. */
3381 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3382 block_duration < collect_pad->default_duration_scaled - 1) {
3383 write_duration = TRUE;
3387 /* write the block, for doctype v2 use SimpleBlock if possible
3388 * one slice (*breath*).
3389 * FIXME: Need to do correct lacing! */
3390 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3391 if (relative_timestamp64 >= 0) {
3392 /* round the timestamp */
3393 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3394 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3397 /* round the timestamp */
3398 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3399 relative_timestamp =
3400 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3404 if (is_video_invisible)
3407 if (mux->doctype_version > 1 && !write_duration) {
3408 if (is_video_keyframe)
3412 gst_matroska_mux_create_buffer_header (collect_pad->track,
3413 relative_timestamp, flags);
3414 gst_ebml_write_set_cache (ebml, 0x40);
3415 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3416 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3417 gst_ebml_write_buffer (ebml, hdr);
3418 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3419 gst_ebml_write_buffer (ebml, buf);
3421 return gst_ebml_last_write_result (ebml);
3423 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3424 /* write and call order slightly unnatural,
3425 * but avoids seek and minizes pushing */
3426 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3428 gst_matroska_mux_create_buffer_header (collect_pad->track,
3429 relative_timestamp, flags);
3431 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3432 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3433 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3434 gst_ebml_write_buffer (ebml, hdr);
3435 gst_ebml_write_master_finish_full (ebml, blockgroup,
3436 gst_buffer_get_size (buf));
3437 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3438 gst_ebml_write_buffer (ebml, buf);
3440 return gst_ebml_last_write_result (ebml);
3445 * gst_matroska_mux_handle_buffer:
3446 * @pads: #GstCollectPads
3447 * @uuser_data: #GstMatroskaMux
3449 * Collectpads callback.
3451 * Returns: #GstFlowReturn
3453 static GstFlowReturn
3454 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
3455 GstBuffer * buf, gpointer user_data)
3457 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3458 GstEbmlWrite *ebml = mux->ebml_write;
3459 GstMatroskaPad *best;
3460 GstFlowReturn ret = GST_FLOW_OK;
3462 GST_DEBUG_OBJECT (mux, "Collected pads");
3464 /* start with a header */
3465 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3466 if (mux->collect->data == NULL) {
3467 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3468 ("No input streams configured"));
3469 return GST_FLOW_ERROR;
3471 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3472 gst_ebml_start_streamheader (ebml);
3473 gst_matroska_mux_start (mux);
3474 gst_matroska_mux_stop_streamheader (mux);
3475 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3478 /* provided with stream to write from */
3479 best = (GstMatroskaPad *) data;
3481 /* if there is no best pad, we have reached EOS */
3483 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
3484 if (!mux->streamable) {
3485 gst_matroska_mux_finish (mux);
3487 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3489 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3494 /* if we have a best stream, should also have a buffer */
3497 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3498 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3499 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3500 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3502 /* make note of first and last encountered timestamps, so we can calculate
3503 * the actual duration later when we send an updated header on eos */
3504 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3505 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3506 GstClockTime end_ts = start_ts;
3508 if (GST_BUFFER_DURATION_IS_VALID (buf))
3509 end_ts += GST_BUFFER_DURATION (buf);
3510 else if (best->track->default_duration)
3511 end_ts += best->track->default_duration;
3513 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3514 best->end_ts = end_ts;
3516 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3517 start_ts < best->start_ts))
3518 best->start_ts = start_ts;
3521 /* write one buffer */
3522 ret = gst_matroska_mux_write_data (mux, best, buf);
3530 * gst_matroska_mux_change_state:
3531 * @element: #GstMatroskaMux
3532 * @transition: State change transition.
3534 * Change the muxer state.
3536 * Returns: #GstStateChangeReturn
3538 static GstStateChangeReturn
3539 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3541 GstStateChangeReturn ret;
3542 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3544 switch (transition) {
3545 case GST_STATE_CHANGE_NULL_TO_READY:
3547 case GST_STATE_CHANGE_READY_TO_PAUSED:
3548 gst_collect_pads_start (mux->collect);
3550 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3552 case GST_STATE_CHANGE_PAUSED_TO_READY:
3553 gst_collect_pads_stop (mux->collect);
3559 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3561 switch (transition) {
3562 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3564 case GST_STATE_CHANGE_PAUSED_TO_READY:
3565 gst_matroska_mux_reset (GST_ELEMENT (mux));
3567 case GST_STATE_CHANGE_READY_TO_NULL:
3577 gst_matroska_mux_set_property (GObject * object,
3578 guint prop_id, const GValue * value, GParamSpec * pspec)
3580 GstMatroskaMux *mux;
3582 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3583 mux = GST_MATROSKA_MUX (object);
3586 case ARG_WRITING_APP:
3587 if (!g_value_get_string (value)) {
3588 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3591 g_free (mux->writing_app);
3592 mux->writing_app = g_value_dup_string (value);
3594 case ARG_DOCTYPE_VERSION:
3595 mux->doctype_version = g_value_get_int (value);
3597 case ARG_MIN_INDEX_INTERVAL:
3598 mux->min_index_interval = g_value_get_int64 (value);
3600 case ARG_STREAMABLE:
3601 mux->streamable = g_value_get_boolean (value);
3604 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3610 gst_matroska_mux_get_property (GObject * object,
3611 guint prop_id, GValue * value, GParamSpec * pspec)
3613 GstMatroskaMux *mux;
3615 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3616 mux = GST_MATROSKA_MUX (object);
3619 case ARG_WRITING_APP:
3620 g_value_set_string (value, mux->writing_app);
3622 case ARG_DOCTYPE_VERSION:
3623 g_value_set_int (value, mux->doctype_version);
3625 case ARG_MIN_INDEX_INTERVAL:
3626 g_value_set_int64 (value, mux->min_index_interval);
3628 case ARG_STREAMABLE:
3629 g_value_set_boolean (value, mux->streamable);
3632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);