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., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, 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 -v filesrc location=/path/to/mp3 ! mp3parse ! 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 -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")
88 #define COMMON_VIDEO_CAPS \
89 "width = (int) [ 16, 4096 ], " \
90 "height = (int) [ 16, 4096 ], " \
91 "framerate = (fraction) [ 0, MAX ]"
93 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
94 "width = (int) [ 16, 4096 ], " \
95 "height = (int) [ 16, 4096 ] "
98 * * require codec data, etc as needed
101 static GstStaticPadTemplate videosink_templ =
102 GST_STATIC_PAD_TEMPLATE ("video_%u",
105 GST_STATIC_CAPS ("video/mpeg, "
106 "mpegversion = (int) { 1, 2, 4 }, "
107 "systemstream = (boolean) false, "
108 COMMON_VIDEO_CAPS "; "
109 "video/x-h264, stream-format=avc, alignment=au, "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS "; "
114 COMMON_VIDEO_CAPS "; "
116 COMMON_VIDEO_CAPS "; "
118 COMMON_VIDEO_CAPS "; "
120 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS "; "
124 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
127 COMMON_VIDEO_CAPS "; "
128 "video/x-pn-realvideo, "
129 "rmversion = (int) [1, 4], "
130 COMMON_VIDEO_CAPS "; "
132 COMMON_VIDEO_CAPS "; "
134 "format = (string) { YUY2, I420, YV12, UYVY, AYUV }, "
135 COMMON_VIDEO_CAPS "; "
136 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
139 #define COMMON_AUDIO_CAPS \
140 "channels = (int) [ 1, MAX ], " \
141 "rate = (int) [ 1, MAX ]"
144 * * require codec data, etc as needed
146 static GstStaticPadTemplate audiosink_templ =
147 GST_STATIC_PAD_TEMPLATE ("audio_%u",
150 GST_STATIC_CAPS ("audio/mpeg, "
151 "mpegversion = (int) 1, "
152 "layer = (int) [ 1, 3 ], "
153 COMMON_AUDIO_CAPS "; "
155 "mpegversion = (int) { 2, 4 }, "
156 "stream-format = (string) raw, "
157 COMMON_AUDIO_CAPS "; "
159 COMMON_AUDIO_CAPS "; "
161 COMMON_AUDIO_CAPS "; "
163 COMMON_AUDIO_CAPS "; "
165 COMMON_AUDIO_CAPS "; "
167 COMMON_AUDIO_CAPS "; "
169 COMMON_AUDIO_CAPS "; "
171 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
172 "layout = (string) interleaved, "
173 COMMON_AUDIO_CAPS ";"
175 "width = (int) { 8, 16, 24 }, "
176 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
177 "audio/x-pn-realaudio, "
178 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
179 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
180 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
181 COMMON_AUDIO_CAPS ";"
183 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
185 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
188 static GstStaticPadTemplate subtitlesink_templ =
189 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
192 GST_STATIC_CAPS ("subtitle/x-kate; "
193 "text/plain; application/x-ssa; application/x-ass; "
194 "application/x-usf; video/x-dvd-subpicture; "
195 "application/x-subtitle-unknown")
198 static GArray *used_uids;
199 G_LOCK_DEFINE_STATIC (used_uids);
201 #define parent_class gst_matroska_mux_parent_class
202 G_DEFINE_TYPE_WITH_CODE (GstMatroskaMux, gst_matroska_mux, GST_TYPE_ELEMENT,
203 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL)
204 G_IMPLEMENT_INTERFACE (GST_TYPE_TOC_SETTER, NULL)
207 /* Matroska muxer destructor */
208 static void gst_matroska_mux_finalize (GObject * object);
210 /* Pads collected callback */
211 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
212 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
213 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
214 GstCollectData2 * data, GstEvent * event, gpointer user_data);
217 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
218 GstObject * parent, GstEvent * event);
219 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
220 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
221 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
223 /* gst internal change state handler */
224 static GstStateChangeReturn
225 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
227 /* gobject bla bla */
228 static void gst_matroska_mux_set_property (GObject * object,
229 guint prop_id, const GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_get_property (GObject * object,
231 guint prop_id, GValue * value, GParamSpec * pspec);
234 static void gst_matroska_mux_reset (GstElement * element);
237 static guint64 gst_matroska_mux_create_uid ();
239 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
250 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
254 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
256 GObjectClass *gobject_class;
257 GstElementClass *gstelement_class;
259 gobject_class = (GObjectClass *) klass;
260 gstelement_class = (GstElementClass *) klass;
262 gst_element_class_add_pad_template (gstelement_class,
263 gst_static_pad_template_get (&videosink_templ));
264 gst_element_class_add_pad_template (gstelement_class,
265 gst_static_pad_template_get (&audiosink_templ));
266 gst_element_class_add_pad_template (gstelement_class,
267 gst_static_pad_template_get (&subtitlesink_templ));
268 gst_element_class_add_pad_template (gstelement_class,
269 gst_static_pad_template_get (&src_templ));
270 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
272 "Muxes video/audio/subtitle streams into a matroska stream",
273 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
275 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
278 gobject_class->finalize = gst_matroska_mux_finalize;
280 gobject_class->get_property = gst_matroska_mux_get_property;
281 gobject_class->set_property = gst_matroska_mux_set_property;
283 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
284 g_param_spec_string ("writing-app", "Writing application.",
285 "The name the application that creates the matroska file.",
286 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
287 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
288 g_param_spec_int ("version", "DocType version",
289 "This parameter determines what Matroska features can be used.",
290 1, 2, DEFAULT_DOCTYPE_VERSION,
291 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
292 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
293 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
294 "entries", "An index entry is created every so many nanoseconds.",
295 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
296 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
297 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
298 g_param_spec_boolean ("streamable", "Determines whether output should "
299 "be streamable", "If set to true, the output should be as if it is "
300 "to be streamed and hence no indexes written or duration written.",
302 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
304 gstelement_class->change_state =
305 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
306 gstelement_class->request_new_pad =
307 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
308 gstelement_class->release_pad =
309 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
313 * Start of pad option handler code
315 #define DEFAULT_PAD_FRAME_DURATION TRUE
316 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
321 PROP_PAD_FRAME_DURATION
327 gboolean frame_duration;
328 gboolean frame_duration_user;
331 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
334 gst_matroskamux_pad_get_type (void)
336 static GType type = 0;
338 if (G_UNLIKELY (type == 0)) {
339 type = g_type_register_static_simple (GST_TYPE_PAD,
340 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
341 (GClassInitFunc) gst_matroskamux_pad_class_init,
342 sizeof (GstMatroskamuxPad), NULL, 0);
347 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
348 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
349 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
350 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
353 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
354 GValue * value, GParamSpec * pspec)
356 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
359 case PROP_PAD_FRAME_DURATION:
360 g_value_set_boolean (value, pad->frame_duration);
363 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
369 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
370 const GValue * value, GParamSpec * pspec)
372 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
375 case PROP_PAD_FRAME_DURATION:
376 pad->frame_duration = g_value_get_boolean (value);
377 pad->frame_duration_user = TRUE;
380 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386 gst_matroskamux_pad_class_init (GstPadClass * klass)
388 GObjectClass *gobject_class = (GObjectClass *) klass;
390 gobject_class->set_property = gst_matroskamux_pad_set_property;
391 gobject_class->get_property = gst_matroskamux_pad_get_property;
393 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
394 g_param_spec_boolean ("frame-duration", "Frame duration",
395 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
396 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
400 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
402 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
403 pad->frame_duration_user = FALSE;
407 * End of pad option handler code
411 * gst_matroska_mux_init:
412 * @mux: #GstMatroskaMux that should be initialized.
413 * @g_class: Class of the muxer.
415 * Matroska muxer constructor.
418 gst_matroska_mux_init (GstMatroskaMux * mux)
420 GstPadTemplate *templ;
423 gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mux), "src");
424 mux->srcpad = gst_pad_new_from_template (templ, "src");
426 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
427 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
429 mux->collect = gst_collect_pads2_new ();
430 gst_collect_pads2_set_clip_function (mux->collect,
431 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
432 gst_collect_pads2_set_buffer_function (mux->collect,
433 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
434 gst_collect_pads2_set_event_function (mux->collect,
435 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
437 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
438 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
440 /* property defaults */
441 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
442 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
443 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
444 mux->streamable = DEFAULT_STREAMABLE;
446 /* initialize internal variables */
448 mux->num_streams = 0;
449 mux->num_a_streams = 0;
450 mux->num_t_streams = 0;
451 mux->num_v_streams = 0;
453 /* initialize remaining variables */
454 gst_matroska_mux_reset (GST_ELEMENT (mux));
459 * gst_matroska_mux_finalize:
460 * @object: #GstMatroskaMux that should be finalized.
462 * Finalize matroska muxer.
465 gst_matroska_mux_finalize (GObject * object)
467 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
469 gst_event_replace (&mux->force_key_unit_event, NULL);
471 gst_object_unref (mux->collect);
472 gst_object_unref (mux->ebml_write);
473 if (mux->writing_app)
474 g_free (mux->writing_app);
476 G_OBJECT_CLASS (parent_class)->finalize (object);
481 * gst_matroska_mux_create_uid:
483 * Generate new unused track UID.
485 * Returns: New track UID.
488 gst_matroska_mux_create_uid (void)
495 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
500 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
501 for (i = 0; i < used_uids->len; i++) {
502 if (g_array_index (used_uids, guint64, i) == uid) {
507 g_array_append_val (used_uids, uid);
510 G_UNLOCK (used_uids);
516 * gst_matroska_pad_reset:
517 * @collect_pad: the #GstMatroskaPad
519 * Reset and/or release resources of a matroska collect pad.
522 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
525 GstMatroskaTrackType type = 0;
527 /* free track information */
528 if (collect_pad->track != NULL) {
529 /* retrieve for optional later use */
530 name = collect_pad->track->name;
531 type = collect_pad->track->type;
532 /* extra for video */
533 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
534 GstMatroskaTrackVideoContext *ctx =
535 (GstMatroskaTrackVideoContext *) collect_pad->track;
537 if (ctx->dirac_unit) {
538 gst_buffer_unref (ctx->dirac_unit);
539 ctx->dirac_unit = NULL;
542 g_free (collect_pad->track->codec_id);
543 g_free (collect_pad->track->codec_name);
545 g_free (collect_pad->track->name);
546 g_free (collect_pad->track->language);
547 g_free (collect_pad->track->codec_priv);
548 g_free (collect_pad->track);
549 collect_pad->track = NULL;
552 if (!full && type != 0) {
553 GstMatroskaTrackContext *context;
555 /* create a fresh context */
557 case GST_MATROSKA_TRACK_TYPE_VIDEO:
558 context = (GstMatroskaTrackContext *)
559 g_new0 (GstMatroskaTrackVideoContext, 1);
561 case GST_MATROSKA_TRACK_TYPE_AUDIO:
562 context = (GstMatroskaTrackContext *)
563 g_new0 (GstMatroskaTrackAudioContext, 1);
565 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
566 context = (GstMatroskaTrackContext *)
567 g_new0 (GstMatroskaTrackSubtitleContext, 1);
570 g_assert_not_reached ();
574 context->type = type;
575 context->name = name;
576 /* TODO: check default values for the context */
577 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
578 collect_pad->track = context;
579 collect_pad->duration = 0;
580 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
581 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
586 * gst_matroska_pad_free:
587 * @collect_pad: the #GstMatroskaPad
589 * Release resources of a matroska collect pad.
592 gst_matroska_pad_free (GstPad * collect_pad)
594 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
599 * gst_matroska_mux_reset:
600 * @element: #GstMatroskaMux that should be reseted.
602 * Reset matroska muxer back to initial state.
605 gst_matroska_mux_reset (GstElement * element)
607 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
610 /* reset EBML write */
611 gst_ebml_write_reset (mux->ebml_write);
614 mux->state = GST_MATROSKA_MUX_STATE_START;
616 /* clean up existing streams */
618 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
619 GstMatroskaPad *collect_pad;
621 collect_pad = (GstMatroskaPad *) walk->data;
623 /* reset collect pad to pristine state */
624 gst_matroska_pad_reset (collect_pad, FALSE);
628 mux->num_indexes = 0;
633 mux->time_scale = GST_MSECOND;
634 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
639 mux->cluster_time = 0;
640 mux->cluster_pos = 0;
641 mux->prev_cluster_size = 0;
644 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
649 gst_toc_setter_reset_toc (GST_TOC_SETTER (mux));
651 mux->chapters_pos = 0;
655 * gst_matroska_mux_handle_src_event:
656 * @pad: Pad which received the event.
657 * @event: Received event.
659 * handle events - copied from oggmux without understanding
661 * Returns: #TRUE on success.
664 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
669 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
673 /* disable seeking for now */
679 return gst_pad_event_default (pad, parent, event);
684 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
686 if (context->codec_priv != NULL) {
687 g_free (context->codec_priv);
688 context->codec_priv = NULL;
689 context->codec_priv_size = 0;
694 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
704 /* produce comma-separated list in hex format */
705 for (i = 0; i < 16; ++i) {
707 /* replicate vobsub's slightly off RGB conversion calculation */
708 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
709 u = ((col >> 8) & 0xff) - 128;
710 v = (col & 0xff) - 128;
711 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
712 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
713 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
714 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
717 sclut = g_strjoinv (",", clutv);
719 /* build codec private; only palette for now */
720 gst_matroska_mux_free_codec_priv (context);
721 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
722 /* include terminating 0 */
723 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
725 for (i = 0; i < 16; ++i) {
732 * gst_matroska_mux_handle_sink_event:
733 * @pad: Pad which received the event.
734 * @event: Received event.
736 * handle events - informational ones like tags
738 * Returns: #TRUE on success.
741 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
742 GstCollectData2 * data, GstEvent * event, gpointer user_data)
744 GstMatroskaPad *collect_pad;
745 GstMatroskaTrackContext *context;
749 gboolean ret = FALSE;
751 mux = GST_MATROSKA_MUX (user_data);
752 collect_pad = (GstMatroskaPad *) data;
754 context = collect_pad->track;
757 switch (GST_EVENT_TYPE (event)) {
758 case GST_EVENT_CAPS:{
761 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
762 gst_event_parse_caps (event, &caps);
764 ret = collect_pad->capsfunc (pad, caps);
765 gst_event_unref (event);
772 GST_DEBUG_OBJECT (mux, "received tag event");
773 gst_event_parse_tag (event, &list);
775 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
776 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
777 const gchar *lang_code;
779 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
781 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
782 context->language = g_strdup (lang_code);
784 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
789 /* FIXME: what about stream-specific tags? */
790 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
791 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
793 gst_event_unref (event);
794 /* handled this, don't want collectpads to forward it downstream */
799 case GST_EVENT_SEGMENT:{
800 const GstSegment *segment;
802 gst_event_parse_segment (event, &segment);
803 if (segment->format != GST_FORMAT_TIME) {
806 gst_event_unref (event);
814 if (mux->chapters_pos > 0)
817 GST_DEBUG_OBJECT (mux, "received toc event");
818 gst_event_parse_toc (event, &toc, NULL);
821 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
822 gst_toc_setter_reset_toc (GST_TOC_SETTER (mux));
823 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
826 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
830 gst_event_unref (event);
831 /* handled this, don't want collectpads to forward it downstream */
835 case GST_EVENT_CUSTOM_DOWNSTREAM:{
836 const GstStructure *structure;
838 structure = gst_event_get_structure (event);
839 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
840 gst_event_replace (&mux->force_key_unit_event, NULL);
841 mux->force_key_unit_event = event;
843 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
844 !strcmp ("dvd-spu-clut-change",
845 gst_structure_get_string (structure, "event"))) {
850 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
851 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
852 GST_DEBUG_OBJECT (pad, "... discarding");
855 /* first transform event data into table form */
856 for (i = 0; i < 16; i++) {
857 g_snprintf (name, sizeof (name), "clut%02d", i);
858 if (!gst_structure_get_int (structure, name, &value)) {
859 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
860 "contain %s field", name);
866 /* transform into private data for stream; text form */
867 gst_matroska_mux_build_vobsub_private (context, clut);
872 ret = gst_pad_event_default (data->pad, GST_OBJECT (mux), event);
875 gst_event_unref (event);
884 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
887 g_assert (context && id);
888 if (context->codec_id)
889 g_free (context->codec_id);
890 context->codec_id = g_strdup (id);
894 * gst_matroska_mux_video_pad_setcaps:
895 * @pad: Pad which got the caps.
898 * Setcaps function for video sink pad.
900 * Returns: #TRUE on success.
903 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
905 GstMatroskaTrackContext *context = NULL;
906 GstMatroskaTrackVideoContext *videocontext;
908 GstMatroskaPad *collect_pad;
909 GstStructure *structure;
910 const gchar *mimetype;
911 const GValue *value = NULL;
912 GstBuffer *codec_buf = NULL;
913 gint width, height, pixel_width, pixel_height;
915 gboolean interlaced = FALSE;
917 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
920 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
921 g_assert (collect_pad);
922 context = collect_pad->track;
924 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
925 videocontext = (GstMatroskaTrackVideoContext *) context;
927 /* gst -> matroska ID'ing */
928 structure = gst_caps_get_structure (caps, 0);
930 mimetype = gst_structure_get_name (structure);
932 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
934 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
936 if (!strcmp (mimetype, "video/x-theora")) {
937 /* we'll extract the details later from the theora identification header */
941 /* get general properties */
942 /* spec says it is mandatory */
943 if (!gst_structure_get_int (structure, "width", &width) ||
944 !gst_structure_get_int (structure, "height", &height))
947 videocontext->pixel_width = width;
948 videocontext->pixel_height = height;
950 /* set vp8 defaults or let user override it */
951 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
952 && (!strcmp (mimetype, "video/x-vp8")))
953 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
954 DEFAULT_PAD_FRAME_DURATION_VP8;
956 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
957 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
959 context->default_duration =
960 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
961 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
962 GST_TIME_ARGS (context->default_duration));
964 context->default_duration = 0;
966 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
967 &pixel_width, &pixel_height)) {
968 if (pixel_width > pixel_height) {
969 videocontext->display_width = width * pixel_width / pixel_height;
970 videocontext->display_height = height;
971 } else if (pixel_width < pixel_height) {
972 videocontext->display_width = width;
973 videocontext->display_height = height * pixel_height / pixel_width;
975 videocontext->display_width = 0;
976 videocontext->display_height = 0;
979 videocontext->display_width = 0;
980 videocontext->display_height = 0;
985 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
986 videocontext->fourcc = 0;
988 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
989 * data and other settings
993 /* extract codec_data, may turn out needed */
994 value = gst_structure_get_value (structure, "codec_data");
996 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
999 if (!strcmp (mimetype, "video/x-raw")) {
1001 gst_matroska_mux_set_codec_id (context,
1002 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1003 fstr = gst_structure_get_string (structure, "format");
1004 if (fstr && strlen (fstr) == 4)
1005 videocontext->fourcc = GST_STR_FOURCC (fstr);
1006 } else if (!strcmp (mimetype, "image/jpeg")) {
1007 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
1008 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
1009 ||!strcmp (mimetype, "video/x-huffyuv")
1010 || !strcmp (mimetype, "video/x-divx")
1011 || !strcmp (mimetype, "video/x-dv")
1012 || !strcmp (mimetype, "video/x-h263")
1013 || !strcmp (mimetype, "video/x-msmpeg")
1014 || !strcmp (mimetype, "video/x-wmv")
1015 || !strcmp (mimetype, "image/jpeg")) {
1016 gst_riff_strf_vids *bih;
1017 gint size = sizeof (gst_riff_strf_vids);
1020 if (!strcmp (mimetype, "video/x-xvid"))
1021 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
1022 else if (!strcmp (mimetype, "video/x-huffyuv"))
1023 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1024 else if (!strcmp (mimetype, "video/x-dv"))
1025 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1026 else if (!strcmp (mimetype, "video/x-h263"))
1027 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1028 else if (!strcmp (mimetype, "video/x-divx")) {
1031 gst_structure_get_int (structure, "divxversion", &divxversion);
1032 switch (divxversion) {
1034 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1037 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1040 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1043 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1046 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1047 switch (msmpegversion) {
1049 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1052 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1058 } else if (!strcmp (mimetype, "video/x-wmv")) {
1062 fstr = gst_structure_get_string (structure, "format");
1063 if (fstr && strlen (fstr) == 4) {
1064 fourcc = GST_STR_FOURCC (fstr);
1065 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1066 if (wmvversion == 2) {
1067 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1068 } else if (wmvversion == 1) {
1069 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1070 } else if (wmvversion == 3) {
1071 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1074 } else if (!strcmp (mimetype, "image/jpeg")) {
1075 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1081 bih = g_new0 (gst_riff_strf_vids, 1);
1082 GST_WRITE_UINT32_LE (&bih->size, size);
1083 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1084 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1085 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1086 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1087 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1088 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1089 videocontext->pixel_height * 3);
1091 /* process codec private/initialization data, if any */
1093 size += gst_buffer_get_size (codec_buf);
1094 bih = g_realloc (bih, size);
1095 GST_WRITE_UINT32_LE (&bih->size, size);
1096 gst_buffer_extract (codec_buf, 0,
1097 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1100 gst_matroska_mux_set_codec_id (context,
1101 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1102 gst_matroska_mux_free_codec_priv (context);
1103 context->codec_priv = (gpointer) bih;
1104 context->codec_priv_size = size;
1105 } else if (!strcmp (mimetype, "video/x-h264")) {
1106 gst_matroska_mux_set_codec_id (context,
1107 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1108 gst_matroska_mux_free_codec_priv (context);
1109 /* Create avcC header */
1110 if (codec_buf != NULL) {
1111 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1112 context->codec_priv = g_malloc0 (context->codec_priv_size);
1113 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1115 } else if (!strcmp (mimetype, "video/x-theora")) {
1116 const GValue *streamheader;
1118 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1120 gst_matroska_mux_free_codec_priv (context);
1122 streamheader = gst_structure_get_value (structure, "streamheader");
1123 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1124 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1125 ("theora stream headers missing or malformed"));
1128 } else if (!strcmp (mimetype, "video/x-dirac")) {
1129 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1130 } else if (!strcmp (mimetype, "video/x-vp8")) {
1131 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1132 } else if (!strcmp (mimetype, "video/mpeg")) {
1135 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1136 switch (mpegversion) {
1138 gst_matroska_mux_set_codec_id (context,
1139 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1142 gst_matroska_mux_set_codec_id (context,
1143 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1146 gst_matroska_mux_set_codec_id (context,
1147 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1153 /* global headers may be in codec data */
1154 if (codec_buf != NULL) {
1155 gst_matroska_mux_free_codec_priv (context);
1156 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1157 context->codec_priv = g_malloc0 (context->codec_priv_size);
1158 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1160 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1162 /* can only make it here if preceding case verified it was version 3 */
1163 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1164 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1166 const GValue *mdpr_data;
1168 gst_structure_get_int (structure, "rmversion", &rmversion);
1169 switch (rmversion) {
1171 gst_matroska_mux_set_codec_id (context,
1172 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1175 gst_matroska_mux_set_codec_id (context,
1176 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1179 gst_matroska_mux_set_codec_id (context,
1180 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1183 gst_matroska_mux_set_codec_id (context,
1184 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1190 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1191 if (mdpr_data != NULL) {
1192 guint8 *priv_data = NULL;
1193 guint priv_data_size = 0;
1195 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1197 priv_data_size = gst_buffer_get_size (codec_data_buf);
1198 priv_data = g_malloc0 (priv_data_size);
1200 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1202 gst_matroska_mux_free_codec_priv (context);
1203 context->codec_priv = priv_data;
1204 context->codec_priv_size = priv_data_size;
1213 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1214 GST_PAD_NAME (pad), caps);
1219 /* N > 0 to expect a particular number of headers, negative if the
1220 number of headers is variable */
1222 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1223 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1225 GstBuffer **buf = NULL;
1228 guint bufi, i, offset, priv_data_size;
1230 if (streamheader == NULL)
1231 goto no_stream_headers;
1233 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1236 bufarr = g_value_peek_pointer (streamheader);
1237 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1239 if (N > 0 && bufarr->len != N)
1242 context->xiph_headers_to_skip = bufarr->len;
1244 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1245 for (i = 0; i < bufarr->len; i++) {
1246 GValue *bufval = &g_array_index (bufarr, GValue, i);
1248 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1250 goto wrong_content_type;
1253 buf[i] = g_value_peek_pointer (bufval);
1257 if (bufarr->len > 0) {
1258 for (i = 0; i < bufarr->len - 1; i++) {
1259 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1263 for (i = 0; i < bufarr->len; ++i) {
1264 priv_data_size += gst_buffer_get_size (buf[i]);
1267 priv_data = g_malloc0 (priv_data_size);
1269 priv_data[0] = bufarr->len - 1;
1272 if (bufarr->len > 0) {
1273 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1274 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1275 priv_data[offset++] = 0xff;
1277 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1281 for (i = 0; i < bufarr->len; ++i) {
1282 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1283 offset += gst_buffer_get_size (buf[i]);
1286 gst_matroska_mux_free_codec_priv (context);
1287 context->codec_priv = priv_data;
1288 context->codec_priv_size = priv_data_size;
1291 *p_buf0 = gst_buffer_ref (buf[0]);
1300 GST_WARNING ("required streamheaders missing in sink caps!");
1305 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1306 G_VALUE_TYPE_NAME (streamheader));
1311 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1316 GST_WARNING ("streamheaders array does not contain GstBuffers");
1322 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1323 GstMatroskaTrackContext * context)
1325 GstBuffer *buf0 = NULL;
1327 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1330 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1331 GST_WARNING ("First vorbis header too small, ignoring");
1333 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1334 GstMatroskaTrackAudioContext *audiocontext;
1338 gst_buffer_map (buf0, &map, GST_MAP_READ);
1339 hdr = map.data + 1 + 6 + 4;
1340 audiocontext = (GstMatroskaTrackAudioContext *) context;
1341 audiocontext->channels = GST_READ_UINT8 (hdr);
1342 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1343 gst_buffer_unmap (buf0, &map);
1348 gst_buffer_unref (buf0);
1354 theora_streamheader_to_codecdata (const GValue * streamheader,
1355 GstMatroskaTrackContext * context)
1357 GstBuffer *buf0 = NULL;
1359 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1362 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1363 GST_WARNING ("First theora header too small, ignoring");
1364 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1365 GST_WARNING ("First header not a theora identification header, ignoring");
1367 GstMatroskaTrackVideoContext *videocontext;
1368 guint fps_num, fps_denom, par_num, par_denom;
1372 gst_buffer_map (buf0, &map, GST_MAP_READ);
1373 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1375 videocontext = (GstMatroskaTrackVideoContext *) context;
1376 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1377 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1378 hdr += 3 + 3 + 1 + 1;
1379 fps_num = GST_READ_UINT32_BE (hdr);
1380 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1381 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1382 fps_denom, fps_num);
1384 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1385 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1386 if (par_num > 0 && par_num > 0) {
1387 if (par_num > par_denom) {
1388 videocontext->display_width =
1389 videocontext->pixel_width * par_num / par_denom;
1390 videocontext->display_height = videocontext->pixel_height;
1391 } else if (par_num < par_denom) {
1392 videocontext->display_width = videocontext->pixel_width;
1393 videocontext->display_height =
1394 videocontext->pixel_height * par_denom / par_num;
1396 videocontext->display_width = 0;
1397 videocontext->display_height = 0;
1400 videocontext->display_width = 0;
1401 videocontext->display_height = 0;
1405 gst_buffer_unmap (buf0, &map);
1409 gst_buffer_unref (buf0);
1415 kate_streamheader_to_codecdata (const GValue * streamheader,
1416 GstMatroskaTrackContext * context)
1418 GstBuffer *buf0 = NULL;
1420 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1423 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1424 GST_WARNING ("First kate header too small, ignoring");
1425 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1426 GST_WARNING ("First header not a kate identification header, ignoring");
1430 gst_buffer_unref (buf0);
1436 flac_streamheader_to_codecdata (const GValue * streamheader,
1437 GstMatroskaTrackContext * context)
1444 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1445 GST_WARNING ("No or invalid streamheader field in the caps");
1449 bufarr = g_value_peek_pointer (streamheader);
1450 if (bufarr->len < 2) {
1451 GST_WARNING ("Too few headers in streamheader field");
1455 context->xiph_headers_to_skip = bufarr->len + 1;
1457 bufval = &g_array_index (bufarr, GValue, 0);
1458 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1459 GST_WARNING ("streamheaders array does not contain GstBuffers");
1463 buffer = g_value_peek_pointer (bufval);
1465 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1466 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1467 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1468 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1469 GST_WARNING ("Invalid streamheader for FLAC");
1473 gst_matroska_mux_free_codec_priv (context);
1474 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1475 context->codec_priv = g_malloc (context->codec_priv_size);
1476 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1478 for (i = 1; i < bufarr->len; i++) {
1480 bufval = &g_array_index (bufarr, GValue, i);
1482 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1483 gst_matroska_mux_free_codec_priv (context);
1484 GST_WARNING ("streamheaders array does not contain GstBuffers");
1488 buffer = g_value_peek_pointer (bufval);
1490 old_size = context->codec_priv_size;
1491 context->codec_priv_size += gst_buffer_get_size (buffer);
1493 context->codec_priv = g_realloc (context->codec_priv,
1494 context->codec_priv_size);
1495 gst_buffer_extract (buffer, 0,
1496 (guint8 *) context->codec_priv + old_size, -1);
1503 speex_streamheader_to_codecdata (const GValue * streamheader,
1504 GstMatroskaTrackContext * context)
1511 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1512 GST_WARNING ("No or invalid streamheader field in the caps");
1516 bufarr = g_value_peek_pointer (streamheader);
1517 if (bufarr->len != 2) {
1518 GST_WARNING ("Too few headers in streamheader field");
1522 context->xiph_headers_to_skip = bufarr->len + 1;
1524 bufval = &g_array_index (bufarr, GValue, 0);
1525 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1526 GST_WARNING ("streamheaders array does not contain GstBuffers");
1530 buffer = g_value_peek_pointer (bufval);
1532 if (gst_buffer_get_size (buffer) < 80
1533 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1534 GST_WARNING ("Invalid streamheader for Speex");
1538 gst_matroska_mux_free_codec_priv (context);
1539 context->codec_priv_size = gst_buffer_get_size (buffer);
1540 context->codec_priv = g_malloc (context->codec_priv_size);
1541 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1543 bufval = &g_array_index (bufarr, GValue, 1);
1545 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1546 gst_matroska_mux_free_codec_priv (context);
1547 GST_WARNING ("streamheaders array does not contain GstBuffers");
1551 buffer = g_value_peek_pointer (bufval);
1553 old_size = context->codec_priv_size;
1554 context->codec_priv_size += gst_buffer_get_size (buffer);
1555 context->codec_priv = g_realloc (context->codec_priv,
1556 context->codec_priv_size);
1557 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1562 static const gchar *
1563 aac_codec_data_to_codec_id (GstBuffer * buf)
1565 const gchar *result;
1568 /* default to MAIN */
1571 if (gst_buffer_get_size (buf) >= 2) {
1572 gst_buffer_extract (buf, 0, &profile, 1);
1590 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1599 * gst_matroska_mux_audio_pad_setcaps:
1600 * @pad: Pad which got the caps.
1603 * Setcaps function for audio sink pad.
1605 * Returns: #TRUE on success.
1608 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1610 GstMatroskaTrackContext *context = NULL;
1611 GstMatroskaTrackAudioContext *audiocontext;
1612 GstMatroskaMux *mux;
1613 GstMatroskaPad *collect_pad;
1614 const gchar *mimetype;
1615 gint samplerate = 0, channels = 0;
1616 GstStructure *structure;
1617 const GValue *codec_data = NULL;
1618 GstBuffer *buf = NULL;
1619 const gchar *stream_format = NULL;
1621 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1624 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1625 g_assert (collect_pad);
1626 context = collect_pad->track;
1628 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1629 audiocontext = (GstMatroskaTrackAudioContext *) context;
1631 structure = gst_caps_get_structure (caps, 0);
1632 mimetype = gst_structure_get_name (structure);
1635 gst_structure_get_int (structure, "rate", &samplerate);
1636 gst_structure_get_int (structure, "channels", &channels);
1638 audiocontext->samplerate = samplerate;
1639 audiocontext->channels = channels;
1640 audiocontext->bitdepth = 0;
1641 context->default_duration = 0;
1643 codec_data = gst_structure_get_value (structure, "codec_data");
1645 buf = gst_value_get_buffer (codec_data);
1647 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1648 * data and other settings
1652 if (!strcmp (mimetype, "audio/mpeg")) {
1653 gint mpegversion = 0;
1655 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1656 switch (mpegversion) {
1662 gst_structure_get_int (structure, "layer", &layer);
1664 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1665 GST_WARNING_OBJECT (mux,
1666 "Unable to determine MPEG audio version, assuming 1");
1672 else if (layer == 2)
1674 else if (version == 2)
1679 context->default_duration =
1680 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1684 gst_matroska_mux_set_codec_id (context,
1685 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1688 gst_matroska_mux_set_codec_id (context,
1689 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1692 gst_matroska_mux_set_codec_id (context,
1693 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1702 stream_format = gst_structure_get_string (structure, "stream-format");
1703 /* check this is raw aac */
1704 if (stream_format) {
1705 if (strcmp (stream_format, "raw") != 0) {
1706 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1710 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1715 if (mpegversion == 2)
1717 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1718 aac_codec_data_to_codec_id (buf));
1719 else if (mpegversion == 4)
1721 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1722 aac_codec_data_to_codec_id (buf));
1724 g_assert_not_reached ();
1726 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1733 } else if (!strcmp (mimetype, "audio/x-raw")) {
1736 gst_audio_info_init (&info);
1737 if (!gst_audio_info_from_caps (&info, caps)) {
1738 GST_DEBUG_OBJECT (mux,
1739 "broken caps, rejected by gst_audio_info_from_caps");
1743 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1744 case GST_AUDIO_FORMAT_U8:
1745 case GST_AUDIO_FORMAT_S16BE:
1746 case GST_AUDIO_FORMAT_S16LE:
1747 case GST_AUDIO_FORMAT_S24BE:
1748 case GST_AUDIO_FORMAT_S24LE:
1749 case GST_AUDIO_FORMAT_S32BE:
1750 case GST_AUDIO_FORMAT_S32LE:
1751 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1752 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1755 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1756 gst_matroska_mux_set_codec_id (context,
1757 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1759 gst_matroska_mux_set_codec_id (context,
1760 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1762 case GST_AUDIO_FORMAT_F32LE:
1763 case GST_AUDIO_FORMAT_F64LE:
1764 gst_matroska_mux_set_codec_id (context,
1765 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1769 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1773 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1774 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1775 const GValue *streamheader;
1777 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1779 gst_matroska_mux_free_codec_priv (context);
1781 streamheader = gst_structure_get_value (structure, "streamheader");
1782 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1783 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1784 ("vorbis stream headers missing or malformed"));
1787 } else if (!strcmp (mimetype, "audio/x-flac")) {
1788 const GValue *streamheader;
1790 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1792 gst_matroska_mux_free_codec_priv (context);
1794 streamheader = gst_structure_get_value (structure, "streamheader");
1795 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1796 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1797 ("flac stream headers missing or malformed"));
1800 } else if (!strcmp (mimetype, "audio/x-speex")) {
1801 const GValue *streamheader;
1803 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1804 gst_matroska_mux_free_codec_priv (context);
1806 streamheader = gst_structure_get_value (structure, "streamheader");
1807 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1808 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1809 ("speex stream headers missing or malformed"));
1812 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1813 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1814 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1815 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1816 } else if (!strcmp (mimetype, "audio/x-dts")) {
1817 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1818 } else if (!strcmp (mimetype, "audio/x-tta")) {
1821 /* TTA frame duration */
1822 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1824 gst_structure_get_int (structure, "width", &width);
1825 audiocontext->bitdepth = width;
1826 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1828 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1830 const GValue *mdpr_data;
1832 gst_structure_get_int (structure, "raversion", &raversion);
1833 switch (raversion) {
1835 gst_matroska_mux_set_codec_id (context,
1836 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1839 gst_matroska_mux_set_codec_id (context,
1840 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1843 gst_matroska_mux_set_codec_id (context,
1844 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1850 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1851 if (mdpr_data != NULL) {
1852 guint8 *priv_data = NULL;
1853 guint priv_data_size = 0;
1855 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1857 priv_data_size = gst_buffer_get_size (codec_data_buf);
1858 priv_data = g_malloc0 (priv_data_size);
1860 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1862 gst_matroska_mux_free_codec_priv (context);
1864 context->codec_priv = priv_data;
1865 context->codec_priv_size = priv_data_size;
1868 } else if (!strcmp (mimetype, "audio/x-wma")
1869 || !strcmp (mimetype, "audio/x-alaw")
1870 || !strcmp (mimetype, "audio/x-mulaw")) {
1872 guint codec_priv_size;
1877 if (samplerate == 0 || channels == 0) {
1878 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1882 if (!strcmp (mimetype, "audio/x-wma")) {
1886 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1887 || !gst_structure_get_int (structure, "block_align", &block_align)
1888 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1889 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1894 switch (wmaversion) {
1896 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1899 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1902 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1905 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1909 if (gst_structure_get_int (structure, "depth", &depth))
1910 audiocontext->bitdepth = depth;
1911 } else if (!strcmp (mimetype, "audio/x-alaw")
1912 || !strcmp (mimetype, "audio/x-mulaw")) {
1913 audiocontext->bitdepth = 8;
1914 if (!strcmp (mimetype, "audio/x-alaw"))
1915 format = GST_RIFF_WAVE_FORMAT_ALAW;
1917 format = GST_RIFF_WAVE_FORMAT_MULAW;
1919 block_align = channels;
1920 bitrate = block_align * samplerate;
1922 g_assert (format != 0);
1924 codec_priv_size = WAVEFORMATEX_SIZE;
1926 codec_priv_size += gst_buffer_get_size (buf);
1928 /* serialize waveformatex structure */
1929 codec_priv = g_malloc0 (codec_priv_size);
1930 GST_WRITE_UINT16_LE (codec_priv, format);
1931 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1932 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1933 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1934 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1935 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1937 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1939 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1941 /* process codec private/initialization data, if any */
1943 gst_buffer_extract (buf, 0,
1944 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1947 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1948 gst_matroska_mux_free_codec_priv (context);
1949 context->codec_priv = (gpointer) codec_priv;
1950 context->codec_priv_size = codec_priv_size;
1958 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1959 GST_PAD_NAME (pad), caps);
1964 /* we probably don't have the data at start,
1965 * so have to reserve (a maximum) space to write this at the end.
1966 * bit spacy, but some formats can hold quite some */
1967 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1970 * gst_matroska_mux_subtitle_pad_setcaps:
1971 * @pad: Pad which got the caps.
1974 * Setcaps function for subtitle sink pad.
1976 * Returns: #TRUE on success.
1979 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1981 /* There is now (at least) one such alement (kateenc), and I'm going
1982 to handle it here and claim it works when it can be piped back
1983 through GStreamer and VLC */
1985 GstMatroskaTrackContext *context = NULL;
1986 GstMatroskaTrackSubtitleContext *scontext;
1987 GstMatroskaMux *mux;
1988 GstMatroskaPad *collect_pad;
1989 const gchar *mimetype;
1990 GstStructure *structure;
1991 const GValue *value = NULL;
1992 GstBuffer *buf = NULL;
1993 gboolean ret = TRUE;
1995 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1998 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1999 g_assert (collect_pad);
2000 context = collect_pad->track;
2002 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2003 scontext = (GstMatroskaTrackSubtitleContext *) context;
2005 structure = gst_caps_get_structure (caps, 0);
2006 mimetype = gst_structure_get_name (structure);
2009 scontext->check_utf8 = 1;
2010 scontext->invalid_utf8 = 0;
2011 context->default_duration = 0;
2013 if (!strcmp (mimetype, "subtitle/x-kate")) {
2014 const GValue *streamheader;
2016 gst_matroska_mux_set_codec_id (context,
2017 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2019 gst_matroska_mux_free_codec_priv (context);
2021 streamheader = gst_structure_get_value (structure, "streamheader");
2022 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2023 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2024 ("kate stream headers missing or malformed"));
2028 } else if (!strcmp (mimetype, "text/plain")) {
2029 gst_matroska_mux_set_codec_id (context,
2030 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2031 } else if (!strcmp (mimetype, "application/x-ssa")) {
2032 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2033 } else if (!strcmp (mimetype, "application/x-ass")) {
2034 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2035 } else if (!strcmp (mimetype, "application/x-usf")) {
2036 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2037 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2038 gst_matroska_mux_set_codec_id (context,
2039 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2045 /* maybe some private data, e.g. vobsub */
2046 value = gst_structure_get_value (structure, "codec_data");
2048 buf = gst_value_get_buffer (value);
2051 guint8 *priv_data = NULL;
2053 gst_buffer_map (buf, &map, GST_MAP_READ);
2055 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2056 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2057 " exceeded maximum (%d); discarding", pad,
2058 SUBTITLE_MAX_CODEC_PRIVATE);
2059 gst_buffer_unmap (buf, &map);
2063 gst_matroska_mux_free_codec_priv (context);
2065 priv_data = g_malloc0 (map.size);
2066 memcpy (priv_data, map.data, map.size);
2067 context->codec_priv = priv_data;
2068 context->codec_priv_size = map.size;
2069 gst_buffer_unmap (buf, &map);
2072 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2073 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2082 * gst_matroska_mux_request_new_pad:
2083 * @element: #GstMatroskaMux.
2084 * @templ: #GstPadTemplate.
2085 * @pad_name: New pad name.
2087 * Request pad function for sink templates.
2089 * Returns: New #GstPad.
2092 gst_matroska_mux_request_new_pad (GstElement * element,
2093 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2095 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2096 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2097 GstMatroskaPad *collect_pad;
2098 GstMatroskamuxPad *newpad;
2100 const gchar *pad_name = NULL;
2101 GstMatroskaCapsFunc capsfunc = NULL;
2102 GstMatroskaTrackContext *context = NULL;
2104 gboolean locked = TRUE;
2107 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2108 /* don't mix named and unnamed pads, if the pad already exists we fail when
2109 * trying to add it */
2110 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2111 pad_name = req_name;
2113 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2116 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2117 context = (GstMatroskaTrackContext *)
2118 g_new0 (GstMatroskaTrackAudioContext, 1);
2119 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2120 context->name = g_strdup ("Audio");
2121 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2122 /* don't mix named and unnamed pads, if the pad already exists we fail when
2123 * trying to add it */
2124 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2125 pad_name = req_name;
2127 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2130 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2131 context = (GstMatroskaTrackContext *)
2132 g_new0 (GstMatroskaTrackVideoContext, 1);
2133 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2134 context->name = g_strdup ("Video");
2135 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2136 /* don't mix named and unnamed pads, if the pad already exists we fail when
2137 * trying to add it */
2138 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2139 pad_name = req_name;
2141 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2144 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2145 context = (GstMatroskaTrackContext *)
2146 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2147 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2148 context->name = g_strdup ("Subtitle");
2149 /* setcaps may only provide proper one a lot later */
2150 id = g_strdup ("S_SUB_UNKNOWN");
2153 GST_WARNING_OBJECT (mux, "This is not our template!");
2157 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2158 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2161 gst_matroskamux_pad_init (newpad);
2162 collect_pad = (GstMatroskaPad *)
2163 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2164 sizeof (GstMatroskamuxPad),
2165 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2167 collect_pad->track = context;
2168 gst_matroska_pad_reset (collect_pad, FALSE);
2169 collect_pad->track->codec_id = id;
2171 collect_pad->capsfunc = capsfunc;
2172 gst_pad_set_active (GST_PAD (newpad), TRUE);
2173 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2174 goto pad_add_failed;
2178 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2180 return GST_PAD (newpad);
2185 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2186 gst_object_unref (newpad);
2192 * gst_matroska_mux_release_pad:
2193 * @element: #GstMatroskaMux.
2194 * @pad: Pad to release.
2196 * Release a previously requested pad.
2199 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2201 GstMatroskaMux *mux;
2204 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2206 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2207 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2208 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2210 if (cdata->pad == pad) {
2211 GstClockTime min_dur; /* observed minimum duration */
2213 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2214 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2215 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2216 if (collect_pad->duration < min_dur)
2217 collect_pad->duration = min_dur;
2220 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2221 mux->duration < collect_pad->duration)
2222 mux->duration = collect_pad->duration;
2228 gst_collect_pads2_remove_pad (mux->collect, pad);
2229 if (gst_element_remove_pad (element, pad))
2235 * gst_matroska_mux_track_header:
2236 * @mux: #GstMatroskaMux
2237 * @context: Tack context.
2239 * Write a track header.
2242 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2243 GstMatroskaTrackContext * context)
2245 GstEbmlWrite *ebml = mux->ebml_write;
2248 /* TODO: check if everything necessary is written and check default values */
2250 /* track type goes before the type-specific stuff */
2251 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2252 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2254 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2255 gst_matroska_mux_create_uid ());
2256 if (context->default_duration) {
2257 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2258 context->default_duration);
2260 if (context->language) {
2261 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2265 /* FIXME: until we have a nice way of getting the codecname
2266 * out of the caps, I'm not going to enable this. Too much
2267 * (useless, double, boring) work... */
2268 /* TODO: Use value from tags if any */
2269 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2270 context->codec_name); */
2271 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2273 /* type-specific stuff */
2274 switch (context->type) {
2275 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2276 GstMatroskaTrackVideoContext *videocontext =
2277 (GstMatroskaTrackVideoContext *) context;
2279 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2280 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2281 videocontext->pixel_width);
2282 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2283 videocontext->pixel_height);
2284 if (videocontext->display_width && videocontext->display_height) {
2285 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2286 videocontext->display_width);
2287 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2288 videocontext->display_height);
2290 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2291 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2292 if (videocontext->fourcc) {
2293 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2295 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2296 (gpointer) & fcc_le, 4);
2298 gst_ebml_write_master_finish (ebml, master);
2303 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2304 GstMatroskaTrackAudioContext *audiocontext =
2305 (GstMatroskaTrackAudioContext *) context;
2307 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2308 if (audiocontext->samplerate != 8000)
2309 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2310 audiocontext->samplerate);
2311 if (audiocontext->channels != 1)
2312 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2313 audiocontext->channels);
2314 if (audiocontext->bitdepth) {
2315 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2316 audiocontext->bitdepth);
2318 gst_ebml_write_master_finish (ebml, master);
2323 /* this is what we write for now and must be filled
2324 * and remainder void'ed later on */
2325 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2327 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2330 context->pos = ebml->pos;
2331 /* CodecID is mandatory ... */
2332 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2334 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2335 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2336 SUBTITLE_MAX_CODEC_PRIVATE);
2338 /* real data has to be written at finish */
2342 /* doesn't need type-specific data */
2346 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2347 if (context->codec_priv)
2348 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2349 context->codec_priv, context->codec_priv_size);
2353 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2355 guint64 title_master;
2358 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2360 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2361 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2362 GST_MATROSKA_MUX_CHAPLANG);
2364 gst_ebml_write_master_finish (ebml, title_master);
2368 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2369 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2370 guint64 * master_edition)
2372 guint64 uid, master_chapteratom;
2374 GstTocEntry *cur_entry;
2379 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2381 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2383 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2384 /* create uid for the parent */
2385 uid = gst_matroska_mux_create_uid ();
2386 g_free (edition->uid);
2387 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2390 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2392 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2393 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2394 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2395 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2398 uid = gst_matroska_mux_create_uid ();
2399 gst_toc_entry_get_start_stop (entry, &start, &stop);
2401 master_chapteratom =
2402 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2403 g_free (entry->uid);
2404 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2405 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2406 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2407 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2408 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2409 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2411 cur = entry->subentries;
2412 while (cur != NULL) {
2413 cur_entry = cur->data;
2414 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2419 if (G_LIKELY (entry->tags != NULL)) {
2420 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2422 for (i = 0; i < count; ++i) {
2423 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2424 gst_matroska_mux_write_chapter_title (title, ebml);
2428 /* remove title tag */
2429 if (G_LIKELY (count > 0))
2430 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2433 gst_ebml_write_master_finish (ebml, master_chapteratom);
2437 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2438 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2440 guint64 master_edition = 0;
2442 GstTocEntry *subentry;
2444 cur = entry->subentries;
2445 while (cur != NULL) {
2446 subentry = cur->data;
2447 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2453 if (G_LIKELY (master_edition != 0))
2454 gst_ebml_write_master_finish (ebml, master_edition);
2458 * gst_matroska_mux_start:
2459 * @mux: #GstMatroskaMux
2461 * Start a new matroska file (write headers etc...)
2464 gst_matroska_mux_start (GstMatroskaMux * mux)
2466 GstEbmlWrite *ebml = mux->ebml_write;
2467 const gchar *doctype;
2468 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2469 GST_MATROSKA_ID_TRACKS,
2470 GST_MATROSKA_ID_CHAPTERS,
2471 GST_MATROSKA_ID_CUES,
2472 GST_MATROSKA_ID_TAGS,
2475 guint64 master, child;
2479 GstClockTime duration = 0;
2480 guint32 segment_uid[4];
2481 GTimeVal time = { 0, 0 };
2483 /* if not streaming, check if downstream is seekable */
2484 if (!mux->streamable) {
2488 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2489 if (gst_pad_peer_query (mux->srcpad, query)) {
2490 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2491 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2493 /* have to assume seeking is supported if query not handled downstream */
2494 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2498 mux->streamable = TRUE;
2499 g_object_notify (G_OBJECT (mux), "streamable");
2500 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2501 "streamable=false. Will ignore that and create streamable output "
2504 gst_query_unref (query);
2507 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2508 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2510 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2512 /* we start with a EBML header */
2513 doctype = mux->doctype;
2514 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2515 doctype, mux->doctype_version);
2516 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2518 /* the rest of the header is cached */
2519 gst_ebml_write_set_cache (ebml, 0x1000);
2521 /* start a segment */
2523 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2524 mux->segment_master = ebml->pos;
2526 if (!mux->streamable) {
2527 /* seekhead (table of contents) - we set the positions later */
2528 mux->seekhead_pos = ebml->pos;
2529 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2530 for (i = 0; seekhead_id[i] != 0; i++) {
2531 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2532 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2533 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2534 gst_ebml_write_master_finish (ebml, child);
2536 gst_ebml_write_master_finish (ebml, master);
2539 if (mux->streamable) {
2540 const GstTagList *tags;
2543 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2545 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2546 guint64 master_tags, master_tag;
2548 GST_DEBUG_OBJECT (mux, "Writing tags");
2550 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2551 mux->tags_pos = ebml->pos;
2552 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2553 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2554 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2555 gst_ebml_write_master_finish (ebml, master_tag);
2556 gst_ebml_write_master_finish (ebml, master_tags);
2561 mux->info_pos = ebml->pos;
2562 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2563 for (i = 0; i < 4; i++) {
2564 segment_uid[i] = g_random_int ();
2566 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2567 (guint8 *) segment_uid, 16);
2568 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2569 mux->duration_pos = ebml->pos;
2571 if (!mux->streamable) {
2572 for (collected = mux->collect->data; collected;
2573 collected = g_slist_next (collected)) {
2574 GstMatroskaPad *collect_pad;
2576 gint64 trackduration;
2578 collect_pad = (GstMatroskaPad *) collected->data;
2579 thepad = collect_pad->collect.pad;
2581 /* Query the total length of the track. */
2582 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2583 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2584 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2585 GST_TIME_ARGS (trackduration));
2586 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2587 duration = (GstClockTime) trackduration;
2591 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2592 gst_guint64_to_gdouble (duration) /
2593 gst_guint64_to_gdouble (mux->time_scale));
2595 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2596 "GStreamer plugin version " PACKAGE_VERSION);
2597 if (mux->writing_app && mux->writing_app[0]) {
2598 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2600 g_get_current_time (&time);
2601 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2602 gst_ebml_write_master_finish (ebml, master);
2605 mux->tracks_pos = ebml->pos;
2606 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2608 for (collected = mux->collect->data; collected;
2609 collected = g_slist_next (collected)) {
2610 GstMatroskaPad *collect_pad;
2613 collect_pad = (GstMatroskaPad *) collected->data;
2614 thepad = collect_pad->collect.pad;
2616 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2617 collect_pad->track->codec_id != 0) {
2618 collect_pad->track->num = tracknum++;
2619 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2620 gst_matroska_mux_track_header (mux, collect_pad->track);
2621 gst_ebml_write_master_finish (ebml, child);
2622 /* some remaining pad/track setup */
2623 collect_pad->default_duration_scaled =
2624 gst_util_uint64_scale (collect_pad->track->default_duration,
2625 1, mux->time_scale);
2628 gst_ebml_write_master_finish (ebml, master);
2631 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL && !mux->streamable) {
2632 guint64 master_chapters = 0;
2633 GstTocEntry *toc_entry;
2635 GList *cur, *to_write = NULL;
2638 GST_DEBUG ("Writing chapters");
2640 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2642 /* check whether we have editions or chapters at the root level */
2643 toc_entry = toc->entries->data;
2645 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2646 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2647 gst_toc_entry_set_start_stop (toc_entry, -1, -1);
2649 /* aggregate all chapters without root edition */
2651 while (cur != NULL) {
2652 toc_entry->subentries =
2653 g_list_prepend (toc_entry->subentries, cur->data);
2657 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2658 subentries->data), &start, NULL);
2659 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2660 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2661 subentries->data), NULL, &stop);
2662 gst_toc_entry_set_start_stop (toc_entry, start, stop);
2664 to_write = g_list_append (to_write, toc_entry);
2667 to_write = toc->entries;
2670 /* finally write chapters */
2671 mux->chapters_pos = ebml->pos;
2674 while (cur != NULL) {
2675 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2680 /* close master element if any edition was written */
2681 if (G_LIKELY (master_chapters != 0))
2682 gst_ebml_write_master_finish (ebml, master_chapters);
2684 if (toc_entry != NULL) {
2685 g_list_free (toc_entry->subentries);
2686 toc_entry->subentries = NULL;
2687 gst_toc_entry_free (toc_entry);
2688 g_list_free (to_write);
2692 /* lastly, flush the cache */
2693 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2697 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2700 /* TODO: more sensible tag mappings */
2703 const gchar *matroska_tagname;
2704 const gchar *gstreamer_tagname;
2708 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2709 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2710 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2711 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2712 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2713 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2714 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2715 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2716 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2717 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2718 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2719 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2720 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2721 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2722 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2724 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2726 guint64 simpletag_master;
2728 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2729 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2730 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2732 if (strcmp (tagname_gst, tag) == 0) {
2733 GValue src = { 0, };
2736 if (!gst_tag_list_copy_value (&src, list, tag))
2738 if ((dest = gst_value_serialize (&src))) {
2740 simpletag_master = gst_ebml_write_master_start (ebml,
2741 GST_MATROSKA_ID_SIMPLETAG);
2742 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2743 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2744 gst_ebml_write_master_finish (ebml, simpletag_master);
2747 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2749 g_value_unset (&src);
2756 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2757 const GstTocEntry * entry, guint64 * master_tags)
2759 guint64 master_tag, master_targets;
2763 ebml = mux->ebml_write;
2765 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2766 if (*master_tags == 0) {
2767 mux->tags_pos = ebml->pos;
2768 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2771 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2773 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2775 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2776 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2777 g_ascii_strtoull (entry->uid, NULL, 10));
2779 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2780 g_ascii_strtoull (entry->uid, NULL, 10));
2782 gst_ebml_write_master_finish (ebml, master_targets);
2783 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2784 gst_ebml_write_master_finish (ebml, master_tag);
2787 cur = entry->subentries;
2788 while (cur != NULL) {
2789 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2795 * gst_matroska_mux_finish:
2796 * @mux: #GstMatroskaMux
2798 * Finish a new matroska file (write index etc...)
2801 gst_matroska_mux_finish (GstMatroskaMux * mux)
2803 GstEbmlWrite *ebml = mux->ebml_write;
2805 guint64 duration = 0;
2807 const GstTagList *tags;
2809 /* finish last cluster */
2811 gst_ebml_write_master_finish (ebml, mux->cluster);
2815 if (mux->index != NULL) {
2817 guint64 master, pointentry_master, trackpos_master;
2819 mux->cues_pos = ebml->pos;
2820 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2821 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2823 for (n = 0; n < mux->num_indexes; n++) {
2824 GstMatroskaIndex *idx = &mux->index[n];
2826 pointentry_master = gst_ebml_write_master_start (ebml,
2827 GST_MATROSKA_ID_POINTENTRY);
2828 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2829 idx->time / mux->time_scale);
2830 trackpos_master = gst_ebml_write_master_start (ebml,
2831 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2832 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2833 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2834 idx->pos - mux->segment_master);
2835 gst_ebml_write_master_finish (ebml, trackpos_master);
2836 gst_ebml_write_master_finish (ebml, pointentry_master);
2839 gst_ebml_write_master_finish (ebml, master);
2840 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2844 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2846 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2847 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2848 guint64 master_tags = 0, master_tag;
2852 GST_DEBUG_OBJECT (mux, "Writing tags");
2854 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2857 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2858 mux->tags_pos = ebml->pos;
2859 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2860 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2863 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2865 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2868 gst_ebml_write_master_finish (ebml, master_tag);
2873 while (cur != NULL) {
2874 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2879 if (master_tags != 0)
2880 gst_ebml_write_master_finish (ebml, master_tags);
2883 /* update seekhead. We know that:
2884 * - a seekhead contains 5 entries.
2885 * - order of entries is as above.
2886 * - a seekhead has a 4-byte header + 8-byte length
2887 * - each entry is 2-byte master, 2-byte ID pointer,
2888 * 2-byte length pointer, all 8/1-byte length, 4-
2889 * byte ID and 8-byte length pointer, where the
2890 * length pointer starts at 20.
2891 * - all entries are local to the segment (so pos - segment_master).
2892 * - so each entry is at 12 + 20 + num * 28. */
2893 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2894 mux->info_pos - mux->segment_master);
2895 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2896 mux->tracks_pos - mux->segment_master);
2897 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2898 && mux->chapters_pos > 0) {
2899 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2900 mux->chapters_pos - mux->segment_master);
2903 guint64 my_pos = ebml->pos;
2905 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2906 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2907 gst_ebml_write_seek (ebml, my_pos);
2909 if (mux->index != NULL) {
2910 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2911 mux->cues_pos - mux->segment_master);
2914 guint64 my_pos = ebml->pos;
2916 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2917 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2918 gst_ebml_write_seek (ebml, my_pos);
2922 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
2923 mux->tags_pos - mux->segment_master);
2926 guint64 my_pos = ebml->pos;
2928 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
2929 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2930 gst_ebml_write_seek (ebml, my_pos);
2934 * - first get the overall duration
2935 * (a released track may have left a duration in here)
2936 * - write some track header data for subtitles
2938 duration = mux->duration;
2940 for (collected = mux->collect->data; collected;
2941 collected = g_slist_next (collected)) {
2942 GstMatroskaPad *collect_pad;
2943 GstClockTime min_duration; /* observed minimum duration */
2944 GstMatroskaTrackContext *context;
2945 gint voidleft = 0, fill = 0;
2948 collect_pad = (GstMatroskaPad *) collected->data;
2949 context = collect_pad->track;
2951 GST_DEBUG_OBJECT (mux,
2952 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2953 " end ts %" GST_TIME_FORMAT, collect_pad,
2954 GST_TIME_ARGS (collect_pad->start_ts),
2955 GST_TIME_ARGS (collect_pad->end_ts));
2957 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2958 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2960 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2961 if (collect_pad->duration < min_duration)
2962 collect_pad->duration = min_duration;
2963 GST_DEBUG_OBJECT (collect_pad,
2964 "final track duration: %" GST_TIME_FORMAT,
2965 GST_TIME_ARGS (collect_pad->duration));
2968 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2969 duration < collect_pad->duration)
2970 duration = collect_pad->duration;
2972 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2976 /* write subtitle type and possible private data */
2977 gst_ebml_write_seek (ebml, context->pos);
2978 /* complex way to write ascii to account for extra filling */
2979 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2980 strcpy (codec_id, context->codec_id);
2981 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2982 codec_id, strlen (context->codec_id) + 1 + fill);
2984 if (context->codec_priv)
2985 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2986 context->codec_priv, context->codec_priv_size);
2987 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2988 /* void'ify; sigh, variable sized length field */
2989 if (voidleft == 1) {
2992 } else if (voidleft && voidleft <= 128)
2993 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2994 else if (voidleft >= 130)
2995 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2996 else if (voidleft == 129) {
2997 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2998 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
3002 /* seek back (optional, but do anyway) */
3003 gst_ebml_write_seek (ebml, pos);
3005 /* update duration */
3006 if (duration != 0) {
3007 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3008 GST_TIME_ARGS (duration));
3009 pos = mux->ebml_write->pos;
3010 gst_ebml_write_seek (ebml, mux->duration_pos);
3011 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3012 gst_guint64_to_gdouble (duration) /
3013 gst_guint64_to_gdouble (mux->time_scale));
3014 gst_ebml_write_seek (ebml, pos);
3017 guint64 my_pos = ebml->pos;
3019 gst_ebml_write_seek (ebml, mux->duration_pos);
3020 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3021 gst_ebml_write_seek (ebml, my_pos);
3023 GST_DEBUG_OBJECT (mux, "finishing segment");
3024 /* finish segment - this also writes element length */
3025 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3029 * gst_matroska_mux_buffer_header:
3030 * @track: Track context.
3031 * @relative_timestamp: relative timestamp of the buffer
3032 * @flags: Buffer flags.
3034 * Create a buffer containing buffer header.
3036 * Returns: New buffer.
3039 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3040 gint16 relative_timestamp, int flags)
3043 guint8 *data = g_malloc (4);
3045 hdr = gst_buffer_new_wrapped (data, 4);
3046 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3047 data[0] = track->num | 0x80;
3048 /* time relative to clustertime */
3049 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3057 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3058 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3059 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3062 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3063 GstMatroskaPad * collect_pad, GstBuffer * buf)
3065 GstMatroskaTrackVideoContext *ctx =
3066 (GstMatroskaTrackVideoContext *) collect_pad->track;
3071 guint32 next_parse_offset;
3072 GstBuffer *ret = NULL;
3073 gboolean is_muxing_unit = FALSE;
3075 gst_buffer_map (buf, &map, GST_MAP_READ);
3080 gst_buffer_unmap (buf, &map);
3081 gst_buffer_unref (buf);
3085 /* Check if this buffer contains a picture or end-of-sequence packet */
3086 while (size >= 13) {
3087 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3088 gst_buffer_unmap (buf, &map);
3089 gst_buffer_unref (buf);
3093 parse_code = GST_READ_UINT8 (data + 4);
3094 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3095 if (ctx->dirac_unit) {
3096 gst_buffer_unref (ctx->dirac_unit);
3097 ctx->dirac_unit = NULL;
3099 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3100 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3101 is_muxing_unit = TRUE;
3105 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3107 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3110 data += next_parse_offset;
3111 size -= next_parse_offset;
3114 if (ctx->dirac_unit)
3115 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3117 ctx->dirac_unit = gst_buffer_ref (buf);
3119 gst_buffer_unmap (buf, &map);
3121 if (is_muxing_unit) {
3122 ret = gst_buffer_make_writable (ctx->dirac_unit);
3123 ctx->dirac_unit = NULL;
3124 gst_buffer_copy_into (ret, buf,
3125 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3126 gst_buffer_unref (buf);
3128 gst_buffer_unref (buf);
3136 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3140 GValue streamheader = { 0 };
3141 GValue bufval = { 0 };
3142 GstBuffer *streamheader_buffer;
3143 GstEbmlWrite *ebml = mux->ebml_write;
3145 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3146 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
3147 caps = gst_caps_new_empty_simple ("video/webm");
3149 caps = gst_caps_new_empty_simple ("video/x-matroska");
3151 s = gst_caps_get_structure (caps, 0);
3152 g_value_init (&streamheader, GST_TYPE_ARRAY);
3153 g_value_init (&bufval, GST_TYPE_BUFFER);
3154 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3155 gst_value_set_buffer (&bufval, streamheader_buffer);
3156 gst_value_array_append_value (&streamheader, &bufval);
3157 g_value_unset (&bufval);
3158 gst_structure_set_value (s, "streamheader", &streamheader);
3159 g_value_unset (&streamheader);
3160 gst_caps_replace (&ebml->caps, caps);
3161 gst_buffer_unref (streamheader_buffer);
3162 gst_caps_unref (caps);
3166 * gst_matroska_mux_write_data:
3167 * @mux: #GstMatroskaMux
3168 * @collect_pad: #GstMatroskaPad with the data
3170 * Write collected data (called from gst_matroska_mux_collected).
3172 * Returns: Result of the gst_pad_push issued to write the data.
3174 static GstFlowReturn
3175 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3178 GstEbmlWrite *ebml = mux->ebml_write;
3181 gboolean write_duration;
3182 gint16 relative_timestamp;
3183 gint64 relative_timestamp64;
3184 guint64 block_duration;
3185 gboolean is_video_keyframe = FALSE;
3186 GstMatroskamuxPad *pad;
3189 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3191 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3192 if (collect_pad->track->xiph_headers_to_skip > 0) {
3193 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3194 gst_buffer_unref (buf);
3195 --collect_pad->track->xiph_headers_to_skip;
3199 /* for dirac we have to queue up everything up to a picture unit */
3200 if (collect_pad->track->codec_id != NULL &&
3201 strcmp (collect_pad->track->codec_id,
3202 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3203 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3208 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3209 * this would wreak havoc with time stored in matroska file */
3210 /* TODO: maybe calculate a timestamp by using the previous timestamp
3211 * and default duration */
3212 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3213 GST_WARNING_OBJECT (collect_pad->collect.pad,
3214 "Invalid buffer timestamp; dropping buffer");
3215 gst_buffer_unref (buf);
3219 /* set the timestamp for outgoing buffers */
3220 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3222 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
3223 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3224 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3225 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3226 is_video_keyframe = TRUE;
3230 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3231 * or when we may be reaching the limit of the relative timestamp */
3232 if (mux->cluster_time +
3233 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3234 || is_video_keyframe || mux->force_key_unit_event) {
3235 if (!mux->streamable)
3236 gst_ebml_write_master_finish (ebml, mux->cluster);
3238 /* Forward the GstForceKeyUnit event after finishing the cluster */
3239 if (mux->force_key_unit_event) {
3240 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3241 mux->force_key_unit_event = NULL;
3244 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3245 mux->cluster_pos = ebml->pos;
3246 gst_ebml_write_set_cache (ebml, 0x20);
3248 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3249 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3250 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3252 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3253 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3255 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3256 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3257 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3258 mux->prev_cluster_size);
3263 mux->cluster_pos = ebml->pos;
3264 gst_ebml_write_set_cache (ebml, 0x20);
3265 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3266 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3267 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3268 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3269 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3272 /* update duration of this track */
3273 if (GST_BUFFER_DURATION_IS_VALID (buf))
3274 collect_pad->duration += GST_BUFFER_DURATION (buf);
3276 /* We currently write index entries for all video tracks or for the audio
3277 * track in a single-track audio file. This could be improved by keeping the
3278 * index only for the *first* video track. */
3280 /* TODO: index is useful for every track, should contain the number of
3281 * the block in the cluster which contains the timestamp, should also work
3282 * for files with multiple audio tracks.
3284 if (!mux->streamable &&
3285 (is_video_keyframe ||
3286 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3287 (mux->num_streams == 1)))) {
3290 if (mux->min_index_interval != 0) {
3291 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3292 if (mux->index[last_idx].track == collect_pad->track->num)
3297 if (last_idx < 0 || mux->min_index_interval == 0 ||
3298 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3299 >= mux->min_index_interval)) {
3300 GstMatroskaIndex *idx;
3302 if (mux->num_indexes % 32 == 0) {
3303 mux->index = g_renew (GstMatroskaIndex, mux->index,
3304 mux->num_indexes + 32);
3306 idx = &mux->index[mux->num_indexes++];
3308 idx->pos = mux->cluster_pos;
3309 idx->time = GST_BUFFER_TIMESTAMP (buf);
3310 idx->track = collect_pad->track->num;
3314 /* Check if the duration differs from the default duration. */
3315 write_duration = FALSE;
3317 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3318 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3319 1, mux->time_scale);
3321 /* small difference should be ok. */
3322 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3323 block_duration < collect_pad->default_duration_scaled - 1) {
3324 write_duration = TRUE;
3328 /* write the block, for doctype v2 use SimpleBlock if possible
3329 * one slice (*breath*).
3330 * FIXME: Need to do correct lacing! */
3331 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3332 if (relative_timestamp64 >= 0) {
3333 /* round the timestamp */
3334 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3336 /* round the timestamp */
3337 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3339 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3341 if (mux->doctype_version > 1 && !write_duration) {
3343 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3346 gst_matroska_mux_create_buffer_header (collect_pad->track,
3347 relative_timestamp, flags);
3348 gst_ebml_write_set_cache (ebml, 0x40);
3349 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3350 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3351 gst_ebml_write_buffer (ebml, hdr);
3352 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3353 gst_ebml_write_buffer (ebml, buf);
3355 return gst_ebml_last_write_result (ebml);
3357 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3358 /* write and call order slightly unnatural,
3359 * but avoids seek and minizes pushing */
3360 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3362 gst_matroska_mux_create_buffer_header (collect_pad->track,
3363 relative_timestamp, 0);
3365 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3366 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3367 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3368 gst_ebml_write_buffer (ebml, hdr);
3369 gst_ebml_write_master_finish_full (ebml, blockgroup,
3370 gst_buffer_get_size (buf));
3371 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3372 gst_ebml_write_buffer (ebml, buf);
3374 return gst_ebml_last_write_result (ebml);
3379 * gst_matroska_mux_handle_buffer:
3380 * @pads: #GstCollectPads2
3381 * @uuser_data: #GstMatroskaMux
3383 * Collectpads callback.
3385 * Returns: #GstFlowReturn
3387 static GstFlowReturn
3388 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3389 GstBuffer * buf, gpointer user_data)
3391 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3392 GstEbmlWrite *ebml = mux->ebml_write;
3393 GstMatroskaPad *best;
3394 GstFlowReturn ret = GST_FLOW_OK;
3396 GST_DEBUG_OBJECT (mux, "Collected pads");
3398 /* start with a header */
3399 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3400 if (mux->collect->data == NULL) {
3401 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3402 ("No input streams configured"));
3403 return GST_FLOW_ERROR;
3405 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3406 gst_ebml_start_streamheader (ebml);
3407 gst_matroska_mux_start (mux);
3408 gst_matroska_mux_stop_streamheader (mux);
3409 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3412 /* provided with stream to write from */
3413 best = (GstMatroskaPad *) data;
3415 /* if there is no best pad, we have reached EOS */
3417 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3418 if (!mux->streamable) {
3419 gst_matroska_mux_finish (mux);
3421 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3423 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3428 /* if we have a best stream, should also have a buffer */
3431 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3432 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3433 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3434 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3436 /* make note of first and last encountered timestamps, so we can calculate
3437 * the actual duration later when we send an updated header on eos */
3438 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3439 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3440 GstClockTime end_ts = start_ts;
3442 if (GST_BUFFER_DURATION_IS_VALID (buf))
3443 end_ts += GST_BUFFER_DURATION (buf);
3444 else if (best->track->default_duration)
3445 end_ts += best->track->default_duration;
3447 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3448 best->end_ts = end_ts;
3450 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3451 start_ts < best->start_ts))
3452 best->start_ts = start_ts;
3455 /* write one buffer */
3456 ret = gst_matroska_mux_write_data (mux, best, buf);
3464 * gst_matroska_mux_change_state:
3465 * @element: #GstMatroskaMux
3466 * @transition: State change transition.
3468 * Change the muxer state.
3470 * Returns: #GstStateChangeReturn
3472 static GstStateChangeReturn
3473 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3475 GstStateChangeReturn ret;
3476 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3478 switch (transition) {
3479 case GST_STATE_CHANGE_NULL_TO_READY:
3481 case GST_STATE_CHANGE_READY_TO_PAUSED:
3482 gst_collect_pads2_start (mux->collect);
3484 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3486 case GST_STATE_CHANGE_PAUSED_TO_READY:
3487 gst_collect_pads2_stop (mux->collect);
3493 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3495 switch (transition) {
3496 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3498 case GST_STATE_CHANGE_PAUSED_TO_READY:
3499 gst_matroska_mux_reset (GST_ELEMENT (mux));
3501 case GST_STATE_CHANGE_READY_TO_NULL:
3511 gst_matroska_mux_set_property (GObject * object,
3512 guint prop_id, const GValue * value, GParamSpec * pspec)
3514 GstMatroskaMux *mux;
3516 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3517 mux = GST_MATROSKA_MUX (object);
3520 case ARG_WRITING_APP:
3521 if (!g_value_get_string (value)) {
3522 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3525 g_free (mux->writing_app);
3526 mux->writing_app = g_value_dup_string (value);
3528 case ARG_DOCTYPE_VERSION:
3529 mux->doctype_version = g_value_get_int (value);
3531 case ARG_MIN_INDEX_INTERVAL:
3532 mux->min_index_interval = g_value_get_int64 (value);
3534 case ARG_STREAMABLE:
3535 mux->streamable = g_value_get_boolean (value);
3538 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3544 gst_matroska_mux_get_property (GObject * object,
3545 guint prop_id, GValue * value, GParamSpec * pspec)
3547 GstMatroskaMux *mux;
3549 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3550 mux = GST_MATROSKA_MUX (object);
3553 case ARG_WRITING_APP:
3554 g_value_set_string (value, mux->writing_app);
3556 case ARG_DOCTYPE_VERSION:
3557 g_value_set_int (value, mux->doctype_version);
3559 case ARG_MIN_INDEX_INTERVAL:
3560 g_value_set_int64 (value, mux->min_index_interval);
3562 case ARG_STREAMABLE:
3563 g_value_set_boolean (value, mux->streamable);
3566 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);