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;
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 */
802 if (mux->chapters_pos > 0)
805 GST_DEBUG_OBJECT (mux, "received toc event");
806 gst_event_parse_toc (event, &toc, NULL);
809 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
810 gst_toc_setter_reset_toc (GST_TOC_SETTER (mux));
811 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
814 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
818 gst_event_unref (event);
819 /* handled this, don't want collectpads to forward it downstream */
823 case GST_EVENT_CUSTOM_DOWNSTREAM:{
824 const GstStructure *structure;
826 structure = gst_event_get_structure (event);
827 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
828 gst_event_replace (&mux->force_key_unit_event, NULL);
829 mux->force_key_unit_event = event;
831 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
832 !strcmp ("dvd-spu-clut-change",
833 gst_structure_get_string (structure, "event"))) {
838 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
839 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
840 GST_DEBUG_OBJECT (pad, "... discarding");
843 /* first transform event data into table form */
844 for (i = 0; i < 16; i++) {
845 g_snprintf (name, sizeof (name), "clut%02d", i);
846 if (!gst_structure_get_int (structure, name, &value)) {
847 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
848 "contain %s field", name);
854 /* transform into private data for stream; text form */
855 gst_matroska_mux_build_vobsub_private (context, clut);
864 return gst_collect_pads2_event_default (pads, data, event, FALSE);
870 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
873 g_assert (context && id);
874 if (context->codec_id)
875 g_free (context->codec_id);
876 context->codec_id = g_strdup (id);
880 * gst_matroska_mux_video_pad_setcaps:
881 * @pad: Pad which got the caps.
884 * Setcaps function for video sink pad.
886 * Returns: #TRUE on success.
889 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
891 GstMatroskaTrackContext *context = NULL;
892 GstMatroskaTrackVideoContext *videocontext;
894 GstMatroskaPad *collect_pad;
895 GstStructure *structure;
896 const gchar *mimetype;
897 const GValue *value = NULL;
898 GstBuffer *codec_buf = NULL;
899 gint width, height, pixel_width, pixel_height;
901 gboolean interlaced = FALSE;
903 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
906 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
907 g_assert (collect_pad);
908 context = collect_pad->track;
910 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
911 videocontext = (GstMatroskaTrackVideoContext *) context;
913 /* gst -> matroska ID'ing */
914 structure = gst_caps_get_structure (caps, 0);
916 mimetype = gst_structure_get_name (structure);
918 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
920 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
922 if (!strcmp (mimetype, "video/x-theora")) {
923 /* we'll extract the details later from the theora identification header */
927 /* get general properties */
928 /* spec says it is mandatory */
929 if (!gst_structure_get_int (structure, "width", &width) ||
930 !gst_structure_get_int (structure, "height", &height))
933 videocontext->pixel_width = width;
934 videocontext->pixel_height = height;
936 /* set vp8 defaults or let user override it */
937 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
938 && (!strcmp (mimetype, "video/x-vp8")))
939 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
940 DEFAULT_PAD_FRAME_DURATION_VP8;
942 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
943 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
945 context->default_duration =
946 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
947 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
948 GST_TIME_ARGS (context->default_duration));
950 context->default_duration = 0;
952 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
953 &pixel_width, &pixel_height)) {
954 if (pixel_width > pixel_height) {
955 videocontext->display_width = width * pixel_width / pixel_height;
956 videocontext->display_height = height;
957 } else if (pixel_width < pixel_height) {
958 videocontext->display_width = width;
959 videocontext->display_height = height * pixel_height / pixel_width;
961 videocontext->display_width = 0;
962 videocontext->display_height = 0;
965 videocontext->display_width = 0;
966 videocontext->display_height = 0;
971 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
972 videocontext->fourcc = 0;
974 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
975 * data and other settings
979 /* extract codec_data, may turn out needed */
980 value = gst_structure_get_value (structure, "codec_data");
982 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
985 if (!strcmp (mimetype, "video/x-raw")) {
987 gst_matroska_mux_set_codec_id (context,
988 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
989 fstr = gst_structure_get_string (structure, "format");
990 if (fstr && strlen (fstr) == 4)
991 videocontext->fourcc = GST_STR_FOURCC (fstr);
992 } else if (!strcmp (mimetype, "image/jpeg")) {
993 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
994 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
995 ||!strcmp (mimetype, "video/x-huffyuv")
996 || !strcmp (mimetype, "video/x-divx")
997 || !strcmp (mimetype, "video/x-dv")
998 || !strcmp (mimetype, "video/x-h263")
999 || !strcmp (mimetype, "video/x-msmpeg")
1000 || !strcmp (mimetype, "video/x-wmv")
1001 || !strcmp (mimetype, "image/jpeg")) {
1002 gst_riff_strf_vids *bih;
1003 gint size = sizeof (gst_riff_strf_vids);
1006 if (!strcmp (mimetype, "video/x-xvid"))
1007 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
1008 else if (!strcmp (mimetype, "video/x-huffyuv"))
1009 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1010 else if (!strcmp (mimetype, "video/x-dv"))
1011 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1012 else if (!strcmp (mimetype, "video/x-h263"))
1013 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1014 else if (!strcmp (mimetype, "video/x-divx")) {
1017 gst_structure_get_int (structure, "divxversion", &divxversion);
1018 switch (divxversion) {
1020 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1023 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1026 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1029 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1032 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1033 switch (msmpegversion) {
1035 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1038 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1044 } else if (!strcmp (mimetype, "video/x-wmv")) {
1048 fstr = gst_structure_get_string (structure, "format");
1049 if (fstr && strlen (fstr) == 4) {
1050 fourcc = GST_STR_FOURCC (fstr);
1051 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1052 if (wmvversion == 2) {
1053 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1054 } else if (wmvversion == 1) {
1055 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1056 } else if (wmvversion == 3) {
1057 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1060 } else if (!strcmp (mimetype, "image/jpeg")) {
1061 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1067 bih = g_new0 (gst_riff_strf_vids, 1);
1068 GST_WRITE_UINT32_LE (&bih->size, size);
1069 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1070 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1071 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1072 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1073 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1074 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1075 videocontext->pixel_height * 3);
1077 /* process codec private/initialization data, if any */
1079 size += gst_buffer_get_size (codec_buf);
1080 bih = g_realloc (bih, size);
1081 GST_WRITE_UINT32_LE (&bih->size, size);
1082 gst_buffer_extract (codec_buf, 0,
1083 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1086 gst_matroska_mux_set_codec_id (context,
1087 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1088 gst_matroska_mux_free_codec_priv (context);
1089 context->codec_priv = (gpointer) bih;
1090 context->codec_priv_size = size;
1091 } else if (!strcmp (mimetype, "video/x-h264")) {
1092 gst_matroska_mux_set_codec_id (context,
1093 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1094 gst_matroska_mux_free_codec_priv (context);
1095 /* Create avcC header */
1096 if (codec_buf != NULL) {
1097 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1098 context->codec_priv = g_malloc0 (context->codec_priv_size);
1099 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1101 } else if (!strcmp (mimetype, "video/x-theora")) {
1102 const GValue *streamheader;
1104 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1106 gst_matroska_mux_free_codec_priv (context);
1108 streamheader = gst_structure_get_value (structure, "streamheader");
1109 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1110 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1111 ("theora stream headers missing or malformed"));
1114 } else if (!strcmp (mimetype, "video/x-dirac")) {
1115 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1116 } else if (!strcmp (mimetype, "video/x-vp8")) {
1117 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1118 } else if (!strcmp (mimetype, "video/mpeg")) {
1121 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1122 switch (mpegversion) {
1124 gst_matroska_mux_set_codec_id (context,
1125 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1128 gst_matroska_mux_set_codec_id (context,
1129 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1132 gst_matroska_mux_set_codec_id (context,
1133 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1139 /* global headers may be in codec data */
1140 if (codec_buf != NULL) {
1141 gst_matroska_mux_free_codec_priv (context);
1142 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1143 context->codec_priv = g_malloc0 (context->codec_priv_size);
1144 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1146 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1148 /* can only make it here if preceding case verified it was version 3 */
1149 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1150 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1152 const GValue *mdpr_data;
1154 gst_structure_get_int (structure, "rmversion", &rmversion);
1155 switch (rmversion) {
1157 gst_matroska_mux_set_codec_id (context,
1158 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1161 gst_matroska_mux_set_codec_id (context,
1162 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1165 gst_matroska_mux_set_codec_id (context,
1166 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1169 gst_matroska_mux_set_codec_id (context,
1170 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1176 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1177 if (mdpr_data != NULL) {
1178 guint8 *priv_data = NULL;
1179 guint priv_data_size = 0;
1181 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1183 priv_data_size = gst_buffer_get_size (codec_data_buf);
1184 priv_data = g_malloc0 (priv_data_size);
1186 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1188 gst_matroska_mux_free_codec_priv (context);
1189 context->codec_priv = priv_data;
1190 context->codec_priv_size = priv_data_size;
1199 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1200 GST_PAD_NAME (pad), caps);
1205 /* N > 0 to expect a particular number of headers, negative if the
1206 number of headers is variable */
1208 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1209 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1211 GstBuffer **buf = NULL;
1214 guint bufi, i, offset, priv_data_size;
1216 if (streamheader == NULL)
1217 goto no_stream_headers;
1219 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1222 bufarr = g_value_peek_pointer (streamheader);
1223 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1225 if (N > 0 && bufarr->len != N)
1228 context->xiph_headers_to_skip = bufarr->len;
1230 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1231 for (i = 0; i < bufarr->len; i++) {
1232 GValue *bufval = &g_array_index (bufarr, GValue, i);
1234 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1236 goto wrong_content_type;
1239 buf[i] = g_value_peek_pointer (bufval);
1243 if (bufarr->len > 0) {
1244 for (i = 0; i < bufarr->len - 1; i++) {
1245 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1249 for (i = 0; i < bufarr->len; ++i) {
1250 priv_data_size += gst_buffer_get_size (buf[i]);
1253 priv_data = g_malloc0 (priv_data_size);
1255 priv_data[0] = bufarr->len - 1;
1258 if (bufarr->len > 0) {
1259 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1260 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1261 priv_data[offset++] = 0xff;
1263 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1267 for (i = 0; i < bufarr->len; ++i) {
1268 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1269 offset += gst_buffer_get_size (buf[i]);
1272 gst_matroska_mux_free_codec_priv (context);
1273 context->codec_priv = priv_data;
1274 context->codec_priv_size = priv_data_size;
1277 *p_buf0 = gst_buffer_ref (buf[0]);
1286 GST_WARNING ("required streamheaders missing in sink caps!");
1291 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1292 G_VALUE_TYPE_NAME (streamheader));
1297 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1302 GST_WARNING ("streamheaders array does not contain GstBuffers");
1308 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1309 GstMatroskaTrackContext * context)
1311 GstBuffer *buf0 = NULL;
1313 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1316 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1317 GST_WARNING ("First vorbis header too small, ignoring");
1319 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1320 GstMatroskaTrackAudioContext *audiocontext;
1324 gst_buffer_map (buf0, &map, GST_MAP_READ);
1325 hdr = map.data + 1 + 6 + 4;
1326 audiocontext = (GstMatroskaTrackAudioContext *) context;
1327 audiocontext->channels = GST_READ_UINT8 (hdr);
1328 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1329 gst_buffer_unmap (buf0, &map);
1334 gst_buffer_unref (buf0);
1340 theora_streamheader_to_codecdata (const GValue * streamheader,
1341 GstMatroskaTrackContext * context)
1343 GstBuffer *buf0 = NULL;
1345 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1348 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1349 GST_WARNING ("First theora header too small, ignoring");
1350 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1351 GST_WARNING ("First header not a theora identification header, ignoring");
1353 GstMatroskaTrackVideoContext *videocontext;
1354 guint fps_num, fps_denom, par_num, par_denom;
1358 gst_buffer_map (buf0, &map, GST_MAP_READ);
1359 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1361 videocontext = (GstMatroskaTrackVideoContext *) context;
1362 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1363 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1364 hdr += 3 + 3 + 1 + 1;
1365 fps_num = GST_READ_UINT32_BE (hdr);
1366 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1367 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1368 fps_denom, fps_num);
1370 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1371 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1372 if (par_num > 0 && par_num > 0) {
1373 if (par_num > par_denom) {
1374 videocontext->display_width =
1375 videocontext->pixel_width * par_num / par_denom;
1376 videocontext->display_height = videocontext->pixel_height;
1377 } else if (par_num < par_denom) {
1378 videocontext->display_width = videocontext->pixel_width;
1379 videocontext->display_height =
1380 videocontext->pixel_height * par_denom / par_num;
1382 videocontext->display_width = 0;
1383 videocontext->display_height = 0;
1386 videocontext->display_width = 0;
1387 videocontext->display_height = 0;
1391 gst_buffer_unmap (buf0, &map);
1395 gst_buffer_unref (buf0);
1401 kate_streamheader_to_codecdata (const GValue * streamheader,
1402 GstMatroskaTrackContext * context)
1404 GstBuffer *buf0 = NULL;
1406 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1409 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1410 GST_WARNING ("First kate header too small, ignoring");
1411 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1412 GST_WARNING ("First header not a kate identification header, ignoring");
1416 gst_buffer_unref (buf0);
1422 flac_streamheader_to_codecdata (const GValue * streamheader,
1423 GstMatroskaTrackContext * context)
1430 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1431 GST_WARNING ("No or invalid streamheader field in the caps");
1435 bufarr = g_value_peek_pointer (streamheader);
1436 if (bufarr->len < 2) {
1437 GST_WARNING ("Too few headers in streamheader field");
1441 context->xiph_headers_to_skip = bufarr->len + 1;
1443 bufval = &g_array_index (bufarr, GValue, 0);
1444 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1445 GST_WARNING ("streamheaders array does not contain GstBuffers");
1449 buffer = g_value_peek_pointer (bufval);
1451 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1452 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1453 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1454 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1455 GST_WARNING ("Invalid streamheader for FLAC");
1459 gst_matroska_mux_free_codec_priv (context);
1460 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1461 context->codec_priv = g_malloc (context->codec_priv_size);
1462 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1464 for (i = 1; i < bufarr->len; i++) {
1466 bufval = &g_array_index (bufarr, GValue, i);
1468 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1469 gst_matroska_mux_free_codec_priv (context);
1470 GST_WARNING ("streamheaders array does not contain GstBuffers");
1474 buffer = g_value_peek_pointer (bufval);
1476 old_size = context->codec_priv_size;
1477 context->codec_priv_size += gst_buffer_get_size (buffer);
1479 context->codec_priv = g_realloc (context->codec_priv,
1480 context->codec_priv_size);
1481 gst_buffer_extract (buffer, 0,
1482 (guint8 *) context->codec_priv + old_size, -1);
1489 speex_streamheader_to_codecdata (const GValue * streamheader,
1490 GstMatroskaTrackContext * context)
1497 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1498 GST_WARNING ("No or invalid streamheader field in the caps");
1502 bufarr = g_value_peek_pointer (streamheader);
1503 if (bufarr->len != 2) {
1504 GST_WARNING ("Too few headers in streamheader field");
1508 context->xiph_headers_to_skip = bufarr->len + 1;
1510 bufval = &g_array_index (bufarr, GValue, 0);
1511 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1512 GST_WARNING ("streamheaders array does not contain GstBuffers");
1516 buffer = g_value_peek_pointer (bufval);
1518 if (gst_buffer_get_size (buffer) < 80
1519 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1520 GST_WARNING ("Invalid streamheader for Speex");
1524 gst_matroska_mux_free_codec_priv (context);
1525 context->codec_priv_size = gst_buffer_get_size (buffer);
1526 context->codec_priv = g_malloc (context->codec_priv_size);
1527 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1529 bufval = &g_array_index (bufarr, GValue, 1);
1531 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1532 gst_matroska_mux_free_codec_priv (context);
1533 GST_WARNING ("streamheaders array does not contain GstBuffers");
1537 buffer = g_value_peek_pointer (bufval);
1539 old_size = context->codec_priv_size;
1540 context->codec_priv_size += gst_buffer_get_size (buffer);
1541 context->codec_priv = g_realloc (context->codec_priv,
1542 context->codec_priv_size);
1543 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1548 static const gchar *
1549 aac_codec_data_to_codec_id (GstBuffer * buf)
1551 const gchar *result;
1554 /* default to MAIN */
1557 if (gst_buffer_get_size (buf) >= 2) {
1558 gst_buffer_extract (buf, 0, &profile, 1);
1576 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1585 * gst_matroska_mux_audio_pad_setcaps:
1586 * @pad: Pad which got the caps.
1589 * Setcaps function for audio sink pad.
1591 * Returns: #TRUE on success.
1594 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1596 GstMatroskaTrackContext *context = NULL;
1597 GstMatroskaTrackAudioContext *audiocontext;
1598 GstMatroskaMux *mux;
1599 GstMatroskaPad *collect_pad;
1600 const gchar *mimetype;
1601 gint samplerate = 0, channels = 0;
1602 GstStructure *structure;
1603 const GValue *codec_data = NULL;
1604 GstBuffer *buf = NULL;
1605 const gchar *stream_format = NULL;
1607 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1610 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1611 g_assert (collect_pad);
1612 context = collect_pad->track;
1614 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1615 audiocontext = (GstMatroskaTrackAudioContext *) context;
1617 structure = gst_caps_get_structure (caps, 0);
1618 mimetype = gst_structure_get_name (structure);
1621 gst_structure_get_int (structure, "rate", &samplerate);
1622 gst_structure_get_int (structure, "channels", &channels);
1624 audiocontext->samplerate = samplerate;
1625 audiocontext->channels = channels;
1626 audiocontext->bitdepth = 0;
1627 context->default_duration = 0;
1629 codec_data = gst_structure_get_value (structure, "codec_data");
1631 buf = gst_value_get_buffer (codec_data);
1633 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1634 * data and other settings
1638 if (!strcmp (mimetype, "audio/mpeg")) {
1639 gint mpegversion = 0;
1641 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1642 switch (mpegversion) {
1648 gst_structure_get_int (structure, "layer", &layer);
1650 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1651 GST_WARNING_OBJECT (mux,
1652 "Unable to determine MPEG audio version, assuming 1");
1658 else if (layer == 2)
1660 else if (version == 2)
1665 context->default_duration =
1666 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1670 gst_matroska_mux_set_codec_id (context,
1671 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1674 gst_matroska_mux_set_codec_id (context,
1675 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1678 gst_matroska_mux_set_codec_id (context,
1679 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1688 stream_format = gst_structure_get_string (structure, "stream-format");
1689 /* check this is raw aac */
1690 if (stream_format) {
1691 if (strcmp (stream_format, "raw") != 0) {
1692 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1696 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1701 if (mpegversion == 2)
1703 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1704 aac_codec_data_to_codec_id (buf));
1705 else if (mpegversion == 4)
1707 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1708 aac_codec_data_to_codec_id (buf));
1710 g_assert_not_reached ();
1712 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1719 } else if (!strcmp (mimetype, "audio/x-raw")) {
1722 gst_audio_info_init (&info);
1723 if (!gst_audio_info_from_caps (&info, caps)) {
1724 GST_DEBUG_OBJECT (mux,
1725 "broken caps, rejected by gst_audio_info_from_caps");
1729 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1730 case GST_AUDIO_FORMAT_U8:
1731 case GST_AUDIO_FORMAT_S16BE:
1732 case GST_AUDIO_FORMAT_S16LE:
1733 case GST_AUDIO_FORMAT_S24BE:
1734 case GST_AUDIO_FORMAT_S24LE:
1735 case GST_AUDIO_FORMAT_S32BE:
1736 case GST_AUDIO_FORMAT_S32LE:
1737 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1738 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1741 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1742 gst_matroska_mux_set_codec_id (context,
1743 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1745 gst_matroska_mux_set_codec_id (context,
1746 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1748 case GST_AUDIO_FORMAT_F32LE:
1749 case GST_AUDIO_FORMAT_F64LE:
1750 gst_matroska_mux_set_codec_id (context,
1751 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1755 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1759 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1760 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1761 const GValue *streamheader;
1763 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1765 gst_matroska_mux_free_codec_priv (context);
1767 streamheader = gst_structure_get_value (structure, "streamheader");
1768 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1769 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1770 ("vorbis stream headers missing or malformed"));
1773 } else if (!strcmp (mimetype, "audio/x-flac")) {
1774 const GValue *streamheader;
1776 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1778 gst_matroska_mux_free_codec_priv (context);
1780 streamheader = gst_structure_get_value (structure, "streamheader");
1781 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1782 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1783 ("flac stream headers missing or malformed"));
1786 } else if (!strcmp (mimetype, "audio/x-speex")) {
1787 const GValue *streamheader;
1789 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1790 gst_matroska_mux_free_codec_priv (context);
1792 streamheader = gst_structure_get_value (structure, "streamheader");
1793 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1794 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1795 ("speex stream headers missing or malformed"));
1798 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1799 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1800 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1801 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1802 } else if (!strcmp (mimetype, "audio/x-dts")) {
1803 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1804 } else if (!strcmp (mimetype, "audio/x-tta")) {
1807 /* TTA frame duration */
1808 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1810 gst_structure_get_int (structure, "width", &width);
1811 audiocontext->bitdepth = width;
1812 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1814 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1816 const GValue *mdpr_data;
1818 gst_structure_get_int (structure, "raversion", &raversion);
1819 switch (raversion) {
1821 gst_matroska_mux_set_codec_id (context,
1822 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1825 gst_matroska_mux_set_codec_id (context,
1826 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1829 gst_matroska_mux_set_codec_id (context,
1830 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1836 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1837 if (mdpr_data != NULL) {
1838 guint8 *priv_data = NULL;
1839 guint priv_data_size = 0;
1841 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1843 priv_data_size = gst_buffer_get_size (codec_data_buf);
1844 priv_data = g_malloc0 (priv_data_size);
1846 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1848 gst_matroska_mux_free_codec_priv (context);
1850 context->codec_priv = priv_data;
1851 context->codec_priv_size = priv_data_size;
1854 } else if (!strcmp (mimetype, "audio/x-wma")
1855 || !strcmp (mimetype, "audio/x-alaw")
1856 || !strcmp (mimetype, "audio/x-mulaw")) {
1858 guint codec_priv_size;
1863 if (samplerate == 0 || channels == 0) {
1864 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1868 if (!strcmp (mimetype, "audio/x-wma")) {
1872 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1873 || !gst_structure_get_int (structure, "block_align", &block_align)
1874 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1875 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1880 switch (wmaversion) {
1882 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1885 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1888 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1891 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1895 if (gst_structure_get_int (structure, "depth", &depth))
1896 audiocontext->bitdepth = depth;
1897 } else if (!strcmp (mimetype, "audio/x-alaw")
1898 || !strcmp (mimetype, "audio/x-mulaw")) {
1899 audiocontext->bitdepth = 8;
1900 if (!strcmp (mimetype, "audio/x-alaw"))
1901 format = GST_RIFF_WAVE_FORMAT_ALAW;
1903 format = GST_RIFF_WAVE_FORMAT_MULAW;
1905 block_align = channels;
1906 bitrate = block_align * samplerate;
1908 g_assert (format != 0);
1910 codec_priv_size = WAVEFORMATEX_SIZE;
1912 codec_priv_size += gst_buffer_get_size (buf);
1914 /* serialize waveformatex structure */
1915 codec_priv = g_malloc0 (codec_priv_size);
1916 GST_WRITE_UINT16_LE (codec_priv, format);
1917 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1918 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1919 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1920 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1921 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1923 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
1925 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1927 /* process codec private/initialization data, if any */
1929 gst_buffer_extract (buf, 0,
1930 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
1933 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1934 gst_matroska_mux_free_codec_priv (context);
1935 context->codec_priv = (gpointer) codec_priv;
1936 context->codec_priv_size = codec_priv_size;
1944 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1945 GST_PAD_NAME (pad), caps);
1950 /* we probably don't have the data at start,
1951 * so have to reserve (a maximum) space to write this at the end.
1952 * bit spacy, but some formats can hold quite some */
1953 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1956 * gst_matroska_mux_subtitle_pad_setcaps:
1957 * @pad: Pad which got the caps.
1960 * Setcaps function for subtitle sink pad.
1962 * Returns: #TRUE on success.
1965 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1967 /* There is now (at least) one such alement (kateenc), and I'm going
1968 to handle it here and claim it works when it can be piped back
1969 through GStreamer and VLC */
1971 GstMatroskaTrackContext *context = NULL;
1972 GstMatroskaTrackSubtitleContext *scontext;
1973 GstMatroskaMux *mux;
1974 GstMatroskaPad *collect_pad;
1975 const gchar *mimetype;
1976 GstStructure *structure;
1977 const GValue *value = NULL;
1978 GstBuffer *buf = NULL;
1979 gboolean ret = TRUE;
1981 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1984 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1985 g_assert (collect_pad);
1986 context = collect_pad->track;
1988 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1989 scontext = (GstMatroskaTrackSubtitleContext *) context;
1991 structure = gst_caps_get_structure (caps, 0);
1992 mimetype = gst_structure_get_name (structure);
1995 scontext->check_utf8 = 1;
1996 scontext->invalid_utf8 = 0;
1997 context->default_duration = 0;
1999 if (!strcmp (mimetype, "subtitle/x-kate")) {
2000 const GValue *streamheader;
2002 gst_matroska_mux_set_codec_id (context,
2003 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2005 gst_matroska_mux_free_codec_priv (context);
2007 streamheader = gst_structure_get_value (structure, "streamheader");
2008 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2009 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2010 ("kate stream headers missing or malformed"));
2014 } else if (!strcmp (mimetype, "text/plain")) {
2015 gst_matroska_mux_set_codec_id (context,
2016 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2017 } else if (!strcmp (mimetype, "application/x-ssa")) {
2018 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2019 } else if (!strcmp (mimetype, "application/x-ass")) {
2020 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2021 } else if (!strcmp (mimetype, "application/x-usf")) {
2022 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2023 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2024 gst_matroska_mux_set_codec_id (context,
2025 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2031 /* maybe some private data, e.g. vobsub */
2032 value = gst_structure_get_value (structure, "codec_data");
2034 buf = gst_value_get_buffer (value);
2037 guint8 *priv_data = NULL;
2039 gst_buffer_map (buf, &map, GST_MAP_READ);
2041 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2042 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2043 " exceeded maximum (%d); discarding", pad,
2044 SUBTITLE_MAX_CODEC_PRIVATE);
2045 gst_buffer_unmap (buf, &map);
2049 gst_matroska_mux_free_codec_priv (context);
2051 priv_data = g_malloc0 (map.size);
2052 memcpy (priv_data, map.data, map.size);
2053 context->codec_priv = priv_data;
2054 context->codec_priv_size = map.size;
2055 gst_buffer_unmap (buf, &map);
2058 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2059 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2068 * gst_matroska_mux_request_new_pad:
2069 * @element: #GstMatroskaMux.
2070 * @templ: #GstPadTemplate.
2071 * @pad_name: New pad name.
2073 * Request pad function for sink templates.
2075 * Returns: New #GstPad.
2078 gst_matroska_mux_request_new_pad (GstElement * element,
2079 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2081 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2082 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2083 GstMatroskaPad *collect_pad;
2084 GstMatroskamuxPad *newpad;
2086 const gchar *pad_name = NULL;
2087 GstMatroskaCapsFunc capsfunc = NULL;
2088 GstMatroskaTrackContext *context = NULL;
2090 gboolean locked = TRUE;
2093 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2094 /* don't mix named and unnamed pads, if the pad already exists we fail when
2095 * trying to add it */
2096 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2097 pad_name = req_name;
2099 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2102 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2103 context = (GstMatroskaTrackContext *)
2104 g_new0 (GstMatroskaTrackAudioContext, 1);
2105 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2106 context->name = g_strdup ("Audio");
2107 } else if (templ == gst_element_class_get_pad_template (klass, "video_%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, "video_%u", &pad_id) == 1) {
2111 pad_name = req_name;
2113 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2116 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2117 context = (GstMatroskaTrackContext *)
2118 g_new0 (GstMatroskaTrackVideoContext, 1);
2119 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2120 context->name = g_strdup ("Video");
2121 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%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, "subtitle_%u", &pad_id) == 1) {
2125 pad_name = req_name;
2127 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2130 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2131 context = (GstMatroskaTrackContext *)
2132 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2133 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2134 context->name = g_strdup ("Subtitle");
2135 /* setcaps may only provide proper one a lot later */
2136 id = g_strdup ("S_SUB_UNKNOWN");
2139 GST_WARNING_OBJECT (mux, "This is not our template!");
2143 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2144 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2147 gst_matroskamux_pad_init (newpad);
2148 collect_pad = (GstMatroskaPad *)
2149 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2150 sizeof (GstMatroskamuxPad),
2151 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2153 collect_pad->track = context;
2154 gst_matroska_pad_reset (collect_pad, FALSE);
2155 collect_pad->track->codec_id = id;
2157 collect_pad->capsfunc = capsfunc;
2158 gst_pad_set_active (GST_PAD (newpad), TRUE);
2159 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2160 goto pad_add_failed;
2164 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2166 return GST_PAD (newpad);
2171 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2172 gst_object_unref (newpad);
2178 * gst_matroska_mux_release_pad:
2179 * @element: #GstMatroskaMux.
2180 * @pad: Pad to release.
2182 * Release a previously requested pad.
2185 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2187 GstMatroskaMux *mux;
2190 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2192 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2193 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2194 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2196 if (cdata->pad == pad) {
2197 GstClockTime min_dur; /* observed minimum duration */
2199 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2200 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2201 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2202 if (collect_pad->duration < min_dur)
2203 collect_pad->duration = min_dur;
2206 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2207 mux->duration < collect_pad->duration)
2208 mux->duration = collect_pad->duration;
2214 gst_collect_pads2_remove_pad (mux->collect, pad);
2215 if (gst_element_remove_pad (element, pad))
2221 * gst_matroska_mux_track_header:
2222 * @mux: #GstMatroskaMux
2223 * @context: Tack context.
2225 * Write a track header.
2228 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2229 GstMatroskaTrackContext * context)
2231 GstEbmlWrite *ebml = mux->ebml_write;
2234 /* TODO: check if everything necessary is written and check default values */
2236 /* track type goes before the type-specific stuff */
2237 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2238 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2240 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2241 gst_matroska_mux_create_uid ());
2242 if (context->default_duration) {
2243 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2244 context->default_duration);
2246 if (context->language) {
2247 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2251 /* FIXME: until we have a nice way of getting the codecname
2252 * out of the caps, I'm not going to enable this. Too much
2253 * (useless, double, boring) work... */
2254 /* TODO: Use value from tags if any */
2255 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2256 context->codec_name); */
2257 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2259 /* type-specific stuff */
2260 switch (context->type) {
2261 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2262 GstMatroskaTrackVideoContext *videocontext =
2263 (GstMatroskaTrackVideoContext *) context;
2265 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2266 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2267 videocontext->pixel_width);
2268 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2269 videocontext->pixel_height);
2270 if (videocontext->display_width && videocontext->display_height) {
2271 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2272 videocontext->display_width);
2273 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2274 videocontext->display_height);
2276 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2277 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2278 if (videocontext->fourcc) {
2279 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2281 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2282 (gpointer) & fcc_le, 4);
2284 gst_ebml_write_master_finish (ebml, master);
2289 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2290 GstMatroskaTrackAudioContext *audiocontext =
2291 (GstMatroskaTrackAudioContext *) context;
2293 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2294 if (audiocontext->samplerate != 8000)
2295 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2296 audiocontext->samplerate);
2297 if (audiocontext->channels != 1)
2298 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2299 audiocontext->channels);
2300 if (audiocontext->bitdepth) {
2301 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2302 audiocontext->bitdepth);
2304 gst_ebml_write_master_finish (ebml, master);
2309 /* this is what we write for now and must be filled
2310 * and remainder void'ed later on */
2311 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2313 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2316 context->pos = ebml->pos;
2317 /* CodecID is mandatory ... */
2318 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2320 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2321 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2322 SUBTITLE_MAX_CODEC_PRIVATE);
2324 /* real data has to be written at finish */
2328 /* doesn't need type-specific data */
2332 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2333 if (context->codec_priv)
2334 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2335 context->codec_priv, context->codec_priv_size);
2339 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2341 guint64 title_master;
2344 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2346 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2347 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2348 GST_MATROSKA_MUX_CHAPLANG);
2350 gst_ebml_write_master_finish (ebml, title_master);
2354 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2355 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2356 guint64 * master_edition)
2358 guint64 uid, master_chapteratom;
2360 GstTocEntry *cur_entry;
2365 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2367 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2369 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2370 /* create uid for the parent */
2371 uid = gst_matroska_mux_create_uid ();
2372 g_free (edition->uid);
2373 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2376 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2378 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2379 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2380 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2381 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2384 uid = gst_matroska_mux_create_uid ();
2385 gst_toc_entry_get_start_stop (entry, &start, &stop);
2387 master_chapteratom =
2388 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2389 g_free (entry->uid);
2390 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2391 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2392 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2393 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2394 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2395 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2397 cur = entry->subentries;
2398 while (cur != NULL) {
2399 cur_entry = cur->data;
2400 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2405 if (G_LIKELY (entry->tags != NULL)) {
2406 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2408 for (i = 0; i < count; ++i) {
2409 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2410 gst_matroska_mux_write_chapter_title (title, ebml);
2414 /* remove title tag */
2415 if (G_LIKELY (count > 0))
2416 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2419 gst_ebml_write_master_finish (ebml, master_chapteratom);
2423 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2424 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2426 guint64 master_edition = 0;
2428 GstTocEntry *subentry;
2430 cur = entry->subentries;
2431 while (cur != NULL) {
2432 subentry = cur->data;
2433 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2439 if (G_LIKELY (master_edition != 0))
2440 gst_ebml_write_master_finish (ebml, master_edition);
2444 * gst_matroska_mux_start:
2445 * @mux: #GstMatroskaMux
2447 * Start a new matroska file (write headers etc...)
2450 gst_matroska_mux_start (GstMatroskaMux * mux)
2452 GstEbmlWrite *ebml = mux->ebml_write;
2453 const gchar *doctype;
2454 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2455 GST_MATROSKA_ID_TRACKS,
2456 GST_MATROSKA_ID_CHAPTERS,
2457 GST_MATROSKA_ID_CUES,
2458 GST_MATROSKA_ID_TAGS,
2461 guint64 master, child;
2465 GstClockTime duration = 0;
2466 guint32 segment_uid[4];
2467 GTimeVal time = { 0, 0 };
2469 /* if not streaming, check if downstream is seekable */
2470 if (!mux->streamable) {
2474 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2475 if (gst_pad_peer_query (mux->srcpad, query)) {
2476 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2477 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2479 /* have to assume seeking is supported if query not handled downstream */
2480 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2484 mux->streamable = TRUE;
2485 g_object_notify (G_OBJECT (mux), "streamable");
2486 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2487 "streamable=false. Will ignore that and create streamable output "
2490 gst_query_unref (query);
2493 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2494 ebml->caps = gst_caps_new_empty_simple ("video/webm");
2496 ebml->caps = gst_caps_new_empty_simple ("video/x-matroska");
2498 /* we start with a EBML header */
2499 doctype = mux->doctype;
2500 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2501 doctype, mux->doctype_version);
2502 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2504 /* the rest of the header is cached */
2505 gst_ebml_write_set_cache (ebml, 0x1000);
2507 /* start a segment */
2509 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2510 mux->segment_master = ebml->pos;
2512 if (!mux->streamable) {
2513 /* seekhead (table of contents) - we set the positions later */
2514 mux->seekhead_pos = ebml->pos;
2515 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2516 for (i = 0; seekhead_id[i] != 0; i++) {
2517 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2518 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2519 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2520 gst_ebml_write_master_finish (ebml, child);
2522 gst_ebml_write_master_finish (ebml, master);
2525 if (mux->streamable) {
2526 const GstTagList *tags;
2529 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2531 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2532 guint64 master_tags, master_tag;
2534 GST_DEBUG_OBJECT (mux, "Writing tags");
2536 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2537 mux->tags_pos = ebml->pos;
2538 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2539 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2540 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2541 gst_ebml_write_master_finish (ebml, master_tag);
2542 gst_ebml_write_master_finish (ebml, master_tags);
2547 mux->info_pos = ebml->pos;
2548 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2549 for (i = 0; i < 4; i++) {
2550 segment_uid[i] = g_random_int ();
2552 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2553 (guint8 *) segment_uid, 16);
2554 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2555 mux->duration_pos = ebml->pos;
2557 if (!mux->streamable) {
2558 for (collected = mux->collect->data; collected;
2559 collected = g_slist_next (collected)) {
2560 GstMatroskaPad *collect_pad;
2562 gint64 trackduration;
2564 collect_pad = (GstMatroskaPad *) collected->data;
2565 thepad = collect_pad->collect.pad;
2567 /* Query the total length of the track. */
2568 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2569 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
2570 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2571 GST_TIME_ARGS (trackduration));
2572 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2573 duration = (GstClockTime) trackduration;
2577 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2578 gst_guint64_to_gdouble (duration) /
2579 gst_guint64_to_gdouble (mux->time_scale));
2581 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2582 "GStreamer plugin version " PACKAGE_VERSION);
2583 if (mux->writing_app && mux->writing_app[0]) {
2584 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2586 g_get_current_time (&time);
2587 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2588 gst_ebml_write_master_finish (ebml, master);
2591 mux->tracks_pos = ebml->pos;
2592 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2594 for (collected = mux->collect->data; collected;
2595 collected = g_slist_next (collected)) {
2596 GstMatroskaPad *collect_pad;
2599 collect_pad = (GstMatroskaPad *) collected->data;
2600 thepad = collect_pad->collect.pad;
2602 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2603 collect_pad->track->codec_id != 0) {
2604 collect_pad->track->num = tracknum++;
2605 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2606 gst_matroska_mux_track_header (mux, collect_pad->track);
2607 gst_ebml_write_master_finish (ebml, child);
2608 /* some remaining pad/track setup */
2609 collect_pad->default_duration_scaled =
2610 gst_util_uint64_scale (collect_pad->track->default_duration,
2611 1, mux->time_scale);
2614 gst_ebml_write_master_finish (ebml, master);
2617 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL && !mux->streamable) {
2618 guint64 master_chapters = 0;
2619 GstTocEntry *toc_entry;
2621 GList *cur, *to_write = NULL;
2624 GST_DEBUG ("Writing chapters");
2626 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2628 /* check whether we have editions or chapters at the root level */
2629 toc_entry = toc->entries->data;
2631 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2632 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2633 gst_toc_entry_set_start_stop (toc_entry, -1, -1);
2635 /* aggregate all chapters without root edition */
2637 while (cur != NULL) {
2638 toc_entry->subentries =
2639 g_list_prepend (toc_entry->subentries, cur->data);
2643 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2644 subentries->data), &start, NULL);
2645 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2646 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2647 subentries->data), NULL, &stop);
2648 gst_toc_entry_set_start_stop (toc_entry, start, stop);
2650 to_write = g_list_append (to_write, toc_entry);
2653 to_write = toc->entries;
2656 /* finally write chapters */
2657 mux->chapters_pos = ebml->pos;
2660 while (cur != NULL) {
2661 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2666 /* close master element if any edition was written */
2667 if (G_LIKELY (master_chapters != 0))
2668 gst_ebml_write_master_finish (ebml, master_chapters);
2670 if (toc_entry != NULL) {
2671 g_list_free (toc_entry->subentries);
2672 toc_entry->subentries = NULL;
2673 gst_toc_entry_free (toc_entry);
2674 g_list_free (to_write);
2678 /* lastly, flush the cache */
2679 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2683 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2686 /* TODO: more sensible tag mappings */
2689 const gchar *matroska_tagname;
2690 const gchar *gstreamer_tagname;
2694 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2695 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2696 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2697 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2698 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2699 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2700 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2701 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2702 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2703 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2704 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2705 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2706 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2707 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2708 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2710 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2712 guint64 simpletag_master;
2714 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2715 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2716 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2718 if (strcmp (tagname_gst, tag) == 0) {
2719 GValue src = { 0, };
2722 if (!gst_tag_list_copy_value (&src, list, tag))
2724 if ((dest = gst_value_serialize (&src))) {
2726 simpletag_master = gst_ebml_write_master_start (ebml,
2727 GST_MATROSKA_ID_SIMPLETAG);
2728 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2729 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2730 gst_ebml_write_master_finish (ebml, simpletag_master);
2733 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2735 g_value_unset (&src);
2742 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2743 const GstTocEntry * entry, guint64 * master_tags)
2745 guint64 master_tag, master_targets;
2749 ebml = mux->ebml_write;
2751 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2752 if (*master_tags == 0) {
2753 mux->tags_pos = ebml->pos;
2754 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2757 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2759 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2761 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2762 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2763 g_ascii_strtoull (entry->uid, NULL, 10));
2765 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2766 g_ascii_strtoull (entry->uid, NULL, 10));
2768 gst_ebml_write_master_finish (ebml, master_targets);
2769 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2770 gst_ebml_write_master_finish (ebml, master_tag);
2773 cur = entry->subentries;
2774 while (cur != NULL) {
2775 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2781 * gst_matroska_mux_finish:
2782 * @mux: #GstMatroskaMux
2784 * Finish a new matroska file (write index etc...)
2787 gst_matroska_mux_finish (GstMatroskaMux * mux)
2789 GstEbmlWrite *ebml = mux->ebml_write;
2791 guint64 duration = 0;
2793 const GstTagList *tags;
2795 /* finish last cluster */
2797 gst_ebml_write_master_finish (ebml, mux->cluster);
2801 if (mux->index != NULL) {
2803 guint64 master, pointentry_master, trackpos_master;
2805 mux->cues_pos = ebml->pos;
2806 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2807 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2809 for (n = 0; n < mux->num_indexes; n++) {
2810 GstMatroskaIndex *idx = &mux->index[n];
2812 pointentry_master = gst_ebml_write_master_start (ebml,
2813 GST_MATROSKA_ID_POINTENTRY);
2814 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2815 idx->time / mux->time_scale);
2816 trackpos_master = gst_ebml_write_master_start (ebml,
2817 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2818 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2819 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2820 idx->pos - mux->segment_master);
2821 gst_ebml_write_master_finish (ebml, trackpos_master);
2822 gst_ebml_write_master_finish (ebml, pointentry_master);
2825 gst_ebml_write_master_finish (ebml, master);
2826 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2830 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2832 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2833 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2834 guint64 master_tags = 0, master_tag;
2838 GST_DEBUG_OBJECT (mux, "Writing tags");
2840 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2843 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2844 mux->tags_pos = ebml->pos;
2845 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2846 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2849 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2851 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2854 gst_ebml_write_master_finish (ebml, master_tag);
2859 while (cur != NULL) {
2860 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2865 if (master_tags != 0)
2866 gst_ebml_write_master_finish (ebml, master_tags);
2869 /* update seekhead. We know that:
2870 * - a seekhead contains 5 entries.
2871 * - order of entries is as above.
2872 * - a seekhead has a 4-byte header + 8-byte length
2873 * - each entry is 2-byte master, 2-byte ID pointer,
2874 * 2-byte length pointer, all 8/1-byte length, 4-
2875 * byte ID and 8-byte length pointer, where the
2876 * length pointer starts at 20.
2877 * - all entries are local to the segment (so pos - segment_master).
2878 * - so each entry is at 12 + 20 + num * 28. */
2879 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2880 mux->info_pos - mux->segment_master);
2881 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2882 mux->tracks_pos - mux->segment_master);
2883 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2884 && mux->chapters_pos > 0) {
2885 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2886 mux->chapters_pos - mux->segment_master);
2889 guint64 my_pos = ebml->pos;
2891 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2892 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2893 gst_ebml_write_seek (ebml, my_pos);
2895 if (mux->index != NULL) {
2896 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2897 mux->cues_pos - mux->segment_master);
2900 guint64 my_pos = ebml->pos;
2902 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2903 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2904 gst_ebml_write_seek (ebml, my_pos);
2908 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
2909 mux->tags_pos - mux->segment_master);
2912 guint64 my_pos = ebml->pos;
2914 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
2915 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2916 gst_ebml_write_seek (ebml, my_pos);
2920 * - first get the overall duration
2921 * (a released track may have left a duration in here)
2922 * - write some track header data for subtitles
2924 duration = mux->duration;
2926 for (collected = mux->collect->data; collected;
2927 collected = g_slist_next (collected)) {
2928 GstMatroskaPad *collect_pad;
2929 GstClockTime min_duration; /* observed minimum duration */
2930 GstMatroskaTrackContext *context;
2931 gint voidleft = 0, fill = 0;
2934 collect_pad = (GstMatroskaPad *) collected->data;
2935 context = collect_pad->track;
2937 GST_DEBUG_OBJECT (mux,
2938 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2939 " end ts %" GST_TIME_FORMAT, collect_pad,
2940 GST_TIME_ARGS (collect_pad->start_ts),
2941 GST_TIME_ARGS (collect_pad->end_ts));
2943 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2944 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2946 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2947 if (collect_pad->duration < min_duration)
2948 collect_pad->duration = min_duration;
2949 GST_DEBUG_OBJECT (collect_pad,
2950 "final track duration: %" GST_TIME_FORMAT,
2951 GST_TIME_ARGS (collect_pad->duration));
2954 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2955 duration < collect_pad->duration)
2956 duration = collect_pad->duration;
2958 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2962 /* write subtitle type and possible private data */
2963 gst_ebml_write_seek (ebml, context->pos);
2964 /* complex way to write ascii to account for extra filling */
2965 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2966 strcpy (codec_id, context->codec_id);
2967 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2968 codec_id, strlen (context->codec_id) + 1 + fill);
2970 if (context->codec_priv)
2971 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2972 context->codec_priv, context->codec_priv_size);
2973 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2974 /* void'ify; sigh, variable sized length field */
2975 if (voidleft == 1) {
2978 } else if (voidleft && voidleft <= 128)
2979 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2980 else if (voidleft >= 130)
2981 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2982 else if (voidleft == 129) {
2983 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2984 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2988 /* seek back (optional, but do anyway) */
2989 gst_ebml_write_seek (ebml, pos);
2991 /* update duration */
2992 if (duration != 0) {
2993 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2994 GST_TIME_ARGS (duration));
2995 pos = mux->ebml_write->pos;
2996 gst_ebml_write_seek (ebml, mux->duration_pos);
2997 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2998 gst_guint64_to_gdouble (duration) /
2999 gst_guint64_to_gdouble (mux->time_scale));
3000 gst_ebml_write_seek (ebml, pos);
3003 guint64 my_pos = ebml->pos;
3005 gst_ebml_write_seek (ebml, mux->duration_pos);
3006 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3007 gst_ebml_write_seek (ebml, my_pos);
3009 GST_DEBUG_OBJECT (mux, "finishing segment");
3010 /* finish segment - this also writes element length */
3011 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3015 * gst_matroska_mux_buffer_header:
3016 * @track: Track context.
3017 * @relative_timestamp: relative timestamp of the buffer
3018 * @flags: Buffer flags.
3020 * Create a buffer containing buffer header.
3022 * Returns: New buffer.
3025 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3026 gint16 relative_timestamp, int flags)
3029 guint8 *data = g_malloc (4);
3031 hdr = gst_buffer_new_wrapped (data, 4);
3032 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3033 data[0] = track->num | 0x80;
3034 /* time relative to clustertime */
3035 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3043 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3044 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3045 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3048 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3049 GstMatroskaPad * collect_pad, GstBuffer * buf)
3051 GstMatroskaTrackVideoContext *ctx =
3052 (GstMatroskaTrackVideoContext *) collect_pad->track;
3057 guint32 next_parse_offset;
3058 GstBuffer *ret = NULL;
3059 gboolean is_muxing_unit = FALSE;
3061 gst_buffer_map (buf, &map, GST_MAP_READ);
3066 gst_buffer_unmap (buf, &map);
3067 gst_buffer_unref (buf);
3071 /* Check if this buffer contains a picture or end-of-sequence packet */
3072 while (size >= 13) {
3073 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3074 gst_buffer_unmap (buf, &map);
3075 gst_buffer_unref (buf);
3079 parse_code = GST_READ_UINT8 (data + 4);
3080 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3081 if (ctx->dirac_unit) {
3082 gst_buffer_unref (ctx->dirac_unit);
3083 ctx->dirac_unit = NULL;
3085 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3086 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3087 is_muxing_unit = TRUE;
3091 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3093 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3096 data += next_parse_offset;
3097 size -= next_parse_offset;
3100 if (ctx->dirac_unit)
3101 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3103 ctx->dirac_unit = gst_buffer_ref (buf);
3105 gst_buffer_unmap (buf, &map);
3107 if (is_muxing_unit) {
3108 ret = gst_buffer_make_writable (ctx->dirac_unit);
3109 ctx->dirac_unit = NULL;
3110 gst_buffer_copy_into (ret, buf,
3111 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3112 gst_buffer_unref (buf);
3114 gst_buffer_unref (buf);
3122 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3126 GValue streamheader = { 0 };
3127 GValue bufval = { 0 };
3128 GstBuffer *streamheader_buffer;
3129 GstEbmlWrite *ebml = mux->ebml_write;
3131 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3132 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
3133 caps = gst_caps_new_empty_simple ("video/webm");
3135 caps = gst_caps_new_empty_simple ("video/x-matroska");
3137 s = gst_caps_get_structure (caps, 0);
3138 g_value_init (&streamheader, GST_TYPE_ARRAY);
3139 g_value_init (&bufval, GST_TYPE_BUFFER);
3140 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3141 gst_value_set_buffer (&bufval, streamheader_buffer);
3142 gst_value_array_append_value (&streamheader, &bufval);
3143 g_value_unset (&bufval);
3144 gst_structure_set_value (s, "streamheader", &streamheader);
3145 g_value_unset (&streamheader);
3146 gst_caps_replace (&ebml->caps, caps);
3147 gst_buffer_unref (streamheader_buffer);
3148 gst_caps_unref (caps);
3152 * gst_matroska_mux_write_data:
3153 * @mux: #GstMatroskaMux
3154 * @collect_pad: #GstMatroskaPad with the data
3156 * Write collected data (called from gst_matroska_mux_collected).
3158 * Returns: Result of the gst_pad_push issued to write the data.
3160 static GstFlowReturn
3161 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3164 GstEbmlWrite *ebml = mux->ebml_write;
3167 gboolean write_duration;
3168 gint16 relative_timestamp;
3169 gint64 relative_timestamp64;
3170 guint64 block_duration;
3171 gboolean is_video_keyframe = FALSE;
3172 GstMatroskamuxPad *pad;
3175 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3177 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3178 if (collect_pad->track->xiph_headers_to_skip > 0) {
3179 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3180 gst_buffer_unref (buf);
3181 --collect_pad->track->xiph_headers_to_skip;
3185 /* for dirac we have to queue up everything up to a picture unit */
3186 if (collect_pad->track->codec_id != NULL &&
3187 strcmp (collect_pad->track->codec_id,
3188 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3189 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3194 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3195 * this would wreak havoc with time stored in matroska file */
3196 /* TODO: maybe calculate a timestamp by using the previous timestamp
3197 * and default duration */
3198 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3199 GST_WARNING_OBJECT (collect_pad->collect.pad,
3200 "Invalid buffer timestamp; dropping buffer");
3201 gst_buffer_unref (buf);
3205 /* set the timestamp for outgoing buffers */
3206 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3208 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
3209 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3210 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3211 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3212 is_video_keyframe = TRUE;
3216 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3217 * or when we may be reaching the limit of the relative timestamp */
3218 if (mux->cluster_time +
3219 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3220 || is_video_keyframe || mux->force_key_unit_event) {
3221 if (!mux->streamable)
3222 gst_ebml_write_master_finish (ebml, mux->cluster);
3224 /* Forward the GstForceKeyUnit event after finishing the cluster */
3225 if (mux->force_key_unit_event) {
3226 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3227 mux->force_key_unit_event = NULL;
3230 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3231 mux->cluster_pos = ebml->pos;
3232 gst_ebml_write_set_cache (ebml, 0x20);
3234 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3235 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3236 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3238 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3239 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3241 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3242 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3243 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3244 mux->prev_cluster_size);
3249 mux->cluster_pos = ebml->pos;
3250 gst_ebml_write_set_cache (ebml, 0x20);
3251 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3252 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3253 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3254 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3255 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3258 /* update duration of this track */
3259 if (GST_BUFFER_DURATION_IS_VALID (buf))
3260 collect_pad->duration += GST_BUFFER_DURATION (buf);
3262 /* We currently write index entries for all video tracks or for the audio
3263 * track in a single-track audio file. This could be improved by keeping the
3264 * index only for the *first* video track. */
3266 /* TODO: index is useful for every track, should contain the number of
3267 * the block in the cluster which contains the timestamp, should also work
3268 * for files with multiple audio tracks.
3270 if (!mux->streamable &&
3271 (is_video_keyframe ||
3272 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3273 (mux->num_streams == 1)))) {
3276 if (mux->min_index_interval != 0) {
3277 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3278 if (mux->index[last_idx].track == collect_pad->track->num)
3283 if (last_idx < 0 || mux->min_index_interval == 0 ||
3284 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3285 >= mux->min_index_interval)) {
3286 GstMatroskaIndex *idx;
3288 if (mux->num_indexes % 32 == 0) {
3289 mux->index = g_renew (GstMatroskaIndex, mux->index,
3290 mux->num_indexes + 32);
3292 idx = &mux->index[mux->num_indexes++];
3294 idx->pos = mux->cluster_pos;
3295 idx->time = GST_BUFFER_TIMESTAMP (buf);
3296 idx->track = collect_pad->track->num;
3300 /* Check if the duration differs from the default duration. */
3301 write_duration = FALSE;
3303 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3304 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3305 1, mux->time_scale);
3307 /* small difference should be ok. */
3308 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3309 block_duration < collect_pad->default_duration_scaled - 1) {
3310 write_duration = TRUE;
3314 /* write the block, for doctype v2 use SimpleBlock if possible
3315 * one slice (*breath*).
3316 * FIXME: Need to do correct lacing! */
3317 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3318 if (relative_timestamp64 >= 0) {
3319 /* round the timestamp */
3320 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3322 /* round the timestamp */
3323 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3325 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3327 if (mux->doctype_version > 1 && !write_duration) {
3329 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3332 gst_matroska_mux_create_buffer_header (collect_pad->track,
3333 relative_timestamp, flags);
3334 gst_ebml_write_set_cache (ebml, 0x40);
3335 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3336 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3337 gst_ebml_write_buffer (ebml, hdr);
3338 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3339 gst_ebml_write_buffer (ebml, buf);
3341 return gst_ebml_last_write_result (ebml);
3343 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
3344 /* write and call order slightly unnatural,
3345 * but avoids seek and minizes pushing */
3346 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3348 gst_matroska_mux_create_buffer_header (collect_pad->track,
3349 relative_timestamp, 0);
3351 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3352 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3353 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3354 gst_ebml_write_buffer (ebml, hdr);
3355 gst_ebml_write_master_finish_full (ebml, blockgroup,
3356 gst_buffer_get_size (buf));
3357 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3358 gst_ebml_write_buffer (ebml, buf);
3360 return gst_ebml_last_write_result (ebml);
3365 * gst_matroska_mux_handle_buffer:
3366 * @pads: #GstCollectPads2
3367 * @uuser_data: #GstMatroskaMux
3369 * Collectpads callback.
3371 * Returns: #GstFlowReturn
3373 static GstFlowReturn
3374 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3375 GstBuffer * buf, gpointer user_data)
3377 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3378 GstEbmlWrite *ebml = mux->ebml_write;
3379 GstMatroskaPad *best;
3380 GstFlowReturn ret = GST_FLOW_OK;
3382 GST_DEBUG_OBJECT (mux, "Collected pads");
3384 /* start with a header */
3385 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3386 if (mux->collect->data == NULL) {
3387 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3388 ("No input streams configured"));
3389 return GST_FLOW_ERROR;
3391 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3392 gst_ebml_start_streamheader (ebml);
3393 gst_matroska_mux_start (mux);
3394 gst_matroska_mux_stop_streamheader (mux);
3395 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3398 /* provided with stream to write from */
3399 best = (GstMatroskaPad *) data;
3401 /* if there is no best pad, we have reached EOS */
3403 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3404 if (!mux->streamable) {
3405 gst_matroska_mux_finish (mux);
3407 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3409 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3414 /* if we have a best stream, should also have a buffer */
3417 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3418 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3419 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3420 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3422 /* make note of first and last encountered timestamps, so we can calculate
3423 * the actual duration later when we send an updated header on eos */
3424 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3425 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3426 GstClockTime end_ts = start_ts;
3428 if (GST_BUFFER_DURATION_IS_VALID (buf))
3429 end_ts += GST_BUFFER_DURATION (buf);
3430 else if (best->track->default_duration)
3431 end_ts += best->track->default_duration;
3433 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3434 best->end_ts = end_ts;
3436 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3437 start_ts < best->start_ts))
3438 best->start_ts = start_ts;
3441 /* write one buffer */
3442 ret = gst_matroska_mux_write_data (mux, best, buf);
3450 * gst_matroska_mux_change_state:
3451 * @element: #GstMatroskaMux
3452 * @transition: State change transition.
3454 * Change the muxer state.
3456 * Returns: #GstStateChangeReturn
3458 static GstStateChangeReturn
3459 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3461 GstStateChangeReturn ret;
3462 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3464 switch (transition) {
3465 case GST_STATE_CHANGE_NULL_TO_READY:
3467 case GST_STATE_CHANGE_READY_TO_PAUSED:
3468 gst_collect_pads2_start (mux->collect);
3470 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3472 case GST_STATE_CHANGE_PAUSED_TO_READY:
3473 gst_collect_pads2_stop (mux->collect);
3479 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3481 switch (transition) {
3482 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3484 case GST_STATE_CHANGE_PAUSED_TO_READY:
3485 gst_matroska_mux_reset (GST_ELEMENT (mux));
3487 case GST_STATE_CHANGE_READY_TO_NULL:
3497 gst_matroska_mux_set_property (GObject * object,
3498 guint prop_id, const GValue * value, GParamSpec * pspec)
3500 GstMatroskaMux *mux;
3502 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3503 mux = GST_MATROSKA_MUX (object);
3506 case ARG_WRITING_APP:
3507 if (!g_value_get_string (value)) {
3508 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3511 g_free (mux->writing_app);
3512 mux->writing_app = g_value_dup_string (value);
3514 case ARG_DOCTYPE_VERSION:
3515 mux->doctype_version = g_value_get_int (value);
3517 case ARG_MIN_INDEX_INTERVAL:
3518 mux->min_index_interval = g_value_get_int64 (value);
3520 case ARG_STREAMABLE:
3521 mux->streamable = g_value_get_boolean (value);
3524 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3530 gst_matroska_mux_get_property (GObject * object,
3531 guint prop_id, GValue * value, GParamSpec * pspec)
3533 GstMatroskaMux *mux;
3535 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3536 mux = GST_MATROSKA_MUX (object);
3539 case ARG_WRITING_APP:
3540 g_value_set_string (value, mux->writing_app);
3542 case ARG_DOCTYPE_VERSION:
3543 g_value_set_int (value, mux->doctype_version);
3545 case ARG_MIN_INDEX_INTERVAL:
3546 g_value_set_int64 (value, mux->min_index_interval);
3548 case ARG_STREAMABLE:
3549 g_value_set_boolean (value, mux->streamable);
3552 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);