1 /* GStreamer Matroska muxer/demuxer
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
7 * matroska-mux.c: matroska file/stream muxer
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
30 * SECTION:element-matroskamux
32 * matroskamux muxes different input streams into a Matroska file.
35 * <title>Example launch line</title>
37 * gst-launch-1.0 -v filesrc location=/path/to/mp3 ! mpegaudioparse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
40 * gst-launch-1.0 -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
57 #include "matroska-mux.h"
58 #include "matroska-ids.h"
60 #define GST_MATROSKA_MUX_CHAPLANG "und"
62 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
63 #define GST_CAT_DEFAULT matroskamux_debug
70 ARG_MIN_INDEX_INTERVAL,
74 #define DEFAULT_DOCTYPE_VERSION 2
75 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
76 #define DEFAULT_MIN_INDEX_INTERVAL 0
77 #define DEFAULT_STREAMABLE FALSE
79 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
80 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
82 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
85 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
88 #define COMMON_VIDEO_CAPS \
89 "width = (int) [ 16, 4096 ], " \
90 "height = (int) [ 16, 4096 ] "
93 * * require codec data, etc as needed
96 static GstStaticPadTemplate videosink_templ =
97 GST_STATIC_PAD_TEMPLATE ("video_%u",
100 GST_STATIC_CAPS ("video/mpeg, "
101 "mpegversion = (int) { 1, 2, 4 }, "
102 "systemstream = (boolean) false, "
103 COMMON_VIDEO_CAPS "; "
104 "video/x-h264, stream-format=avc, alignment=au, "
105 COMMON_VIDEO_CAPS "; "
106 "video/x-h265, stream-format=hvc1, alignment=au, "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS "; "
119 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS "; "
123 "video/x-pn-realvideo, "
124 "rmversion = (int) [1, 4], "
125 COMMON_VIDEO_CAPS "; "
127 COMMON_VIDEO_CAPS "; "
129 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
130 COMMON_VIDEO_CAPS "; "
131 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
134 #define COMMON_AUDIO_CAPS \
135 "channels = (int) [ 1, MAX ], " \
136 "rate = (int) [ 1, MAX ]"
139 * * require codec data, etc as needed
141 static GstStaticPadTemplate audiosink_templ =
142 GST_STATIC_PAD_TEMPLATE ("audio_%u",
145 GST_STATIC_CAPS ("audio/mpeg, "
146 "mpegversion = (int) 1, "
147 "layer = (int) [ 1, 3 ], "
148 COMMON_AUDIO_CAPS "; "
150 "mpegversion = (int) { 2, 4 }, "
151 "stream-format = (string) raw, "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
160 COMMON_AUDIO_CAPS "; "
162 COMMON_AUDIO_CAPS "; "
164 COMMON_AUDIO_CAPS "; "
166 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
167 "layout = (string) interleaved, "
168 COMMON_AUDIO_CAPS ";"
170 "width = (int) { 8, 16, 24 }, "
171 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
172 "audio/x-pn-realaudio, "
173 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
174 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
175 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
176 COMMON_AUDIO_CAPS ";"
178 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
180 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
182 "layout = (string)dvi, "
183 "block_align = (int)[64, 8192], "
184 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
186 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
189 static GstStaticPadTemplate subtitlesink_templ =
190 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
193 GST_STATIC_CAPS ("subtitle/x-kate; "
194 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
195 "application/x-usf; subpicture/x-dvd; "
196 "application/x-subtitle-unknown")
199 static GArray *used_uids;
200 G_LOCK_DEFINE_STATIC (used_uids);
202 static gpointer parent_class; /* NULL */
204 /* Matroska muxer destructor */
205 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
206 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
207 static void gst_matroska_mux_finalize (GObject * object);
209 /* Pads collected callback */
210 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
211 GstCollectData * data, GstBuffer * buf, gpointer user_data);
212 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
213 GstCollectData * data, GstEvent * event, gpointer user_data);
216 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
217 GstObject * parent, GstEvent * event);
218 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
219 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
220 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
222 /* gst internal change state handler */
223 static GstStateChangeReturn
224 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
226 /* gobject bla bla */
227 static void gst_matroska_mux_set_property (GObject * object,
228 guint prop_id, const GValue * value, GParamSpec * pspec);
229 static void gst_matroska_mux_get_property (GObject * object,
230 guint prop_id, GValue * value, GParamSpec * pspec);
233 static void gst_matroska_mux_reset (GstElement * element);
236 static guint64 gst_matroska_mux_create_uid ();
238 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
239 GstMatroskaTrackContext * context);
240 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
241 GstMatroskaTrackContext * context);
242 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
243 GstMatroskaTrackContext * context);
244 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
245 GstMatroskaTrackContext * context);
246 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
247 GstMatroskaTrackContext * context);
249 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
252 /* Cannot use boilerplate macros here because we need the full init function
253 * signature with the additional class argument, so we use the right template
254 * for the sink caps */
256 gst_matroska_mux_get_type (void)
258 static GType object_type; /* 0 */
260 if (object_type == 0) {
261 static const GTypeInfo object_info = {
262 sizeof (GstMatroskaMuxClass),
263 NULL, /* base_init */
264 NULL, /* base_finalize */
265 (GClassInitFunc) gst_matroska_mux_class_init,
266 NULL, /* class_finalize */
267 NULL, /* class_data */
268 sizeof (GstMatroskaMux),
270 (GInstanceInitFunc) gst_matroska_mux_init
272 const GInterfaceInfo iface_info = { NULL };
274 object_type = g_type_register_static (GST_TYPE_ELEMENT,
275 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
277 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
278 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
285 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
287 GObjectClass *gobject_class;
288 GstElementClass *gstelement_class;
290 gobject_class = (GObjectClass *) klass;
291 gstelement_class = (GstElementClass *) klass;
293 gst_element_class_add_pad_template (gstelement_class,
294 gst_static_pad_template_get (&videosink_templ));
295 gst_element_class_add_pad_template (gstelement_class,
296 gst_static_pad_template_get (&audiosink_templ));
297 gst_element_class_add_pad_template (gstelement_class,
298 gst_static_pad_template_get (&subtitlesink_templ));
299 gst_element_class_add_pad_template (gstelement_class,
300 gst_static_pad_template_get (&src_templ));
301 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
303 "Muxes video/audio/subtitle streams into a matroska stream",
304 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
306 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
309 gobject_class->finalize = gst_matroska_mux_finalize;
311 gobject_class->get_property = gst_matroska_mux_get_property;
312 gobject_class->set_property = gst_matroska_mux_set_property;
314 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
315 g_param_spec_string ("writing-app", "Writing application.",
316 "The name the application that creates the matroska file.",
317 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
318 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
319 g_param_spec_int ("version", "DocType version",
320 "This parameter determines what Matroska features can be used.",
321 1, 2, DEFAULT_DOCTYPE_VERSION,
322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
323 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
324 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
325 "entries", "An index entry is created every so many nanoseconds.",
326 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
327 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
328 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
329 g_param_spec_boolean ("streamable", "Determines whether output should "
330 "be streamable", "If set to true, the output should be as if it is "
331 "to be streamed and hence no indexes written or duration written.",
333 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
335 gstelement_class->change_state =
336 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
337 gstelement_class->request_new_pad =
338 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
339 gstelement_class->release_pad =
340 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
342 parent_class = g_type_class_peek_parent (klass);
346 * Start of pad option handler code
348 #define DEFAULT_PAD_FRAME_DURATION TRUE
349 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
354 PROP_PAD_FRAME_DURATION
360 gboolean frame_duration;
361 gboolean frame_duration_user;
364 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
367 gst_matroskamux_pad_get_type (void)
369 static GType type = 0;
371 if (G_UNLIKELY (type == 0)) {
372 type = g_type_register_static_simple (GST_TYPE_PAD,
373 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
374 (GClassInitFunc) gst_matroskamux_pad_class_init,
375 sizeof (GstMatroskamuxPad), NULL, 0);
380 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
381 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
382 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
383 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
386 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
387 GValue * value, GParamSpec * pspec)
389 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
392 case PROP_PAD_FRAME_DURATION:
393 g_value_set_boolean (value, pad->frame_duration);
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
403 const GValue * value, GParamSpec * pspec)
405 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
408 case PROP_PAD_FRAME_DURATION:
409 pad->frame_duration = g_value_get_boolean (value);
410 pad->frame_duration_user = TRUE;
413 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419 gst_matroskamux_pad_class_init (GstPadClass * klass)
421 GObjectClass *gobject_class = (GObjectClass *) klass;
423 gobject_class->set_property = gst_matroskamux_pad_set_property;
424 gobject_class->get_property = gst_matroskamux_pad_get_property;
426 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
427 g_param_spec_boolean ("frame-duration", "Frame duration",
428 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
429 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
433 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
435 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
436 pad->frame_duration_user = FALSE;
440 * End of pad option handler code
444 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
446 GstPadTemplate *templ;
449 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
450 mux->srcpad = gst_pad_new_from_template (templ, "src");
452 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
453 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
454 gst_pad_use_fixed_caps (mux->srcpad);
456 mux->collect = gst_collect_pads_new ();
457 gst_collect_pads_set_clip_function (mux->collect,
458 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
459 gst_collect_pads_set_buffer_function (mux->collect,
460 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
461 gst_collect_pads_set_event_function (mux->collect,
462 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
464 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
465 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
467 /* property defaults */
468 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
469 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
470 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
471 mux->streamable = DEFAULT_STREAMABLE;
473 /* initialize internal variables */
475 mux->num_streams = 0;
476 mux->num_a_streams = 0;
477 mux->num_t_streams = 0;
478 mux->num_v_streams = 0;
480 /* initialize remaining variables */
481 gst_matroska_mux_reset (GST_ELEMENT (mux));
486 * gst_matroska_mux_finalize:
487 * @object: #GstMatroskaMux that should be finalized.
489 * Finalize matroska muxer.
492 gst_matroska_mux_finalize (GObject * object)
494 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
496 gst_event_replace (&mux->force_key_unit_event, NULL);
498 gst_object_unref (mux->collect);
499 gst_object_unref (mux->ebml_write);
500 if (mux->writing_app)
501 g_free (mux->writing_app);
503 G_OBJECT_CLASS (parent_class)->finalize (object);
508 * gst_matroska_mux_create_uid:
510 * Generate new unused track UID.
512 * Returns: New track UID.
515 gst_matroska_mux_create_uid (void)
522 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
527 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
528 for (i = 0; i < used_uids->len; i++) {
529 if (g_array_index (used_uids, guint64, i) == uid) {
534 g_array_append_val (used_uids, uid);
537 G_UNLOCK (used_uids);
543 * gst_matroska_pad_reset:
544 * @collect_pad: the #GstMatroskaPad
546 * Reset and/or release resources of a matroska collect pad.
549 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
552 GstMatroskaTrackType type = 0;
554 /* free track information */
555 if (collect_pad->track != NULL) {
556 /* retrieve for optional later use */
557 name = collect_pad->track->name;
558 type = collect_pad->track->type;
559 /* extra for video */
560 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
561 GstMatroskaTrackVideoContext *ctx =
562 (GstMatroskaTrackVideoContext *) collect_pad->track;
564 if (ctx->dirac_unit) {
565 gst_buffer_unref (ctx->dirac_unit);
566 ctx->dirac_unit = NULL;
569 g_free (collect_pad->track->codec_id);
570 g_free (collect_pad->track->codec_name);
572 g_free (collect_pad->track->name);
573 g_free (collect_pad->track->language);
574 g_free (collect_pad->track->codec_priv);
575 g_free (collect_pad->track);
576 collect_pad->track = NULL;
579 if (!full && type != 0) {
580 GstMatroskaTrackContext *context;
582 /* create a fresh context */
584 case GST_MATROSKA_TRACK_TYPE_VIDEO:
585 context = (GstMatroskaTrackContext *)
586 g_new0 (GstMatroskaTrackVideoContext, 1);
588 case GST_MATROSKA_TRACK_TYPE_AUDIO:
589 context = (GstMatroskaTrackContext *)
590 g_new0 (GstMatroskaTrackAudioContext, 1);
592 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
593 context = (GstMatroskaTrackContext *)
594 g_new0 (GstMatroskaTrackSubtitleContext, 1);
597 g_assert_not_reached ();
601 context->type = type;
602 context->name = name;
603 /* TODO: check default values for the context */
604 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
605 collect_pad->track = context;
606 collect_pad->duration = 0;
607 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
608 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
613 * gst_matroska_pad_free:
614 * @collect_pad: the #GstMatroskaPad
616 * Release resources of a matroska collect pad.
619 gst_matroska_pad_free (GstPad * collect_pad)
621 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
626 * gst_matroska_mux_reset:
627 * @element: #GstMatroskaMux that should be reseted.
629 * Reset matroska muxer back to initial state.
632 gst_matroska_mux_reset (GstElement * element)
634 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
637 /* reset EBML write */
638 gst_ebml_write_reset (mux->ebml_write);
641 mux->state = GST_MATROSKA_MUX_STATE_START;
643 /* clean up existing streams */
645 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
646 GstMatroskaPad *collect_pad;
648 collect_pad = (GstMatroskaPad *) walk->data;
650 /* reset collect pad to pristine state */
651 gst_matroska_pad_reset (collect_pad, FALSE);
655 mux->num_indexes = 0;
660 mux->time_scale = GST_MSECOND;
661 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
666 mux->cluster_time = 0;
667 mux->cluster_pos = 0;
668 mux->prev_cluster_size = 0;
671 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
676 gst_toc_setter_reset (GST_TOC_SETTER (mux));
678 mux->chapters_pos = 0;
682 * gst_matroska_mux_handle_src_event:
683 * @pad: Pad which received the event.
684 * @event: Received event.
686 * handle events - copied from oggmux without understanding
688 * Returns: #TRUE on success.
691 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
696 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
700 /* disable seeking for now */
706 return gst_pad_event_default (pad, parent, event);
711 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
713 if (context->codec_priv != NULL) {
714 g_free (context->codec_priv);
715 context->codec_priv = NULL;
716 context->codec_priv_size = 0;
721 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
731 /* produce comma-separated list in hex format */
732 for (i = 0; i < 16; ++i) {
734 /* replicate vobsub's slightly off RGB conversion calculation */
735 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
736 u = ((col >> 8) & 0xff) - 128;
737 v = (col & 0xff) - 128;
738 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
739 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
740 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
741 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
744 sclut = g_strjoinv (",", clutv);
746 /* build codec private; only palette for now */
747 gst_matroska_mux_free_codec_priv (context);
748 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
749 /* include terminating 0 */
750 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
752 for (i = 0; i < 16; ++i) {
759 * gst_matroska_mux_handle_sink_event:
760 * @pad: Pad which received the event.
761 * @event: Received event.
763 * handle events - informational ones like tags
765 * Returns: #TRUE on success.
768 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
769 GstCollectData * data, GstEvent * event, gpointer user_data)
771 GstMatroskaPad *collect_pad;
772 GstMatroskaTrackContext *context;
778 mux = GST_MATROSKA_MUX (user_data);
779 collect_pad = (GstMatroskaPad *) data;
781 context = collect_pad->track;
784 switch (GST_EVENT_TYPE (event)) {
785 case GST_EVENT_CAPS:{
788 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
789 gst_event_parse_caps (event, &caps);
791 ret = collect_pad->capsfunc (pad, caps);
792 gst_event_unref (event);
799 GST_DEBUG_OBJECT (mux, "received tag event");
800 gst_event_parse_tag (event, &list);
802 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
803 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
804 const gchar *lang_code;
806 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
808 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
809 context->language = g_strdup (lang_code);
811 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
816 /* FIXME: what about stream-specific tags? */
817 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
818 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
820 gst_event_unref (event);
821 /* handled this, don't want collectpads to forward it downstream */
827 GstToc *toc, *old_toc;
829 if (mux->chapters_pos > 0)
832 GST_DEBUG_OBJECT (mux, "received toc event");
833 gst_event_parse_toc (event, &toc, NULL);
836 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
837 if (old_toc != NULL) {
839 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
840 gst_toc_unref (old_toc);
843 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
847 gst_event_unref (event);
848 /* handled this, don't want collectpads to forward it downstream */
852 case GST_EVENT_CUSTOM_DOWNSTREAM:{
853 const GstStructure *structure;
855 structure = gst_event_get_structure (event);
856 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
857 gst_event_replace (&mux->force_key_unit_event, NULL);
858 mux->force_key_unit_event = event;
860 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
861 !strcmp ("dvd-spu-clut-change",
862 gst_structure_get_string (structure, "event"))) {
867 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
868 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
869 GST_DEBUG_OBJECT (pad, "... discarding");
872 /* first transform event data into table form */
873 for (i = 0; i < 16; i++) {
874 g_snprintf (name, sizeof (name), "clut%02d", i);
875 if (!gst_structure_get_int (structure, name, &value)) {
876 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
877 "contain %s field", name);
883 /* transform into private data for stream; text form */
884 gst_matroska_mux_build_vobsub_private (context, clut);
893 return gst_collect_pads_event_default (pads, data, event, FALSE);
899 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
902 g_assert (context && id);
903 if (context->codec_id)
904 g_free (context->codec_id);
905 context->codec_id = g_strdup (id);
909 * gst_matroska_mux_video_pad_setcaps:
910 * @pad: Pad which got the caps.
913 * Setcaps function for video sink pad.
915 * Returns: #TRUE on success.
918 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
920 GstMatroskaTrackContext *context = NULL;
921 GstMatroskaTrackVideoContext *videocontext;
923 GstMatroskaPad *collect_pad;
924 GstStructure *structure;
925 const gchar *mimetype;
926 const gchar *interlace_mode;
927 const GValue *value = NULL;
928 GstBuffer *codec_buf = NULL;
929 gint width, height, pixel_width, pixel_height;
932 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
935 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
936 g_assert (collect_pad);
937 context = collect_pad->track;
939 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
940 videocontext = (GstMatroskaTrackVideoContext *) context;
942 /* gst -> matroska ID'ing */
943 structure = gst_caps_get_structure (caps, 0);
945 mimetype = gst_structure_get_name (structure);
947 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
948 if (interlace_mode != NULL && strcmp (interlace_mode, "progressive") != 0)
949 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
951 if (!strcmp (mimetype, "video/x-theora")) {
952 /* we'll extract the details later from the theora identification header */
956 /* get general properties */
957 /* spec says it is mandatory */
958 if (!gst_structure_get_int (structure, "width", &width) ||
959 !gst_structure_get_int (structure, "height", &height))
962 videocontext->pixel_width = width;
963 videocontext->pixel_height = height;
965 /* set vp8 defaults or let user override it */
966 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
967 && (!strcmp (mimetype, "video/x-vp8")
968 || !strcmp (mimetype, "video/x-vp9")))
969 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
970 DEFAULT_PAD_FRAME_DURATION_VP8;
972 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
973 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
975 context->default_duration =
976 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
977 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
978 GST_TIME_ARGS (context->default_duration));
980 context->default_duration = 0;
982 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
983 &pixel_width, &pixel_height)) {
984 if (pixel_width > pixel_height) {
985 videocontext->display_width = width * pixel_width / pixel_height;
986 videocontext->display_height = height;
987 } else if (pixel_width < pixel_height) {
988 videocontext->display_width = width;
989 videocontext->display_height = height * pixel_height / pixel_width;
991 videocontext->display_width = 0;
992 videocontext->display_height = 0;
995 videocontext->display_width = 0;
996 videocontext->display_height = 0;
1001 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1002 videocontext->fourcc = 0;
1004 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1005 * data and other settings
1009 /* extract codec_data, may turn out needed */
1010 value = gst_structure_get_value (structure, "codec_data");
1012 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1015 if (!strcmp (mimetype, "video/x-raw")) {
1017 gst_matroska_mux_set_codec_id (context,
1018 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1019 fstr = gst_structure_get_string (structure, "format");
1020 if (fstr && strlen (fstr) == 4)
1021 videocontext->fourcc = GST_STR_FOURCC (fstr);
1022 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1023 ||!strcmp (mimetype, "video/x-divx")
1024 || !strcmp (mimetype, "video/x-dv")
1025 || !strcmp (mimetype, "video/x-h263")
1026 || !strcmp (mimetype, "video/x-msmpeg")
1027 || !strcmp (mimetype, "video/x-wmv")
1028 || !strcmp (mimetype, "image/jpeg")) {
1029 gst_riff_strf_vids *bih;
1030 gint size = sizeof (gst_riff_strf_vids);
1033 if (!strcmp (mimetype, "video/x-huffyuv"))
1034 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1035 else if (!strcmp (mimetype, "video/x-dv"))
1036 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1037 else if (!strcmp (mimetype, "video/x-h263"))
1038 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1039 else if (!strcmp (mimetype, "video/x-divx")) {
1042 gst_structure_get_int (structure, "divxversion", &divxversion);
1043 switch (divxversion) {
1045 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1048 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1051 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1054 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1057 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1058 switch (msmpegversion) {
1060 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1063 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1069 } else if (!strcmp (mimetype, "video/x-wmv")) {
1073 fstr = gst_structure_get_string (structure, "format");
1074 if (fstr && strlen (fstr) == 4) {
1075 fourcc = GST_STR_FOURCC (fstr);
1076 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1077 if (wmvversion == 2) {
1078 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1079 } else if (wmvversion == 1) {
1080 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1081 } else if (wmvversion == 3) {
1082 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1085 } else if (!strcmp (mimetype, "image/jpeg")) {
1086 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1092 bih = g_new0 (gst_riff_strf_vids, 1);
1093 GST_WRITE_UINT32_LE (&bih->size, size);
1094 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1095 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1096 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1097 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1098 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1099 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1100 videocontext->pixel_height * 3);
1102 /* process codec private/initialization data, if any */
1104 size += gst_buffer_get_size (codec_buf);
1105 bih = g_realloc (bih, size);
1106 GST_WRITE_UINT32_LE (&bih->size, size);
1107 gst_buffer_extract (codec_buf, 0,
1108 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1111 gst_matroska_mux_set_codec_id (context,
1112 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1113 gst_matroska_mux_free_codec_priv (context);
1114 context->codec_priv = (gpointer) bih;
1115 context->codec_priv_size = size;
1116 } else if (!strcmp (mimetype, "video/x-h264")) {
1117 gst_matroska_mux_set_codec_id (context,
1118 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1119 gst_matroska_mux_free_codec_priv (context);
1120 /* Create avcC header */
1121 if (codec_buf != NULL) {
1122 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1123 context->codec_priv = g_malloc0 (context->codec_priv_size);
1124 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1126 } else if (!strcmp (mimetype, "video/x-h265")) {
1127 gst_matroska_mux_set_codec_id (context,
1128 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1129 gst_matroska_mux_free_codec_priv (context);
1130 /* Create hvcC 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-theora")) {
1137 const GValue *streamheader;
1139 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1141 gst_matroska_mux_free_codec_priv (context);
1143 streamheader = gst_structure_get_value (structure, "streamheader");
1144 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1145 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1146 ("theora stream headers missing or malformed"));
1149 } else if (!strcmp (mimetype, "video/x-dirac")) {
1150 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1151 } else if (!strcmp (mimetype, "video/x-vp8")) {
1152 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1153 } else if (!strcmp (mimetype, "video/x-vp9")) {
1154 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1155 } else if (!strcmp (mimetype, "video/mpeg")) {
1158 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1159 switch (mpegversion) {
1161 gst_matroska_mux_set_codec_id (context,
1162 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1165 gst_matroska_mux_set_codec_id (context,
1166 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1169 gst_matroska_mux_set_codec_id (context,
1170 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1176 /* global headers may be in codec data */
1177 if (codec_buf != NULL) {
1178 gst_matroska_mux_free_codec_priv (context);
1179 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1180 context->codec_priv = g_malloc0 (context->codec_priv_size);
1181 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1183 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1185 /* can only make it here if preceding case verified it was version 3 */
1186 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1187 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1189 const GValue *mdpr_data;
1191 gst_structure_get_int (structure, "rmversion", &rmversion);
1192 switch (rmversion) {
1194 gst_matroska_mux_set_codec_id (context,
1195 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1198 gst_matroska_mux_set_codec_id (context,
1199 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1202 gst_matroska_mux_set_codec_id (context,
1203 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1206 gst_matroska_mux_set_codec_id (context,
1207 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1213 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1214 if (mdpr_data != NULL) {
1215 guint8 *priv_data = NULL;
1216 guint priv_data_size = 0;
1218 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1220 priv_data_size = gst_buffer_get_size (codec_data_buf);
1221 priv_data = g_malloc0 (priv_data_size);
1223 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1225 gst_matroska_mux_free_codec_priv (context);
1226 context->codec_priv = priv_data;
1227 context->codec_priv_size = priv_data_size;
1236 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1237 GST_PAD_NAME (pad), caps);
1242 /* N > 0 to expect a particular number of headers, negative if the
1243 number of headers is variable */
1245 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1246 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1248 GstBuffer **buf = NULL;
1251 guint bufi, i, offset, priv_data_size;
1253 if (streamheader == NULL)
1254 goto no_stream_headers;
1256 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1259 bufarr = g_value_peek_pointer (streamheader);
1260 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1262 if (N > 0 && bufarr->len != N)
1265 context->xiph_headers_to_skip = bufarr->len;
1267 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1268 for (i = 0; i < bufarr->len; i++) {
1269 GValue *bufval = &g_array_index (bufarr, GValue, i);
1271 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1273 goto wrong_content_type;
1276 buf[i] = g_value_peek_pointer (bufval);
1280 if (bufarr->len > 0) {
1281 for (i = 0; i < bufarr->len - 1; i++) {
1282 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1286 for (i = 0; i < bufarr->len; ++i) {
1287 priv_data_size += gst_buffer_get_size (buf[i]);
1290 priv_data = g_malloc0 (priv_data_size);
1292 priv_data[0] = bufarr->len - 1;
1295 if (bufarr->len > 0) {
1296 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1297 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1298 priv_data[offset++] = 0xff;
1300 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1304 for (i = 0; i < bufarr->len; ++i) {
1305 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1306 offset += gst_buffer_get_size (buf[i]);
1309 gst_matroska_mux_free_codec_priv (context);
1310 context->codec_priv = priv_data;
1311 context->codec_priv_size = priv_data_size;
1314 *p_buf0 = gst_buffer_ref (buf[0]);
1323 GST_WARNING ("required streamheaders missing in sink caps!");
1328 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1329 G_VALUE_TYPE_NAME (streamheader));
1334 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1339 GST_WARNING ("streamheaders array does not contain GstBuffers");
1345 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1346 GstMatroskaTrackContext * context)
1348 GstBuffer *buf0 = NULL;
1350 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1353 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1354 GST_WARNING ("First vorbis header too small, ignoring");
1356 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1357 GstMatroskaTrackAudioContext *audiocontext;
1361 gst_buffer_map (buf0, &map, GST_MAP_READ);
1362 hdr = map.data + 1 + 6 + 4;
1363 audiocontext = (GstMatroskaTrackAudioContext *) context;
1364 audiocontext->channels = GST_READ_UINT8 (hdr);
1365 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1366 gst_buffer_unmap (buf0, &map);
1371 gst_buffer_unref (buf0);
1377 theora_streamheader_to_codecdata (const GValue * streamheader,
1378 GstMatroskaTrackContext * context)
1380 GstBuffer *buf0 = NULL;
1382 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1385 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1386 GST_WARNING ("First theora header too small, ignoring");
1387 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1388 GST_WARNING ("First header not a theora identification header, ignoring");
1390 GstMatroskaTrackVideoContext *videocontext;
1391 guint fps_num, fps_denom, par_num, par_denom;
1395 gst_buffer_map (buf0, &map, GST_MAP_READ);
1396 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1398 videocontext = (GstMatroskaTrackVideoContext *) context;
1399 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1400 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1401 hdr += 3 + 3 + 1 + 1;
1402 fps_num = GST_READ_UINT32_BE (hdr);
1403 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1404 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1405 fps_denom, fps_num);
1407 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1408 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1409 if (par_num > 0 && par_num > 0) {
1410 if (par_num > par_denom) {
1411 videocontext->display_width =
1412 videocontext->pixel_width * par_num / par_denom;
1413 videocontext->display_height = videocontext->pixel_height;
1414 } else if (par_num < par_denom) {
1415 videocontext->display_width = videocontext->pixel_width;
1416 videocontext->display_height =
1417 videocontext->pixel_height * par_denom / par_num;
1419 videocontext->display_width = 0;
1420 videocontext->display_height = 0;
1423 videocontext->display_width = 0;
1424 videocontext->display_height = 0;
1428 gst_buffer_unmap (buf0, &map);
1432 gst_buffer_unref (buf0);
1438 kate_streamheader_to_codecdata (const GValue * streamheader,
1439 GstMatroskaTrackContext * context)
1441 GstBuffer *buf0 = NULL;
1443 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1446 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1447 GST_WARNING ("First kate header too small, ignoring");
1448 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1449 GST_WARNING ("First header not a kate identification header, ignoring");
1453 gst_buffer_unref (buf0);
1459 flac_streamheader_to_codecdata (const GValue * streamheader,
1460 GstMatroskaTrackContext * context)
1467 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1468 GST_WARNING ("No or invalid streamheader field in the caps");
1472 bufarr = g_value_peek_pointer (streamheader);
1473 if (bufarr->len < 2) {
1474 GST_WARNING ("Too few headers in streamheader field");
1478 context->xiph_headers_to_skip = bufarr->len + 1;
1480 bufval = &g_array_index (bufarr, GValue, 0);
1481 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1482 GST_WARNING ("streamheaders array does not contain GstBuffers");
1486 buffer = g_value_peek_pointer (bufval);
1488 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1489 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1490 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1491 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1492 GST_WARNING ("Invalid streamheader for FLAC");
1496 gst_matroska_mux_free_codec_priv (context);
1497 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1498 context->codec_priv = g_malloc (context->codec_priv_size);
1499 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1501 for (i = 1; i < bufarr->len; i++) {
1503 bufval = &g_array_index (bufarr, GValue, i);
1505 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1506 gst_matroska_mux_free_codec_priv (context);
1507 GST_WARNING ("streamheaders array does not contain GstBuffers");
1511 buffer = g_value_peek_pointer (bufval);
1513 old_size = context->codec_priv_size;
1514 context->codec_priv_size += gst_buffer_get_size (buffer);
1516 context->codec_priv = g_realloc (context->codec_priv,
1517 context->codec_priv_size);
1518 gst_buffer_extract (buffer, 0,
1519 (guint8 *) context->codec_priv + old_size, -1);
1526 speex_streamheader_to_codecdata (const GValue * streamheader,
1527 GstMatroskaTrackContext * context)
1534 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1535 GST_WARNING ("No or invalid streamheader field in the caps");
1539 bufarr = g_value_peek_pointer (streamheader);
1540 if (bufarr->len != 2) {
1541 GST_WARNING ("Too few headers in streamheader field");
1545 context->xiph_headers_to_skip = bufarr->len + 1;
1547 bufval = &g_array_index (bufarr, GValue, 0);
1548 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1549 GST_WARNING ("streamheaders array does not contain GstBuffers");
1553 buffer = g_value_peek_pointer (bufval);
1555 if (gst_buffer_get_size (buffer) < 80
1556 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1557 GST_WARNING ("Invalid streamheader for Speex");
1561 gst_matroska_mux_free_codec_priv (context);
1562 context->codec_priv_size = gst_buffer_get_size (buffer);
1563 context->codec_priv = g_malloc (context->codec_priv_size);
1564 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1566 bufval = &g_array_index (bufarr, GValue, 1);
1568 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1569 gst_matroska_mux_free_codec_priv (context);
1570 GST_WARNING ("streamheaders array does not contain GstBuffers");
1574 buffer = g_value_peek_pointer (bufval);
1576 old_size = context->codec_priv_size;
1577 context->codec_priv_size += gst_buffer_get_size (buffer);
1578 context->codec_priv = g_realloc (context->codec_priv,
1579 context->codec_priv_size);
1580 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1585 static const gchar *
1586 aac_codec_data_to_codec_id (GstBuffer * buf)
1588 const gchar *result;
1591 /* default to MAIN */
1594 if (gst_buffer_get_size (buf) >= 2) {
1595 gst_buffer_extract (buf, 0, &profile, 1);
1613 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1622 * gst_matroska_mux_audio_pad_setcaps:
1623 * @pad: Pad which got the caps.
1626 * Setcaps function for audio sink pad.
1628 * Returns: #TRUE on success.
1631 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1633 GstMatroskaTrackContext *context = NULL;
1634 GstMatroskaTrackAudioContext *audiocontext;
1635 GstMatroskaMux *mux;
1636 GstMatroskaPad *collect_pad;
1637 const gchar *mimetype;
1638 gint samplerate = 0, channels = 0;
1639 GstStructure *structure;
1640 const GValue *codec_data = NULL;
1641 GstBuffer *buf = NULL;
1642 const gchar *stream_format = NULL;
1644 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1647 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1648 g_assert (collect_pad);
1649 context = collect_pad->track;
1651 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1652 audiocontext = (GstMatroskaTrackAudioContext *) context;
1654 structure = gst_caps_get_structure (caps, 0);
1655 mimetype = gst_structure_get_name (structure);
1658 gst_structure_get_int (structure, "rate", &samplerate);
1659 gst_structure_get_int (structure, "channels", &channels);
1661 audiocontext->samplerate = samplerate;
1662 audiocontext->channels = channels;
1663 audiocontext->bitdepth = 0;
1664 context->default_duration = 0;
1666 codec_data = gst_structure_get_value (structure, "codec_data");
1668 buf = gst_value_get_buffer (codec_data);
1670 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1671 * data and other settings
1675 if (!strcmp (mimetype, "audio/mpeg")) {
1676 gint mpegversion = 0;
1678 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1679 switch (mpegversion) {
1685 gst_structure_get_int (structure, "layer", &layer);
1687 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1688 GST_WARNING_OBJECT (mux,
1689 "Unable to determine MPEG audio version, assuming 1");
1695 else if (layer == 2)
1697 else if (version == 2)
1702 context->default_duration =
1703 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1707 gst_matroska_mux_set_codec_id (context,
1708 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1711 gst_matroska_mux_set_codec_id (context,
1712 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1715 gst_matroska_mux_set_codec_id (context,
1716 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1725 stream_format = gst_structure_get_string (structure, "stream-format");
1726 /* check this is raw aac */
1727 if (stream_format) {
1728 if (strcmp (stream_format, "raw") != 0) {
1729 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1733 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1738 if (mpegversion == 2)
1740 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1741 aac_codec_data_to_codec_id (buf));
1742 else if (mpegversion == 4)
1744 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1745 aac_codec_data_to_codec_id (buf));
1747 g_assert_not_reached ();
1749 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1756 } else if (!strcmp (mimetype, "audio/x-raw")) {
1759 gst_audio_info_init (&info);
1760 if (!gst_audio_info_from_caps (&info, caps)) {
1761 GST_DEBUG_OBJECT (mux,
1762 "broken caps, rejected by gst_audio_info_from_caps");
1766 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1767 case GST_AUDIO_FORMAT_U8:
1768 case GST_AUDIO_FORMAT_S16BE:
1769 case GST_AUDIO_FORMAT_S16LE:
1770 case GST_AUDIO_FORMAT_S24BE:
1771 case GST_AUDIO_FORMAT_S24LE:
1772 case GST_AUDIO_FORMAT_S32BE:
1773 case GST_AUDIO_FORMAT_S32LE:
1774 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1775 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1778 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1779 gst_matroska_mux_set_codec_id (context,
1780 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1782 gst_matroska_mux_set_codec_id (context,
1783 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1785 case GST_AUDIO_FORMAT_F32LE:
1786 case GST_AUDIO_FORMAT_F64LE:
1787 gst_matroska_mux_set_codec_id (context,
1788 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1792 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1796 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1797 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1798 const GValue *streamheader;
1800 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1802 gst_matroska_mux_free_codec_priv (context);
1804 streamheader = gst_structure_get_value (structure, "streamheader");
1805 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1806 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1807 ("vorbis stream headers missing or malformed"));
1810 } else if (!strcmp (mimetype, "audio/x-flac")) {
1811 const GValue *streamheader;
1813 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1815 gst_matroska_mux_free_codec_priv (context);
1817 streamheader = gst_structure_get_value (structure, "streamheader");
1818 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1819 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1820 ("flac stream headers missing or malformed"));
1823 } else if (!strcmp (mimetype, "audio/x-speex")) {
1824 const GValue *streamheader;
1826 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1827 gst_matroska_mux_free_codec_priv (context);
1829 streamheader = gst_structure_get_value (structure, "streamheader");
1830 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1831 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1832 ("speex stream headers missing or malformed"));
1835 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1836 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1837 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1838 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1839 } else if (!strcmp (mimetype, "audio/x-dts")) {
1840 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1841 } else if (!strcmp (mimetype, "audio/x-tta")) {
1844 /* TTA frame duration */
1845 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1847 gst_structure_get_int (structure, "width", &width);
1848 audiocontext->bitdepth = width;
1849 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1851 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1853 const GValue *mdpr_data;
1855 gst_structure_get_int (structure, "raversion", &raversion);
1856 switch (raversion) {
1858 gst_matroska_mux_set_codec_id (context,
1859 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1862 gst_matroska_mux_set_codec_id (context,
1863 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1866 gst_matroska_mux_set_codec_id (context,
1867 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1873 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1874 if (mdpr_data != NULL) {
1875 guint8 *priv_data = NULL;
1876 guint priv_data_size = 0;
1878 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1880 priv_data_size = gst_buffer_get_size (codec_data_buf);
1881 priv_data = g_malloc0 (priv_data_size);
1883 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1885 gst_matroska_mux_free_codec_priv (context);
1887 context->codec_priv = priv_data;
1888 context->codec_priv_size = priv_data_size;
1891 } else if (!strcmp (mimetype, "audio/x-wma")
1892 || !strcmp (mimetype, "audio/x-alaw")
1893 || !strcmp (mimetype, "audio/x-mulaw")
1894 || !strcmp (mimetype, "audio/x-adpcm")) {
1896 guint codec_priv_size;
1901 if (samplerate == 0 || channels == 0) {
1902 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1906 if (!strcmp (mimetype, "audio/x-wma")) {
1910 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1911 || !gst_structure_get_int (structure, "block_align", &block_align)
1912 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1913 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1918 switch (wmaversion) {
1920 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1923 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1926 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1929 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1933 if (gst_structure_get_int (structure, "depth", &depth))
1934 audiocontext->bitdepth = depth;
1935 } else if (!strcmp (mimetype, "audio/x-alaw")
1936 || !strcmp (mimetype, "audio/x-mulaw")) {
1937 audiocontext->bitdepth = 8;
1938 if (!strcmp (mimetype, "audio/x-alaw"))
1939 format = GST_RIFF_WAVE_FORMAT_ALAW;
1941 format = GST_RIFF_WAVE_FORMAT_MULAW;
1943 block_align = channels;
1944 bitrate = block_align * samplerate;
1945 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
1948 layout = gst_structure_get_string (structure, "layout");
1950 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
1954 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
1955 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
1959 if (!strcmp (layout, "dvi")) {
1960 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
1961 } else if (!strcmp (layout, "g726")) {
1962 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
1963 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
1964 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
1968 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
1973 g_assert (format != 0);
1975 codec_priv_size = WAVEFORMATEX_SIZE;
1977 codec_priv_size += gst_buffer_get_size (buf);
1979 /* serialize waveformatex structure */
1980 codec_priv = g_malloc0 (codec_priv_size);
1981 GST_WRITE_UINT16_LE (codec_priv, format);
1982 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1983 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1984 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1985 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1986 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1988 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1990 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1992 /* process codec private/initialization data, if any */
1994 gst_buffer_extract (buf, 0,
1995 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1998 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1999 gst_matroska_mux_free_codec_priv (context);
2000 context->codec_priv = (gpointer) codec_priv;
2001 context->codec_priv_size = codec_priv_size;
2009 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2010 GST_PAD_NAME (pad), caps);
2015 /* we probably don't have the data at start,
2016 * so have to reserve (a maximum) space to write this at the end.
2017 * bit spacy, but some formats can hold quite some */
2018 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2021 * gst_matroska_mux_subtitle_pad_setcaps:
2022 * @pad: Pad which got the caps.
2025 * Setcaps function for subtitle sink pad.
2027 * Returns: #TRUE on success.
2030 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2032 /* There is now (at least) one such alement (kateenc), and I'm going
2033 to handle it here and claim it works when it can be piped back
2034 through GStreamer and VLC */
2036 GstMatroskaTrackContext *context = NULL;
2037 GstMatroskaTrackSubtitleContext *scontext;
2038 GstMatroskaMux *mux;
2039 GstMatroskaPad *collect_pad;
2040 const gchar *mimetype;
2041 GstStructure *structure;
2042 const GValue *value = NULL;
2043 GstBuffer *buf = NULL;
2044 gboolean ret = TRUE;
2046 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2049 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2050 g_assert (collect_pad);
2051 context = collect_pad->track;
2053 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2054 scontext = (GstMatroskaTrackSubtitleContext *) context;
2056 structure = gst_caps_get_structure (caps, 0);
2057 mimetype = gst_structure_get_name (structure);
2060 scontext->check_utf8 = 1;
2061 scontext->invalid_utf8 = 0;
2062 context->default_duration = 0;
2064 if (!strcmp (mimetype, "subtitle/x-kate")) {
2065 const GValue *streamheader;
2067 gst_matroska_mux_set_codec_id (context,
2068 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2070 gst_matroska_mux_free_codec_priv (context);
2072 streamheader = gst_structure_get_value (structure, "streamheader");
2073 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2074 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2075 ("kate stream headers missing or malformed"));
2079 } else if (!strcmp (mimetype, "text/x-raw")) {
2080 gst_matroska_mux_set_codec_id (context,
2081 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2082 } else if (!strcmp (mimetype, "application/x-ssa")) {
2083 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2084 } else if (!strcmp (mimetype, "application/x-ass")) {
2085 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2086 } else if (!strcmp (mimetype, "application/x-usf")) {
2087 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2088 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2089 gst_matroska_mux_set_codec_id (context,
2090 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2096 /* maybe some private data, e.g. vobsub */
2097 value = gst_structure_get_value (structure, "codec_data");
2099 buf = gst_value_get_buffer (value);
2102 guint8 *priv_data = NULL;
2104 gst_buffer_map (buf, &map, GST_MAP_READ);
2106 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2107 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2108 " exceeded maximum (%d); discarding", pad,
2109 SUBTITLE_MAX_CODEC_PRIVATE);
2110 gst_buffer_unmap (buf, &map);
2114 gst_matroska_mux_free_codec_priv (context);
2116 priv_data = g_malloc0 (map.size);
2117 memcpy (priv_data, map.data, map.size);
2118 context->codec_priv = priv_data;
2119 context->codec_priv_size = map.size;
2120 gst_buffer_unmap (buf, &map);
2123 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2124 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2133 * gst_matroska_mux_request_new_pad:
2134 * @element: #GstMatroskaMux.
2135 * @templ: #GstPadTemplate.
2136 * @pad_name: New pad name.
2138 * Request pad function for sink templates.
2140 * Returns: New #GstPad.
2143 gst_matroska_mux_request_new_pad (GstElement * element,
2144 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2146 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2147 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2148 GstMatroskaPad *collect_pad;
2149 GstMatroskamuxPad *newpad;
2151 const gchar *pad_name = NULL;
2152 GstMatroskaCapsFunc capsfunc = NULL;
2153 GstMatroskaTrackContext *context = NULL;
2155 gboolean locked = TRUE;
2158 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2159 /* don't mix named and unnamed pads, if the pad already exists we fail when
2160 * trying to add it */
2161 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2162 pad_name = req_name;
2164 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2167 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2168 context = (GstMatroskaTrackContext *)
2169 g_new0 (GstMatroskaTrackAudioContext, 1);
2170 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2171 context->name = g_strdup ("Audio");
2172 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2173 /* don't mix named and unnamed pads, if the pad already exists we fail when
2174 * trying to add it */
2175 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2176 pad_name = req_name;
2178 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2181 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2182 context = (GstMatroskaTrackContext *)
2183 g_new0 (GstMatroskaTrackVideoContext, 1);
2184 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2185 context->name = g_strdup ("Video");
2186 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2187 /* don't mix named and unnamed pads, if the pad already exists we fail when
2188 * trying to add it */
2189 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2190 pad_name = req_name;
2192 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2195 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2196 context = (GstMatroskaTrackContext *)
2197 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2198 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2199 context->name = g_strdup ("Subtitle");
2200 /* setcaps may only provide proper one a lot later */
2201 id = g_strdup ("S_SUB_UNKNOWN");
2204 GST_WARNING_OBJECT (mux, "This is not our template!");
2208 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2209 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2212 gst_matroskamux_pad_init (newpad);
2213 collect_pad = (GstMatroskaPad *)
2214 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2215 sizeof (GstMatroskamuxPad),
2216 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2218 collect_pad->track = context;
2219 gst_matroska_pad_reset (collect_pad, FALSE);
2220 collect_pad->track->codec_id = id;
2222 collect_pad->capsfunc = capsfunc;
2223 gst_pad_set_active (GST_PAD (newpad), TRUE);
2224 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2225 goto pad_add_failed;
2229 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2231 return GST_PAD (newpad);
2236 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2237 gst_object_unref (newpad);
2243 * gst_matroska_mux_release_pad:
2244 * @element: #GstMatroskaMux.
2245 * @pad: Pad to release.
2247 * Release a previously requested pad.
2250 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2252 GstMatroskaMux *mux;
2255 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2257 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2258 GstCollectData *cdata = (GstCollectData *) walk->data;
2259 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2261 if (cdata->pad == pad) {
2262 GstClockTime min_dur; /* observed minimum duration */
2264 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2265 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2266 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2267 if (collect_pad->duration < min_dur)
2268 collect_pad->duration = min_dur;
2271 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2272 mux->duration < collect_pad->duration)
2273 mux->duration = collect_pad->duration;
2279 gst_collect_pads_remove_pad (mux->collect, pad);
2280 if (gst_element_remove_pad (element, pad))
2286 * gst_matroska_mux_track_header:
2287 * @mux: #GstMatroskaMux
2288 * @context: Tack context.
2290 * Write a track header.
2293 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2294 GstMatroskaTrackContext * context)
2296 GstEbmlWrite *ebml = mux->ebml_write;
2299 /* TODO: check if everything necessary is written and check default values */
2301 /* track type goes before the type-specific stuff */
2302 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2303 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2305 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2306 gst_matroska_mux_create_uid ());
2307 if (context->default_duration) {
2308 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2309 context->default_duration);
2311 if (context->language) {
2312 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2316 /* FIXME: until we have a nice way of getting the codecname
2317 * out of the caps, I'm not going to enable this. Too much
2318 * (useless, double, boring) work... */
2319 /* TODO: Use value from tags if any */
2320 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2321 context->codec_name); */
2322 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2324 /* type-specific stuff */
2325 switch (context->type) {
2326 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2327 GstMatroskaTrackVideoContext *videocontext =
2328 (GstMatroskaTrackVideoContext *) context;
2330 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2331 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2332 videocontext->pixel_width);
2333 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2334 videocontext->pixel_height);
2335 if (videocontext->display_width && videocontext->display_height) {
2336 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2337 videocontext->display_width);
2338 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2339 videocontext->display_height);
2341 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2342 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2343 if (videocontext->fourcc) {
2344 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2346 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2347 (gpointer) & fcc_le, 4);
2349 gst_ebml_write_master_finish (ebml, master);
2354 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2355 GstMatroskaTrackAudioContext *audiocontext =
2356 (GstMatroskaTrackAudioContext *) context;
2358 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2359 if (audiocontext->samplerate != 8000)
2360 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2361 audiocontext->samplerate);
2362 if (audiocontext->channels != 1)
2363 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2364 audiocontext->channels);
2365 if (audiocontext->bitdepth) {
2366 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2367 audiocontext->bitdepth);
2369 gst_ebml_write_master_finish (ebml, master);
2374 /* this is what we write for now and must be filled
2375 * and remainder void'ed later on */
2376 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2378 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2381 /* If codec_id and codec data are already known, we can write
2382 them now, as for audio/video */
2383 if (context->codec_id && context->codec_priv)
2386 context->pos = ebml->pos;
2388 /* CodecID is mandatory ... */
2389 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2391 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2392 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2393 SUBTITLE_MAX_CODEC_PRIVATE);
2395 /* real data has to be written at finish */
2399 /* doesn't need type-specific data */
2403 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2404 if (context->codec_priv)
2405 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2406 context->codec_priv, context->codec_priv_size);
2411 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2413 guint64 title_master;
2416 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2418 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2419 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2420 GST_MATROSKA_MUX_CHAPLANG);
2422 gst_ebml_write_master_finish (ebml, title_master);
2426 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2427 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2428 guint64 * master_edition)
2430 guint64 uid, master_chapteratom;
2432 GstTocEntry *cur_entry;
2437 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2439 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2441 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2442 /* create uid for the parent */
2443 uid = gst_matroska_mux_create_uid ();
2444 g_free (edition->uid);
2445 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2448 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2450 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2451 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2452 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2453 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2456 uid = gst_matroska_mux_create_uid ();
2457 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2459 master_chapteratom =
2460 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2461 g_free (entry->uid);
2462 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2463 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2464 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2465 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2466 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2467 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2469 cur = entry->subentries;
2470 while (cur != NULL) {
2471 cur_entry = cur->data;
2472 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2477 if (G_LIKELY (entry->tags != NULL)) {
2478 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2480 for (i = 0; i < count; ++i) {
2481 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2482 gst_matroska_mux_write_chapter_title (title, ebml);
2486 /* remove title tag */
2487 if (G_LIKELY (count > 0))
2488 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2491 gst_ebml_write_master_finish (ebml, master_chapteratom);
2495 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2496 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2498 guint64 master_edition = 0;
2500 GstTocEntry *subentry;
2502 cur = gst_toc_entry_get_sub_entries (entry);
2503 while (cur != NULL) {
2504 subentry = cur->data;
2505 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2511 if (G_LIKELY (master_edition != 0))
2512 gst_ebml_write_master_finish (ebml, master_edition);
2517 * gst_matroska_mux_start:
2518 * @mux: #GstMatroskaMux
2520 * Start a new matroska file (write headers etc...)
2523 gst_matroska_mux_start (GstMatroskaMux * mux)
2525 GstEbmlWrite *ebml = mux->ebml_write;
2526 const gchar *doctype;
2527 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2528 GST_MATROSKA_ID_TRACKS,
2529 GST_MATROSKA_ID_CHAPTERS,
2530 GST_MATROSKA_ID_CUES,
2531 GST_MATROSKA_ID_TAGS,
2534 const gchar *media_type;
2535 gboolean audio_only;
2536 guint64 master, child;
2540 GstClockTime duration = 0;
2541 guint32 segment_uid[4];
2542 GTimeVal time = { 0, 0 };
2548 /* if not streaming, check if downstream is seekable */
2549 if (!mux->streamable) {
2553 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2554 if (gst_pad_peer_query (mux->srcpad, query)) {
2555 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2556 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2558 /* have to assume seeking is supported if query not handled downstream */
2559 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2563 mux->streamable = TRUE;
2564 g_object_notify (G_OBJECT (mux), "streamable");
2565 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2566 "streamable=false. Will ignore that and create streamable output "
2569 gst_query_unref (query);
2572 /* stream-start (FIXME: create id based on input ids) */
2573 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
2574 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
2577 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
2579 media_type = (audio_only) ? "audio/webm" : "video/webm";
2581 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
2583 ebml->caps = gst_caps_new_empty_simple (media_type);
2584 gst_pad_set_caps (mux->srcpad, ebml->caps);
2585 /* we start with a EBML header */
2586 doctype = mux->doctype;
2587 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2588 doctype, mux->doctype_version);
2589 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2591 /* the rest of the header is cached */
2592 gst_ebml_write_set_cache (ebml, 0x1000);
2594 /* start a segment */
2596 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2597 mux->segment_master = ebml->pos;
2599 if (!mux->streamable) {
2600 /* seekhead (table of contents) - we set the positions later */
2601 mux->seekhead_pos = ebml->pos;
2602 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2603 for (i = 0; seekhead_id[i] != 0; i++) {
2604 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2605 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2606 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2607 gst_ebml_write_master_finish (ebml, child);
2609 gst_ebml_write_master_finish (ebml, master);
2612 if (mux->streamable) {
2613 const GstTagList *tags;
2616 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2618 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2619 guint64 master_tags, master_tag;
2621 GST_DEBUG_OBJECT (mux, "Writing tags");
2623 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2624 mux->tags_pos = ebml->pos;
2625 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2626 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2627 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2628 gst_ebml_write_master_finish (ebml, master_tag);
2629 gst_ebml_write_master_finish (ebml, master_tags);
2634 mux->info_pos = ebml->pos;
2635 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2637 /* WebM does not support SegmentUID field on SegmentInfo */
2638 if (!mux->is_webm) {
2639 for (i = 0; i < 4; i++) {
2640 segment_uid[i] = g_random_int ();
2642 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2643 (guint8 *) segment_uid, 16);
2646 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2647 mux->duration_pos = ebml->pos;
2649 if (!mux->streamable) {
2650 for (collected = mux->collect->data; collected;
2651 collected = g_slist_next (collected)) {
2652 GstMatroskaPad *collect_pad;
2654 gint64 trackduration;
2656 collect_pad = (GstMatroskaPad *) collected->data;
2657 thepad = collect_pad->collect.pad;
2659 /* Query the total length of the track. */
2660 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2661 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2662 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2663 GST_TIME_ARGS (trackduration));
2664 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2665 duration = (GstClockTime) trackduration;
2669 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2670 gst_guint64_to_gdouble (duration) /
2671 gst_guint64_to_gdouble (mux->time_scale));
2673 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2674 "GStreamer plugin version " PACKAGE_VERSION);
2675 if (mux->writing_app && mux->writing_app[0]) {
2676 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2678 g_get_current_time (&time);
2679 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2680 gst_ebml_write_master_finish (ebml, master);
2683 mux->tracks_pos = ebml->pos;
2684 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2686 for (collected = mux->collect->data; collected;
2687 collected = g_slist_next (collected)) {
2688 GstMatroskaPad *collect_pad;
2691 collect_pad = (GstMatroskaPad *) collected->data;
2692 thepad = collect_pad->collect.pad;
2694 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2695 collect_pad->track->codec_id != 0) {
2696 collect_pad->track->num = tracknum++;
2697 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2698 gst_matroska_mux_track_header (mux, collect_pad->track);
2699 gst_ebml_write_master_finish (ebml, child);
2700 /* some remaining pad/track setup */
2701 collect_pad->default_duration_scaled =
2702 gst_util_uint64_scale (collect_pad->track->default_duration,
2703 1, mux->time_scale);
2706 gst_ebml_write_master_finish (ebml, master);
2708 /* FIXME: Check if we get a TOC that is supported by Matroska
2709 * and clean up the code below */
2712 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2713 if (toc != NULL && !mux->streamable) {
2714 guint64 master_chapters = 0;
2715 GstTocEntry *toc_entry;
2716 GList *cur, *to_write = NULL;
2719 GST_DEBUG ("Writing chapters");
2721 /* check whether we have editions or chapters at the root level */
2722 toc_entry = toc->entries->data;
2724 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2725 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2726 gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
2728 /* aggregate all chapters without root edition */
2729 cur = gst_toc_get_entries (toc);
2730 while (cur != NULL) {
2731 toc_entry->subentries =
2732 g_list_prepend (toc_entry->subentries, cur->data);
2736 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2737 toc_entry->subentries->data), &start, NULL);
2738 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2739 gst_toc_entry_get_start_stop_times (((GstTocEntry *)
2740 toc_entry->subentries->data), NULL, &stop);
2741 gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
2743 to_write = g_list_append (to_write, toc_entry);
2746 to_write = toc->entries;
2749 /* finally write chapters */
2750 mux->chapters_pos = ebml->pos;
2753 while (cur != NULL) {
2754 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2759 /* close master element if any edition was written */
2760 if (G_LIKELY (master_chapters != 0))
2761 gst_ebml_write_master_finish (ebml, master_chapters);
2763 if (toc_entry != NULL) {
2764 g_list_free (toc_entry->subentries);
2765 toc_entry->subentries = NULL;
2766 gst_toc_entry_unref (toc_entry);
2767 g_list_free (to_write);
2772 /* lastly, flush the cache */
2773 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2777 gst_toc_unref (toc);
2782 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2785 /* TODO: more sensible tag mappings */
2788 const gchar *matroska_tagname;
2789 const gchar *gstreamer_tagname;
2793 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2794 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2795 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2796 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2797 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2798 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2799 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2800 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2801 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2802 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2803 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2804 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2805 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2806 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2807 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2809 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2811 guint64 simpletag_master;
2813 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2814 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2815 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2817 if (strcmp (tagname_gst, tag) == 0) {
2818 GValue src = { 0, };
2821 if (!gst_tag_list_copy_value (&src, list, tag))
2823 if ((dest = gst_value_serialize (&src))) {
2825 simpletag_master = gst_ebml_write_master_start (ebml,
2826 GST_MATROSKA_ID_SIMPLETAG);
2827 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2828 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2829 gst_ebml_write_master_finish (ebml, simpletag_master);
2832 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2834 g_value_unset (&src);
2842 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2843 const GstTocEntry * entry, guint64 * master_tags)
2845 guint64 master_tag, master_targets;
2849 ebml = mux->ebml_write;
2851 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2852 if (*master_tags == 0) {
2853 mux->tags_pos = ebml->pos;
2854 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2857 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2859 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2861 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2862 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2863 g_ascii_strtoull (entry->uid, NULL, 10));
2865 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2866 g_ascii_strtoull (entry->uid, NULL, 10));
2868 gst_ebml_write_master_finish (ebml, master_targets);
2869 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2870 gst_ebml_write_master_finish (ebml, master_tag);
2873 cur = entry->subentries;
2874 while (cur != NULL) {
2875 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2882 * gst_matroska_mux_finish:
2883 * @mux: #GstMatroskaMux
2885 * Finish a new matroska file (write index etc...)
2888 gst_matroska_mux_finish (GstMatroskaMux * mux)
2890 GstEbmlWrite *ebml = mux->ebml_write;
2892 guint64 duration = 0;
2894 const GstTagList *tags;
2896 /* finish last cluster */
2898 gst_ebml_write_master_finish (ebml, mux->cluster);
2902 if (mux->index != NULL) {
2904 guint64 master, pointentry_master, trackpos_master;
2906 mux->cues_pos = ebml->pos;
2907 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2908 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2910 for (n = 0; n < mux->num_indexes; n++) {
2911 GstMatroskaIndex *idx = &mux->index[n];
2913 pointentry_master = gst_ebml_write_master_start (ebml,
2914 GST_MATROSKA_ID_POINTENTRY);
2915 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2916 idx->time / mux->time_scale);
2917 trackpos_master = gst_ebml_write_master_start (ebml,
2918 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2919 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2920 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2921 idx->pos - mux->segment_master);
2922 gst_ebml_write_master_finish (ebml, trackpos_master);
2923 gst_ebml_write_master_finish (ebml, pointentry_master);
2926 gst_ebml_write_master_finish (ebml, master);
2927 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2931 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2933 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2934 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2935 guint64 master_tags = 0, master_tag;
2940 GST_DEBUG_OBJECT (mux, "Writing tags");
2943 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2947 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2948 mux->tags_pos = ebml->pos;
2949 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2950 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2953 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2956 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2960 gst_ebml_write_master_finish (ebml, master_tag);
2965 while (cur != NULL) {
2966 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2972 if (master_tags != 0)
2973 gst_ebml_write_master_finish (ebml, master_tags);
2976 /* update seekhead. We know that:
2977 * - a seekhead contains 5 entries.
2978 * - order of entries is as above.
2979 * - a seekhead has a 4-byte header + 8-byte length
2980 * - each entry is 2-byte master, 2-byte ID pointer,
2981 * 2-byte length pointer, all 8/1-byte length, 4-
2982 * byte ID and 8-byte length pointer, where the
2983 * length pointer starts at 20.
2984 * - all entries are local to the segment (so pos - segment_master).
2985 * - so each entry is at 12 + 20 + num * 28. */
2986 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2987 mux->info_pos - mux->segment_master);
2988 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2989 mux->tracks_pos - mux->segment_master);
2990 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2991 && mux->chapters_pos > 0) {
2992 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2993 mux->chapters_pos - mux->segment_master);
2996 guint64 my_pos = ebml->pos;
2998 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2999 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3000 gst_ebml_write_seek (ebml, my_pos);
3002 if (mux->index != NULL) {
3003 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3004 mux->cues_pos - mux->segment_master);
3007 guint64 my_pos = ebml->pos;
3009 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3010 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3011 gst_ebml_write_seek (ebml, my_pos);
3015 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3016 mux->tags_pos - mux->segment_master);
3019 guint64 my_pos = ebml->pos;
3021 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3022 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3023 gst_ebml_write_seek (ebml, my_pos);
3027 * - first get the overall duration
3028 * (a released track may have left a duration in here)
3029 * - write some track header data for subtitles
3031 duration = mux->duration;
3033 for (collected = mux->collect->data; collected;
3034 collected = g_slist_next (collected)) {
3035 GstMatroskaPad *collect_pad;
3036 GstClockTime min_duration; /* observed minimum duration */
3037 GstMatroskaTrackContext *context;
3038 gint voidleft = 0, fill = 0;
3041 collect_pad = (GstMatroskaPad *) collected->data;
3042 context = collect_pad->track;
3044 GST_DEBUG_OBJECT (mux,
3045 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3046 " end ts %" GST_TIME_FORMAT, collect_pad,
3047 GST_TIME_ARGS (collect_pad->start_ts),
3048 GST_TIME_ARGS (collect_pad->end_ts));
3050 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3051 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3053 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3054 if (collect_pad->duration < min_duration)
3055 collect_pad->duration = min_duration;
3056 GST_DEBUG_OBJECT (collect_pad,
3057 "final track duration: %" GST_TIME_FORMAT,
3058 GST_TIME_ARGS (collect_pad->duration));
3061 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
3062 duration < collect_pad->duration)
3063 duration = collect_pad->duration;
3065 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
3069 /* write subtitle type and possible private data */
3070 gst_ebml_write_seek (ebml, context->pos);
3071 /* complex way to write ascii to account for extra filling */
3072 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
3073 strcpy (codec_id, context->codec_id);
3074 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
3075 codec_id, strlen (context->codec_id) + 1 + fill);
3077 if (context->codec_priv)
3078 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
3079 context->codec_priv, context->codec_priv_size);
3080 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
3081 /* void'ify; sigh, variable sized length field */
3082 if (voidleft == 1) {
3085 } else if (voidleft && voidleft <= 128)
3086 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
3087 else if (voidleft >= 130)
3088 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
3089 else if (voidleft == 129) {
3090 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
3091 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
3095 /* seek back (optional, but do anyway) */
3096 gst_ebml_write_seek (ebml, pos);
3098 /* update duration */
3099 if (duration != 0) {
3100 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3101 GST_TIME_ARGS (duration));
3102 pos = mux->ebml_write->pos;
3103 gst_ebml_write_seek (ebml, mux->duration_pos);
3104 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3105 gst_guint64_to_gdouble (duration) /
3106 gst_guint64_to_gdouble (mux->time_scale));
3107 gst_ebml_write_seek (ebml, pos);
3110 guint64 my_pos = ebml->pos;
3112 gst_ebml_write_seek (ebml, mux->duration_pos);
3113 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3114 gst_ebml_write_seek (ebml, my_pos);
3116 GST_DEBUG_OBJECT (mux, "finishing segment");
3117 /* finish segment - this also writes element length */
3118 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3122 * gst_matroska_mux_buffer_header:
3123 * @track: Track context.
3124 * @relative_timestamp: relative timestamp of the buffer
3125 * @flags: Buffer flags.
3127 * Create a buffer containing buffer header.
3129 * Returns: New buffer.
3132 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3133 gint16 relative_timestamp, int flags)
3136 guint8 *data = g_malloc (4);
3138 hdr = gst_buffer_new_wrapped (data, 4);
3139 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3140 data[0] = track->num | 0x80;
3141 /* time relative to clustertime */
3142 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3150 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3151 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3152 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3155 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3156 GstMatroskaPad * collect_pad, GstBuffer * buf)
3158 GstMatroskaTrackVideoContext *ctx =
3159 (GstMatroskaTrackVideoContext *) collect_pad->track;
3164 guint32 next_parse_offset;
3165 GstBuffer *ret = NULL;
3166 gboolean is_muxing_unit = FALSE;
3168 gst_buffer_map (buf, &map, GST_MAP_READ);
3173 gst_buffer_unmap (buf, &map);
3174 gst_buffer_unref (buf);
3178 /* Check if this buffer contains a picture or end-of-sequence packet */
3179 while (size >= 13) {
3180 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3181 gst_buffer_unmap (buf, &map);
3182 gst_buffer_unref (buf);
3186 parse_code = GST_READ_UINT8 (data + 4);
3187 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3188 if (ctx->dirac_unit) {
3189 gst_buffer_unref (ctx->dirac_unit);
3190 ctx->dirac_unit = NULL;
3192 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3193 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3194 is_muxing_unit = TRUE;
3198 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3200 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3203 data += next_parse_offset;
3204 size -= next_parse_offset;
3207 if (ctx->dirac_unit)
3208 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3210 ctx->dirac_unit = gst_buffer_ref (buf);
3212 gst_buffer_unmap (buf, &map);
3214 if (is_muxing_unit) {
3215 ret = gst_buffer_make_writable (ctx->dirac_unit);
3216 ctx->dirac_unit = NULL;
3217 gst_buffer_copy_into (ret, buf,
3218 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3219 gst_buffer_unref (buf);
3221 gst_buffer_unref (buf);
3229 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3233 GValue streamheader = { 0 };
3234 GValue bufval = { 0 };
3235 GstBuffer *streamheader_buffer;
3236 GstEbmlWrite *ebml = mux->ebml_write;
3238 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3239 caps = gst_caps_copy (mux->ebml_write->caps);
3240 s = gst_caps_get_structure (caps, 0);
3241 g_value_init (&streamheader, GST_TYPE_ARRAY);
3242 g_value_init (&bufval, GST_TYPE_BUFFER);
3243 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3244 gst_value_set_buffer (&bufval, streamheader_buffer);
3245 gst_value_array_append_value (&streamheader, &bufval);
3246 g_value_unset (&bufval);
3247 gst_structure_set_value (s, "streamheader", &streamheader);
3248 g_value_unset (&streamheader);
3249 gst_caps_replace (&ebml->caps, caps);
3250 gst_buffer_unref (streamheader_buffer);
3251 gst_pad_set_caps (mux->srcpad, caps);
3252 gst_caps_unref (caps);
3256 * gst_matroska_mux_write_data:
3257 * @mux: #GstMatroskaMux
3258 * @collect_pad: #GstMatroskaPad with the data
3260 * Write collected data (called from gst_matroska_mux_collected).
3262 * Returns: Result of the gst_pad_push issued to write the data.
3264 static GstFlowReturn
3265 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3268 GstEbmlWrite *ebml = mux->ebml_write;
3271 gboolean write_duration;
3272 gint16 relative_timestamp;
3273 gint64 relative_timestamp64;
3274 guint64 block_duration;
3275 gboolean is_video_keyframe = FALSE;
3276 gboolean is_video_invisible = FALSE;
3277 GstMatroskamuxPad *pad;
3281 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3283 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3284 if (collect_pad->track->xiph_headers_to_skip > 0) {
3285 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3286 gst_buffer_unref (buf);
3287 --collect_pad->track->xiph_headers_to_skip;
3291 /* for dirac we have to queue up everything up to a picture unit */
3292 if (collect_pad->track->codec_id != NULL &&
3293 strcmp (collect_pad->track->codec_id,
3294 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3295 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3300 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3301 * this would wreak havoc with time stored in matroska file */
3302 /* TODO: maybe calculate a timestamp by using the previous timestamp
3303 * and default duration */
3304 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3305 GST_WARNING_OBJECT (collect_pad->collect.pad,
3306 "Invalid buffer timestamp; dropping buffer");
3307 gst_buffer_unref (buf);
3311 /* set the timestamp for outgoing buffers */
3312 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3314 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3315 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3316 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3317 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3318 is_video_keyframe = TRUE;
3319 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3320 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3321 || !strcmp (collect_pad->track->codec_id,
3322 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3323 GST_LOG_OBJECT (mux,
3324 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3325 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3326 is_video_invisible = TRUE;
3331 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3332 * or when we may be reaching the limit of the relative timestamp */
3333 if (mux->cluster_time +
3334 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3335 || is_video_keyframe || mux->force_key_unit_event) {
3336 if (!mux->streamable)
3337 gst_ebml_write_master_finish (ebml, mux->cluster);
3339 /* Forward the GstForceKeyUnit event after finishing the cluster */
3340 if (mux->force_key_unit_event) {
3341 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3342 mux->force_key_unit_event = NULL;
3345 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3346 mux->cluster_pos = ebml->pos;
3347 gst_ebml_write_set_cache (ebml, 0x20);
3349 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3350 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3351 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3353 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3354 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3356 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3357 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3358 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3359 mux->prev_cluster_size);
3364 mux->cluster_pos = ebml->pos;
3365 gst_ebml_write_set_cache (ebml, 0x20);
3366 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3367 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3368 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3369 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3370 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3373 /* update duration of this track */
3374 if (GST_BUFFER_DURATION_IS_VALID (buf))
3375 collect_pad->duration += GST_BUFFER_DURATION (buf);
3377 /* We currently write index entries for all video tracks or for the audio
3378 * track in a single-track audio file. This could be improved by keeping the
3379 * index only for the *first* video track. */
3381 /* TODO: index is useful for every track, should contain the number of
3382 * the block in the cluster which contains the timestamp, should also work
3383 * for files with multiple audio tracks.
3385 if (!mux->streamable &&
3386 (is_video_keyframe ||
3387 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3388 (mux->num_streams == 1)))) {
3391 if (mux->min_index_interval != 0) {
3392 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3393 if (mux->index[last_idx].track == collect_pad->track->num)
3398 if (last_idx < 0 || mux->min_index_interval == 0 ||
3399 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3400 >= mux->min_index_interval)) {
3401 GstMatroskaIndex *idx;
3403 if (mux->num_indexes % 32 == 0) {
3404 mux->index = g_renew (GstMatroskaIndex, mux->index,
3405 mux->num_indexes + 32);
3407 idx = &mux->index[mux->num_indexes++];
3409 idx->pos = mux->cluster_pos;
3410 idx->time = GST_BUFFER_TIMESTAMP (buf);
3411 idx->track = collect_pad->track->num;
3415 /* Check if the duration differs from the default duration. */
3416 write_duration = FALSE;
3418 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3419 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3420 1, mux->time_scale);
3422 /* small difference should be ok. */
3423 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3424 block_duration < collect_pad->default_duration_scaled - 1) {
3425 write_duration = TRUE;
3429 /* write the block, for doctype v2 use SimpleBlock if possible
3430 * one slice (*breath*).
3431 * FIXME: Need to do correct lacing! */
3432 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3433 if (relative_timestamp64 >= 0) {
3434 /* round the timestamp */
3435 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3436 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3439 /* round the timestamp */
3440 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3441 relative_timestamp =
3442 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3446 if (is_video_invisible)
3449 if (mux->doctype_version > 1 && !write_duration) {
3450 if (is_video_keyframe)
3454 gst_matroska_mux_create_buffer_header (collect_pad->track,
3455 relative_timestamp, flags);
3456 gst_ebml_write_set_cache (ebml, 0x40);
3457 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3458 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3459 gst_ebml_write_buffer (ebml, hdr);
3460 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3461 gst_ebml_write_buffer (ebml, buf);
3463 return gst_ebml_last_write_result (ebml);
3465 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3466 /* write and call order slightly unnatural,
3467 * but avoids seek and minizes pushing */
3468 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3470 gst_matroska_mux_create_buffer_header (collect_pad->track,
3471 relative_timestamp, flags);
3473 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3474 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3475 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3476 gst_ebml_write_buffer (ebml, hdr);
3477 gst_ebml_write_master_finish_full (ebml, blockgroup,
3478 gst_buffer_get_size (buf));
3479 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3480 gst_ebml_write_buffer (ebml, buf);
3482 return gst_ebml_last_write_result (ebml);
3487 * gst_matroska_mux_handle_buffer:
3488 * @pads: #GstCollectPads
3489 * @uuser_data: #GstMatroskaMux
3491 * Collectpads callback.
3493 * Returns: #GstFlowReturn
3495 static GstFlowReturn
3496 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
3497 GstBuffer * buf, gpointer user_data)
3499 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3500 GstEbmlWrite *ebml = mux->ebml_write;
3501 GstMatroskaPad *best;
3502 GstFlowReturn ret = GST_FLOW_OK;
3504 GST_DEBUG_OBJECT (mux, "Collected pads");
3506 /* start with a header */
3507 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3508 if (mux->collect->data == NULL) {
3509 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3510 ("No input streams configured"));
3511 return GST_FLOW_ERROR;
3513 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3514 gst_ebml_start_streamheader (ebml);
3515 gst_matroska_mux_start (mux);
3516 gst_matroska_mux_stop_streamheader (mux);
3517 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3520 /* provided with stream to write from */
3521 best = (GstMatroskaPad *) data;
3523 /* if there is no best pad, we have reached EOS */
3525 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
3526 if (!mux->streamable) {
3527 gst_matroska_mux_finish (mux);
3529 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3531 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3536 /* if we have a best stream, should also have a buffer */
3539 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3540 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3541 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3542 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3544 /* make note of first and last encountered timestamps, so we can calculate
3545 * the actual duration later when we send an updated header on eos */
3546 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3547 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3548 GstClockTime end_ts = start_ts;
3550 if (GST_BUFFER_DURATION_IS_VALID (buf))
3551 end_ts += GST_BUFFER_DURATION (buf);
3552 else if (best->track->default_duration)
3553 end_ts += best->track->default_duration;
3555 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3556 best->end_ts = end_ts;
3558 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3559 start_ts < best->start_ts))
3560 best->start_ts = start_ts;
3563 /* write one buffer */
3564 ret = gst_matroska_mux_write_data (mux, best, buf);
3572 * gst_matroska_mux_change_state:
3573 * @element: #GstMatroskaMux
3574 * @transition: State change transition.
3576 * Change the muxer state.
3578 * Returns: #GstStateChangeReturn
3580 static GstStateChangeReturn
3581 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3583 GstStateChangeReturn ret;
3584 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3586 switch (transition) {
3587 case GST_STATE_CHANGE_NULL_TO_READY:
3589 case GST_STATE_CHANGE_READY_TO_PAUSED:
3590 gst_collect_pads_start (mux->collect);
3592 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3594 case GST_STATE_CHANGE_PAUSED_TO_READY:
3595 gst_collect_pads_stop (mux->collect);
3601 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3603 switch (transition) {
3604 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3606 case GST_STATE_CHANGE_PAUSED_TO_READY:
3607 gst_matroska_mux_reset (GST_ELEMENT (mux));
3609 case GST_STATE_CHANGE_READY_TO_NULL:
3619 gst_matroska_mux_set_property (GObject * object,
3620 guint prop_id, const GValue * value, GParamSpec * pspec)
3622 GstMatroskaMux *mux;
3624 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3625 mux = GST_MATROSKA_MUX (object);
3628 case ARG_WRITING_APP:
3629 if (!g_value_get_string (value)) {
3630 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3633 g_free (mux->writing_app);
3634 mux->writing_app = g_value_dup_string (value);
3636 case ARG_DOCTYPE_VERSION:
3637 mux->doctype_version = g_value_get_int (value);
3639 case ARG_MIN_INDEX_INTERVAL:
3640 mux->min_index_interval = g_value_get_int64 (value);
3642 case ARG_STREAMABLE:
3643 mux->streamable = g_value_get_boolean (value);
3646 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3652 gst_matroska_mux_get_property (GObject * object,
3653 guint prop_id, GValue * value, GParamSpec * pspec)
3655 GstMatroskaMux *mux;
3657 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3658 mux = GST_MATROSKA_MUX (object);
3661 case ARG_WRITING_APP:
3662 g_value_set_string (value, mux->writing_app);
3664 case ARG_DOCTYPE_VERSION:
3665 g_value_set_int (value, mux->doctype_version);
3667 case ARG_MIN_INDEX_INTERVAL:
3668 g_value_set_int64 (value, mux->min_index_interval);
3670 case ARG_STREAMABLE:
3671 g_value_set_boolean (value, mux->streamable);
3674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);