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/riff/riff-media.h>
54 #include <gst/tag/tag.h>
56 #include "matroska-mux.h"
57 #include "matroska-ids.h"
59 #define GST_MATROSKA_MUX_CHAPLANG "und"
61 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
62 #define GST_CAT_DEFAULT matroskamux_debug
69 ARG_MIN_INDEX_INTERVAL,
73 #define DEFAULT_DOCTYPE_VERSION 2
74 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
75 #define DEFAULT_MIN_INDEX_INTERVAL 0
76 #define DEFAULT_STREAMABLE FALSE
78 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
79 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
81 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
84 GST_STATIC_CAPS ("video/x-matroska")
87 #define COMMON_VIDEO_CAPS \
88 "width = (int) [ 16, 4096 ], " \
89 "height = (int) [ 16, 4096 ], " \
90 "framerate = (fraction) [ 0, MAX ]"
92 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
93 "width = (int) [ 16, 4096 ], " \
94 "height = (int) [ 16, 4096 ] "
97 * * require codec data, etc as needed
100 static GstStaticPadTemplate videosink_templ =
101 GST_STATIC_PAD_TEMPLATE ("video_%d",
104 GST_STATIC_CAPS ("video/mpeg, "
105 "mpegversion = (int) { 1, 2, 4 }, "
106 "systemstream = (boolean) false, "
107 COMMON_VIDEO_CAPS "; "
108 "video/x-h264, stream-format=avc, alignment=au, "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS "; "
119 COMMON_VIDEO_CAPS "; "
121 COMMON_VIDEO_CAPS "; "
123 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
126 COMMON_VIDEO_CAPS "; "
127 "video/x-pn-realvideo, "
128 "rmversion = (int) [1, 4], "
129 COMMON_VIDEO_CAPS "; "
131 COMMON_VIDEO_CAPS "; "
133 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
134 COMMON_VIDEO_CAPS "; "
135 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
138 #define COMMON_AUDIO_CAPS \
139 "channels = (int) [ 1, MAX ], " \
140 "rate = (int) [ 1, MAX ]"
143 * * require codec data, etc as needed
145 static GstStaticPadTemplate audiosink_templ =
146 GST_STATIC_PAD_TEMPLATE ("audio_%d",
149 GST_STATIC_CAPS ("audio/mpeg, "
150 "mpegversion = (int) 1, "
151 "layer = (int) [ 1, 3 ], "
152 COMMON_AUDIO_CAPS "; "
154 "mpegversion = (int) { 2, 4 }, "
155 "stream-format = (string) raw, "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
160 COMMON_AUDIO_CAPS "; "
162 COMMON_AUDIO_CAPS "; "
164 COMMON_AUDIO_CAPS "; "
166 COMMON_AUDIO_CAPS "; "
168 COMMON_AUDIO_CAPS "; "
172 "signed = (boolean) false, "
173 COMMON_AUDIO_CAPS ";"
177 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
178 "signed = (boolean) true, "
179 COMMON_AUDIO_CAPS ";"
183 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
184 "signed = (boolean) true, "
185 COMMON_AUDIO_CAPS ";"
189 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
190 "signed = (boolean) true, "
191 COMMON_AUDIO_CAPS ";"
192 "audio/x-raw-float, "
193 "width = (int) [ 32, 64 ], "
194 "endianness = (int) LITTLE_ENDIAN, "
195 COMMON_AUDIO_CAPS ";"
197 "width = (int) { 8, 16, 24 }, "
198 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
199 "audio/x-pn-realaudio, "
200 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
201 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
202 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
203 COMMON_AUDIO_CAPS ";"
205 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
207 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
210 static GstStaticPadTemplate subtitlesink_templ =
211 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
214 GST_STATIC_CAPS ("subtitle/x-kate; "
215 "text/plain; application/x-ssa; application/x-ass; "
216 "application/x-usf; video/x-dvd-subpicture; "
217 "application/x-subtitle-unknown")
220 static GArray *used_uids;
221 G_LOCK_DEFINE_STATIC (used_uids);
223 static void gst_matroska_mux_add_interfaces (GType type);
225 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
226 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
228 /* Matroska muxer destructor */
229 static void gst_matroska_mux_finalize (GObject * object);
231 /* Pads collected callback */
232 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
233 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
234 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
235 GstCollectData2 * data, GstEvent * event, gpointer user_data);
238 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
240 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
241 GstPadTemplate * templ, const gchar * name);
242 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
244 /* gst internal change state handler */
245 static GstStateChangeReturn
246 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
248 /* gobject bla bla */
249 static void gst_matroska_mux_set_property (GObject * object,
250 guint prop_id, const GValue * value, GParamSpec * pspec);
251 static void gst_matroska_mux_get_property (GObject * object,
252 guint prop_id, GValue * value, GParamSpec * pspec);
255 static void gst_matroska_mux_reset (GstElement * element);
258 static guint64 gst_matroska_mux_create_uid ();
260 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
261 GstMatroskaTrackContext * context);
262 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
263 GstMatroskaTrackContext * context);
264 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
265 GstMatroskaTrackContext * context);
266 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
267 GstMatroskaTrackContext * context);
268 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
269 GstMatroskaTrackContext * context);
271 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
275 gst_matroska_mux_add_interfaces (GType type)
277 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
278 static const GInterfaceInfo toc_setter_info = { NULL, NULL, NULL };
280 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
281 g_type_add_interface_static (type, GST_TYPE_TOC_SETTER, &toc_setter_info);
285 gst_matroska_mux_base_init (gpointer g_class)
290 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
292 GObjectClass *gobject_class;
293 GstElementClass *gstelement_class;
295 gobject_class = (GObjectClass *) klass;
296 gstelement_class = (GstElementClass *) klass;
298 gst_element_class_add_static_pad_template (gstelement_class,
300 gst_element_class_add_static_pad_template (gstelement_class,
302 gst_element_class_add_static_pad_template (gstelement_class,
303 &subtitlesink_templ);
304 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
305 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
307 "Muxes video/audio/subtitle streams into a matroska stream",
308 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
310 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
313 gobject_class->finalize = gst_matroska_mux_finalize;
315 gobject_class->get_property = gst_matroska_mux_get_property;
316 gobject_class->set_property = gst_matroska_mux_set_property;
318 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
319 g_param_spec_string ("writing-app", "Writing application.",
320 "The name the application that creates the matroska file.",
321 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
322 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
323 g_param_spec_int ("version", "DocType version",
324 "This parameter determines what Matroska features can be used.",
325 1, 2, DEFAULT_DOCTYPE_VERSION,
326 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
327 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
328 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
329 "entries", "An index entry is created every so many nanoseconds.",
330 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
331 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
332 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
333 g_param_spec_boolean ("streamable", "Determines whether output should "
334 "be streamable", "If set to true, the output should be as if it is "
335 "to be streamed and hence no indexes written or duration written.",
337 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
339 gstelement_class->change_state =
340 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
341 gstelement_class->request_new_pad =
342 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
343 gstelement_class->release_pad =
344 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
348 * Start of pad option handler code
350 #define DEFAULT_PAD_FRAME_DURATION TRUE
351 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
356 PROP_PAD_FRAME_DURATION
362 gboolean frame_duration;
363 gboolean frame_duration_user;
366 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
369 gst_matroskamux_pad_get_type (void)
371 static GType type = 0;
373 if (G_UNLIKELY (type == 0)) {
374 type = g_type_register_static_simple (GST_TYPE_PAD,
375 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
376 (GClassInitFunc) gst_matroskamux_pad_class_init,
377 sizeof (GstMatroskamuxPad), NULL, 0);
382 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
383 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
384 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
385 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
388 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
389 GValue * value, GParamSpec * pspec)
391 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
394 case PROP_PAD_FRAME_DURATION:
395 g_value_set_boolean (value, pad->frame_duration);
398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
405 const GValue * value, GParamSpec * pspec)
407 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
410 case PROP_PAD_FRAME_DURATION:
411 pad->frame_duration = g_value_get_boolean (value);
412 pad->frame_duration_user = TRUE;
415 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
421 gst_matroskamux_pad_class_init (GstPadClass * klass)
423 GObjectClass *gobject_class = (GObjectClass *) klass;
425 gobject_class->set_property = gst_matroskamux_pad_set_property;
426 gobject_class->get_property = gst_matroskamux_pad_get_property;
428 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
429 g_param_spec_boolean ("frame-duration", "Frame duration",
430 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
431 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
435 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
437 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
438 pad->frame_duration_user = FALSE;
442 * End of pad option handler code
446 * gst_matroska_mux_init:
447 * @mux: #GstMatroskaMux that should be initialized.
448 * @g_class: Class of the muxer.
450 * Matroska muxer constructor.
453 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
455 GstPadTemplate *templ;
458 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
459 mux->srcpad = gst_pad_new_from_template (templ, "src");
461 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
462 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
464 mux->collect = gst_collect_pads2_new ();
465 gst_collect_pads2_set_clip_function (mux->collect,
466 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
467 gst_collect_pads2_set_buffer_function (mux->collect,
468 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
469 gst_collect_pads2_set_event_function (mux->collect,
470 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
472 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
473 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
475 /* property defaults */
476 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
477 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
478 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
479 mux->streamable = DEFAULT_STREAMABLE;
481 /* initialize internal variables */
483 mux->num_streams = 0;
484 mux->num_a_streams = 0;
485 mux->num_t_streams = 0;
486 mux->num_v_streams = 0;
488 /* initialize remaining variables */
489 gst_matroska_mux_reset (GST_ELEMENT (mux));
494 * gst_matroska_mux_finalize:
495 * @object: #GstMatroskaMux that should be finalized.
497 * Finalize matroska muxer.
500 gst_matroska_mux_finalize (GObject * object)
502 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
504 gst_event_replace (&mux->force_key_unit_event, NULL);
506 gst_object_unref (mux->collect);
507 gst_object_unref (mux->ebml_write);
508 if (mux->writing_app)
509 g_free (mux->writing_app);
511 G_OBJECT_CLASS (parent_class)->finalize (object);
516 * gst_matroska_mux_create_uid:
518 * Generate new unused track UID.
520 * Returns: New track UID.
523 gst_matroska_mux_create_uid (void)
530 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
535 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
536 for (i = 0; i < used_uids->len; i++) {
537 if (g_array_index (used_uids, guint64, i) == uid) {
542 g_array_append_val (used_uids, uid);
545 G_UNLOCK (used_uids);
551 * gst_matroska_pad_reset:
552 * @collect_pad: the #GstMatroskaPad
554 * Reset and/or release resources of a matroska collect pad.
557 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
560 GstMatroskaTrackType type = 0;
562 /* free track information */
563 if (collect_pad->track != NULL) {
564 /* retrieve for optional later use */
565 name = collect_pad->track->name;
566 type = collect_pad->track->type;
567 /* extra for video */
568 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
569 GstMatroskaTrackVideoContext *ctx =
570 (GstMatroskaTrackVideoContext *) collect_pad->track;
572 if (ctx->dirac_unit) {
573 gst_buffer_unref (ctx->dirac_unit);
574 ctx->dirac_unit = NULL;
577 g_free (collect_pad->track->codec_id);
578 g_free (collect_pad->track->codec_name);
580 g_free (collect_pad->track->name);
581 g_free (collect_pad->track->language);
582 g_free (collect_pad->track->codec_priv);
583 g_free (collect_pad->track);
584 collect_pad->track = NULL;
587 if (!full && type != 0) {
588 GstMatroskaTrackContext *context;
590 /* create a fresh context */
592 case GST_MATROSKA_TRACK_TYPE_VIDEO:
593 context = (GstMatroskaTrackContext *)
594 g_new0 (GstMatroskaTrackVideoContext, 1);
596 case GST_MATROSKA_TRACK_TYPE_AUDIO:
597 context = (GstMatroskaTrackContext *)
598 g_new0 (GstMatroskaTrackAudioContext, 1);
600 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
601 context = (GstMatroskaTrackContext *)
602 g_new0 (GstMatroskaTrackSubtitleContext, 1);
605 g_assert_not_reached ();
609 context->type = type;
610 context->name = name;
611 /* TODO: check default values for the context */
612 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
613 collect_pad->track = context;
614 collect_pad->duration = 0;
615 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
616 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
621 * gst_matroska_pad_free:
622 * @collect_pad: the #GstMatroskaPad
624 * Release resources of a matroska collect pad.
627 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
629 gst_matroska_pad_reset (collect_pad, TRUE);
634 * gst_matroska_mux_reset:
635 * @element: #GstMatroskaMux that should be reseted.
637 * Reset matroska muxer back to initial state.
640 gst_matroska_mux_reset (GstElement * element)
642 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
645 /* reset EBML write */
646 gst_ebml_write_reset (mux->ebml_write);
649 mux->state = GST_MATROSKA_MUX_STATE_START;
651 /* clean up existing streams */
653 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
654 GstMatroskaPad *collect_pad;
656 collect_pad = (GstMatroskaPad *) walk->data;
658 /* reset collect pad to pristine state */
659 gst_matroska_pad_reset (collect_pad, FALSE);
663 mux->num_indexes = 0;
668 mux->time_scale = GST_MSECOND;
669 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
674 mux->cluster_time = 0;
675 mux->cluster_pos = 0;
676 mux->prev_cluster_size = 0;
679 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
684 gst_toc_setter_reset_toc (GST_TOC_SETTER (mux));
686 mux->chapters_pos = 0;
690 * gst_matroska_mux_handle_src_event:
691 * @pad: Pad which received the event.
692 * @event: Received event.
694 * handle events - copied from oggmux without understanding
696 * Returns: #TRUE on success.
699 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
703 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
707 /* disable seeking for now */
713 return gst_pad_event_default (pad, event);
718 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
720 if (context->codec_priv != NULL) {
721 g_free (context->codec_priv);
722 context->codec_priv = NULL;
723 context->codec_priv_size = 0;
728 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
738 /* produce comma-separated list in hex format */
739 for (i = 0; i < 16; ++i) {
741 /* replicate vobsub's slightly off RGB conversion calculation */
742 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
743 u = ((col >> 8) & 0xff) - 128;
744 v = (col & 0xff) - 128;
745 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
746 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
747 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
748 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
751 sclut = g_strjoinv (",", clutv);
753 /* build codec private; only palette for now */
754 gst_matroska_mux_free_codec_priv (context);
755 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
756 /* include terminating 0 */
757 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
759 for (i = 0; i < 16; ++i) {
766 * gst_matroska_mux_handle_sink_event:
767 * @pad: Pad which received the event.
768 * @event: Received event.
770 * handle events - informational ones like tags
772 * Returns: #TRUE on success.
775 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
776 GstCollectData2 * data, GstEvent * event, gpointer user_data)
778 GstMatroskaTrackContext *context;
779 GstMatroskaPad *collect_pad;
784 mux = GST_MATROSKA_MUX (user_data);
785 collect_pad = (GstMatroskaPad *) data;
787 context = collect_pad->track;
790 switch (GST_EVENT_TYPE (event)) {
794 GST_DEBUG_OBJECT (mux, "received tag event");
795 gst_event_parse_tag (event, &list);
797 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
798 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
799 const gchar *lang_code;
801 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
803 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
804 context->language = g_strdup (lang_code);
806 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
811 /* FIXME: what about stream-specific tags? */
812 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
813 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
815 gst_event_unref (event);
816 /* handled this, don't want collectpads to forward it downstream */
823 if (mux->chapters_pos > 0)
826 GST_DEBUG_OBJECT (mux, "received toc event");
827 gst_event_parse_toc (event, &toc, NULL);
830 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
831 gst_toc_setter_reset_toc (GST_TOC_SETTER (mux));
832 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
835 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
839 gst_event_unref (event);
840 /* handled this, don't want collectpads to forward it downstream */
844 case GST_EVENT_NEWSEGMENT:{
847 gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
849 if (format != GST_FORMAT_TIME) {
850 gst_event_unref (event);
855 case GST_EVENT_CUSTOM_DOWNSTREAM:{
856 const GstStructure *structure;
858 structure = gst_event_get_structure (event);
859 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
860 gst_event_replace (&mux->force_key_unit_event, NULL);
861 mux->force_key_unit_event = event;
863 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
864 !strcmp ("dvd-spu-clut-change",
865 gst_structure_get_string (structure, "event"))) {
870 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
871 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
872 GST_DEBUG_OBJECT (pad, "... discarding");
875 /* first transform event data into table form */
876 for (i = 0; i < 16; i++) {
877 g_snprintf (name, sizeof (name), "clut%02d", i);
878 if (!gst_structure_get_int (structure, name, &value)) {
879 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
880 "contain %s field", name);
886 /* transform into private data for stream; text form */
887 gst_matroska_mux_build_vobsub_private (context, clut);
895 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
903 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
906 g_assert (context && id);
907 if (context->codec_id)
908 g_free (context->codec_id);
909 context->codec_id = g_strdup (id);
913 * gst_matroska_mux_video_pad_setcaps:
914 * @pad: Pad which got the caps.
917 * Setcaps function for video sink pad.
919 * Returns: #TRUE on success.
922 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
924 GstMatroskaTrackContext *context = NULL;
925 GstMatroskaTrackVideoContext *videocontext;
927 GstMatroskaPad *collect_pad;
928 GstStructure *structure;
929 const gchar *mimetype;
930 const GValue *value = NULL;
931 const GstBuffer *codec_buf = NULL;
932 gint width, height, pixel_width, pixel_height;
934 gboolean interlaced = FALSE;
936 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
939 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
940 g_assert (collect_pad);
941 context = collect_pad->track;
943 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
944 videocontext = (GstMatroskaTrackVideoContext *) context;
946 /* gst -> matroska ID'ing */
947 structure = gst_caps_get_structure (caps, 0);
949 mimetype = gst_structure_get_name (structure);
951 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
953 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
955 if (!strcmp (mimetype, "video/x-theora")) {
956 /* we'll extract the details later from the theora identification header */
960 /* get general properties */
961 /* spec says it is mandatory */
962 if (!gst_structure_get_int (structure, "width", &width) ||
963 !gst_structure_get_int (structure, "height", &height))
966 videocontext->pixel_width = width;
967 videocontext->pixel_height = height;
969 /* set vp8 defaults or let user override it */
970 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
971 && (!strcmp (mimetype, "video/x-vp8")))
972 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
973 DEFAULT_PAD_FRAME_DURATION_VP8;
975 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
976 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
978 context->default_duration =
979 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
980 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
981 GST_TIME_ARGS (context->default_duration));
983 context->default_duration = 0;
985 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
986 &pixel_width, &pixel_height)) {
987 if (pixel_width > pixel_height) {
988 videocontext->display_width = width * pixel_width / pixel_height;
989 videocontext->display_height = height;
990 } else if (pixel_width < pixel_height) {
991 videocontext->display_width = width;
992 videocontext->display_height = height * pixel_height / pixel_width;
994 videocontext->display_width = 0;
995 videocontext->display_height = 0;
998 videocontext->display_width = 0;
999 videocontext->display_height = 0;
1004 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1005 videocontext->fourcc = 0;
1007 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1008 * data and other settings
1012 /* extract codec_data, may turn out needed */
1013 value = gst_structure_get_value (structure, "codec_data");
1015 codec_buf = gst_value_get_buffer (value);
1018 if (!strcmp (mimetype, "video/x-raw-yuv")) {
1019 gst_matroska_mux_set_codec_id (context,
1020 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1021 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
1022 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
1023 ||!strcmp (mimetype, "video/x-huffyuv")
1024 || !strcmp (mimetype, "video/x-divx")
1025 || !strcmp (mimetype, "video/x-dv")
1026 || !strcmp (mimetype, "video/x-h263")
1027 || !strcmp (mimetype, "video/x-msmpeg")
1028 || !strcmp (mimetype, "video/x-wmv")
1029 || !strcmp (mimetype, "image/jpeg")) {
1030 gst_riff_strf_vids *bih;
1031 gint size = sizeof (gst_riff_strf_vids);
1034 if (!strcmp (mimetype, "video/x-xvid"))
1035 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
1036 else if (!strcmp (mimetype, "video/x-huffyuv"))
1037 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1038 else if (!strcmp (mimetype, "video/x-dv"))
1039 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1040 else if (!strcmp (mimetype, "video/x-h263"))
1041 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1042 else if (!strcmp (mimetype, "video/x-divx")) {
1045 gst_structure_get_int (structure, "divxversion", &divxversion);
1046 switch (divxversion) {
1048 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1051 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1054 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1057 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1060 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1061 switch (msmpegversion) {
1063 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1066 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1072 } else if (!strcmp (mimetype, "video/x-wmv")) {
1075 if (gst_structure_get_fourcc (structure, "format", &format)) {
1077 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1078 if (wmvversion == 2) {
1079 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1080 } else if (wmvversion == 1) {
1081 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1082 } else if (wmvversion == 3) {
1083 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1086 } else if (!strcmp (mimetype, "image/jpeg")) {
1087 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1093 bih = g_new0 (gst_riff_strf_vids, 1);
1094 GST_WRITE_UINT32_LE (&bih->size, size);
1095 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1096 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1097 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1098 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1099 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1100 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1101 videocontext->pixel_height * 3);
1103 /* process codec private/initialization data, if any */
1105 size += GST_BUFFER_SIZE (codec_buf);
1106 bih = g_realloc (bih, size);
1107 GST_WRITE_UINT32_LE (&bih->size, size);
1108 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
1109 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
1112 gst_matroska_mux_set_codec_id (context,
1113 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1114 gst_matroska_mux_free_codec_priv (context);
1115 context->codec_priv = (gpointer) bih;
1116 context->codec_priv_size = size;
1117 } else if (!strcmp (mimetype, "video/x-h264")) {
1118 gst_matroska_mux_set_codec_id (context,
1119 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1120 gst_matroska_mux_free_codec_priv (context);
1121 /* Create avcC header */
1122 if (codec_buf != NULL) {
1123 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1124 context->codec_priv = g_malloc0 (context->codec_priv_size);
1125 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1126 context->codec_priv_size);
1128 } else if (!strcmp (mimetype, "video/x-theora")) {
1129 const GValue *streamheader;
1131 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1133 gst_matroska_mux_free_codec_priv (context);
1135 streamheader = gst_structure_get_value (structure, "streamheader");
1136 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1137 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1138 ("theora stream headers missing or malformed"));
1141 } else if (!strcmp (mimetype, "video/x-dirac")) {
1142 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1143 } else if (!strcmp (mimetype, "video/x-vp8")) {
1144 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1145 } else if (!strcmp (mimetype, "video/mpeg")) {
1148 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1149 switch (mpegversion) {
1151 gst_matroska_mux_set_codec_id (context,
1152 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1155 gst_matroska_mux_set_codec_id (context,
1156 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1159 gst_matroska_mux_set_codec_id (context,
1160 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1166 /* global headers may be in codec data */
1167 if (codec_buf != NULL) {
1168 gst_matroska_mux_free_codec_priv (context);
1169 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1170 context->codec_priv = g_malloc0 (context->codec_priv_size);
1171 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1172 context->codec_priv_size);
1174 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1176 /* can only make it here if preceding case verified it was version 3 */
1177 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1178 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1180 const GValue *mdpr_data;
1182 gst_structure_get_int (structure, "rmversion", &rmversion);
1183 switch (rmversion) {
1185 gst_matroska_mux_set_codec_id (context,
1186 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1189 gst_matroska_mux_set_codec_id (context,
1190 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1193 gst_matroska_mux_set_codec_id (context,
1194 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1197 gst_matroska_mux_set_codec_id (context,
1198 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1204 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1205 if (mdpr_data != NULL) {
1206 guint8 *priv_data = NULL;
1207 guint priv_data_size = 0;
1209 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1211 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1212 priv_data = g_malloc0 (priv_data_size);
1214 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1216 gst_matroska_mux_free_codec_priv (context);
1217 context->codec_priv = priv_data;
1218 context->codec_priv_size = priv_data_size;
1227 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1228 GST_PAD_NAME (pad), caps);
1233 /* N > 0 to expect a particular number of headers, negative if the
1234 number of headers is variable */
1236 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1237 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1239 GstBuffer **buf = NULL;
1242 guint bufi, i, offset, priv_data_size;
1244 if (streamheader == NULL)
1245 goto no_stream_headers;
1247 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1250 bufarr = g_value_peek_pointer (streamheader);
1251 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1253 if (N > 0 && bufarr->len != N)
1256 context->xiph_headers_to_skip = bufarr->len;
1258 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1259 for (i = 0; i < bufarr->len; i++) {
1260 GValue *bufval = &g_array_index (bufarr, GValue, i);
1262 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1264 goto wrong_content_type;
1267 buf[i] = g_value_peek_pointer (bufval);
1271 if (bufarr->len > 0) {
1272 for (i = 0; i < bufarr->len - 1; i++) {
1273 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1277 for (i = 0; i < bufarr->len; ++i) {
1278 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1281 priv_data = g_malloc0 (priv_data_size);
1283 priv_data[0] = bufarr->len - 1;
1286 if (bufarr->len > 0) {
1287 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1288 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1289 priv_data[offset++] = 0xff;
1291 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1295 for (i = 0; i < bufarr->len; ++i) {
1296 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1297 GST_BUFFER_SIZE (buf[i]));
1298 offset += GST_BUFFER_SIZE (buf[i]);
1301 gst_matroska_mux_free_codec_priv (context);
1302 context->codec_priv = priv_data;
1303 context->codec_priv_size = priv_data_size;
1306 *p_buf0 = gst_buffer_ref (buf[0]);
1315 GST_WARNING ("required streamheaders missing in sink caps!");
1320 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1321 G_VALUE_TYPE_NAME (streamheader));
1326 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1331 GST_WARNING ("streamheaders array does not contain GstBuffers");
1337 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1338 GstMatroskaTrackContext * context)
1340 GstBuffer *buf0 = NULL;
1342 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1345 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1346 GST_WARNING ("First vorbis header too small, ignoring");
1348 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1349 GstMatroskaTrackAudioContext *audiocontext;
1352 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1353 audiocontext = (GstMatroskaTrackAudioContext *) context;
1354 audiocontext->channels = GST_READ_UINT8 (hdr);
1355 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1360 gst_buffer_unref (buf0);
1366 theora_streamheader_to_codecdata (const GValue * streamheader,
1367 GstMatroskaTrackContext * context)
1369 GstBuffer *buf0 = NULL;
1371 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1374 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1375 GST_WARNING ("First theora header too small, ignoring");
1376 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1377 GST_WARNING ("First header not a theora identification header, ignoring");
1379 GstMatroskaTrackVideoContext *videocontext;
1380 guint fps_num, fps_denom, par_num, par_denom;
1383 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1385 videocontext = (GstMatroskaTrackVideoContext *) context;
1386 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1387 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1388 hdr += 3 + 3 + 1 + 1;
1389 fps_num = GST_READ_UINT32_BE (hdr);
1390 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1391 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1392 fps_denom, fps_num);
1394 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1395 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1396 if (par_num > 0 && par_num > 0) {
1397 if (par_num > par_denom) {
1398 videocontext->display_width =
1399 videocontext->pixel_width * par_num / par_denom;
1400 videocontext->display_height = videocontext->pixel_height;
1401 } else if (par_num < par_denom) {
1402 videocontext->display_width = videocontext->pixel_width;
1403 videocontext->display_height =
1404 videocontext->pixel_height * par_denom / par_num;
1406 videocontext->display_width = 0;
1407 videocontext->display_height = 0;
1410 videocontext->display_width = 0;
1411 videocontext->display_height = 0;
1417 gst_buffer_unref (buf0);
1423 kate_streamheader_to_codecdata (const GValue * streamheader,
1424 GstMatroskaTrackContext * context)
1426 GstBuffer *buf0 = NULL;
1428 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1431 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1432 GST_WARNING ("First kate header too small, ignoring");
1433 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1434 GST_WARNING ("First header not a kate identification header, ignoring");
1438 gst_buffer_unref (buf0);
1444 flac_streamheader_to_codecdata (const GValue * streamheader,
1445 GstMatroskaTrackContext * context)
1452 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1453 GST_WARNING ("No or invalid streamheader field in the caps");
1457 bufarr = g_value_peek_pointer (streamheader);
1458 if (bufarr->len < 2) {
1459 GST_WARNING ("Too few headers in streamheader field");
1463 context->xiph_headers_to_skip = bufarr->len + 1;
1465 bufval = &g_array_index (bufarr, GValue, 0);
1466 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1467 GST_WARNING ("streamheaders array does not contain GstBuffers");
1471 buffer = g_value_peek_pointer (bufval);
1473 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1474 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1475 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1476 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1477 GST_WARNING ("Invalid streamheader for FLAC");
1481 gst_matroska_mux_free_codec_priv (context);
1482 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1483 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1484 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1485 GST_BUFFER_SIZE (buffer) - 9);
1487 for (i = 1; i < bufarr->len; i++) {
1488 bufval = &g_array_index (bufarr, GValue, i);
1490 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1491 gst_matroska_mux_free_codec_priv (context);
1492 GST_WARNING ("streamheaders array does not contain GstBuffers");
1496 buffer = g_value_peek_pointer (bufval);
1498 context->codec_priv =
1499 g_realloc (context->codec_priv,
1500 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1501 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1502 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1503 context->codec_priv_size =
1504 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1511 speex_streamheader_to_codecdata (const GValue * streamheader,
1512 GstMatroskaTrackContext * context)
1518 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1519 GST_WARNING ("No or invalid streamheader field in the caps");
1523 bufarr = g_value_peek_pointer (streamheader);
1524 if (bufarr->len != 2) {
1525 GST_WARNING ("Too few headers in streamheader field");
1529 context->xiph_headers_to_skip = bufarr->len + 1;
1531 bufval = &g_array_index (bufarr, GValue, 0);
1532 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1533 GST_WARNING ("streamheaders array does not contain GstBuffers");
1537 buffer = g_value_peek_pointer (bufval);
1539 if (GST_BUFFER_SIZE (buffer) < 80
1540 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1541 GST_WARNING ("Invalid streamheader for Speex");
1545 gst_matroska_mux_free_codec_priv (context);
1546 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1547 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1548 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1549 GST_BUFFER_SIZE (buffer));
1551 bufval = &g_array_index (bufarr, GValue, 1);
1553 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1554 gst_matroska_mux_free_codec_priv (context);
1555 GST_WARNING ("streamheaders array does not contain GstBuffers");
1559 buffer = g_value_peek_pointer (bufval);
1561 context->codec_priv =
1562 g_realloc (context->codec_priv,
1563 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1564 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1565 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1566 context->codec_priv_size =
1567 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1572 static const gchar *
1573 aac_codec_data_to_codec_id (const GstBuffer * buf)
1575 const gchar *result;
1578 /* default to MAIN */
1581 if (GST_BUFFER_SIZE (buf) >= 2) {
1582 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1600 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1609 * gst_matroska_mux_audio_pad_setcaps:
1610 * @pad: Pad which got the caps.
1613 * Setcaps function for audio sink pad.
1615 * Returns: #TRUE on success.
1618 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1620 GstMatroskaTrackContext *context = NULL;
1621 GstMatroskaTrackAudioContext *audiocontext;
1622 GstMatroskaMux *mux;
1623 GstMatroskaPad *collect_pad;
1624 const gchar *mimetype;
1625 gint samplerate = 0, channels = 0;
1626 GstStructure *structure;
1627 const GValue *codec_data = NULL;
1628 const GstBuffer *buf = NULL;
1629 const gchar *stream_format = NULL;
1631 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1634 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1635 g_assert (collect_pad);
1636 context = collect_pad->track;
1638 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1639 audiocontext = (GstMatroskaTrackAudioContext *) context;
1641 structure = gst_caps_get_structure (caps, 0);
1642 mimetype = gst_structure_get_name (structure);
1645 gst_structure_get_int (structure, "rate", &samplerate);
1646 gst_structure_get_int (structure, "channels", &channels);
1648 audiocontext->samplerate = samplerate;
1649 audiocontext->channels = channels;
1650 audiocontext->bitdepth = 0;
1651 context->default_duration = 0;
1653 codec_data = gst_structure_get_value (structure, "codec_data");
1655 buf = gst_value_get_buffer (codec_data);
1657 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1658 * data and other settings
1662 if (!strcmp (mimetype, "audio/mpeg")) {
1663 gint mpegversion = 0;
1665 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1666 switch (mpegversion) {
1672 gst_structure_get_int (structure, "layer", &layer);
1674 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1675 GST_WARNING_OBJECT (mux,
1676 "Unable to determine MPEG audio version, assuming 1");
1682 else if (layer == 2)
1684 else if (version == 2)
1689 context->default_duration =
1690 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1694 gst_matroska_mux_set_codec_id (context,
1695 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1698 gst_matroska_mux_set_codec_id (context,
1699 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1702 gst_matroska_mux_set_codec_id (context,
1703 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1712 stream_format = gst_structure_get_string (structure, "stream-format");
1713 /* check this is raw aac */
1714 if (stream_format) {
1715 if (strcmp (stream_format, "raw") != 0) {
1716 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1720 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1725 if (mpegversion == 2)
1727 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1728 aac_codec_data_to_codec_id (buf));
1729 else if (mpegversion == 4)
1731 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1732 aac_codec_data_to_codec_id (buf));
1734 g_assert_not_reached ();
1736 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1743 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1745 gint endianness = G_LITTLE_ENDIAN;
1746 gboolean signedness = TRUE;
1748 if (!gst_structure_get_int (structure, "width", &width) ||
1749 !gst_structure_get_int (structure, "depth", &depth) ||
1750 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1751 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1756 !gst_structure_get_int (structure, "endianness", &endianness)) {
1757 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1761 if (width != depth) {
1762 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1766 /* FIXME: where is this spec'ed out? (tpm) */
1767 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1768 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1772 audiocontext->bitdepth = depth;
1773 if (endianness == G_BIG_ENDIAN)
1774 gst_matroska_mux_set_codec_id (context,
1775 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1777 gst_matroska_mux_set_codec_id (context,
1778 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1780 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1783 if (!gst_structure_get_int (structure, "width", &width)) {
1784 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1788 audiocontext->bitdepth = width;
1789 gst_matroska_mux_set_codec_id (context,
1790 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1792 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1793 const GValue *streamheader;
1795 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1797 gst_matroska_mux_free_codec_priv (context);
1799 streamheader = gst_structure_get_value (structure, "streamheader");
1800 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1801 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1802 ("vorbis stream headers missing or malformed"));
1805 } else if (!strcmp (mimetype, "audio/x-flac")) {
1806 const GValue *streamheader;
1808 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1810 gst_matroska_mux_free_codec_priv (context);
1812 streamheader = gst_structure_get_value (structure, "streamheader");
1813 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1814 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1815 ("flac stream headers missing or malformed"));
1818 } else if (!strcmp (mimetype, "audio/x-speex")) {
1819 const GValue *streamheader;
1821 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1822 gst_matroska_mux_free_codec_priv (context);
1824 streamheader = gst_structure_get_value (structure, "streamheader");
1825 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1826 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1827 ("speex stream headers missing or malformed"));
1830 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1831 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1832 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1833 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1834 } else if (!strcmp (mimetype, "audio/x-dts")) {
1835 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1836 } else if (!strcmp (mimetype, "audio/x-tta")) {
1839 /* TTA frame duration */
1840 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1842 gst_structure_get_int (structure, "width", &width);
1843 audiocontext->bitdepth = width;
1844 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1846 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1848 const GValue *mdpr_data;
1850 gst_structure_get_int (structure, "raversion", &raversion);
1851 switch (raversion) {
1853 gst_matroska_mux_set_codec_id (context,
1854 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1857 gst_matroska_mux_set_codec_id (context,
1858 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1861 gst_matroska_mux_set_codec_id (context,
1862 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1868 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1869 if (mdpr_data != NULL) {
1870 guint8 *priv_data = NULL;
1871 guint priv_data_size = 0;
1873 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1875 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1876 priv_data = g_malloc0 (priv_data_size);
1878 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1880 gst_matroska_mux_free_codec_priv (context);
1882 context->codec_priv = priv_data;
1883 context->codec_priv_size = priv_data_size;
1886 } else if (!strcmp (mimetype, "audio/x-wma")
1887 || !strcmp (mimetype, "audio/x-alaw")
1888 || !strcmp (mimetype, "audio/x-mulaw")) {
1890 guint codec_priv_size;
1895 if (samplerate == 0 || channels == 0) {
1896 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1900 if (!strcmp (mimetype, "audio/x-wma")) {
1904 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1905 || !gst_structure_get_int (structure, "block_align", &block_align)
1906 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1907 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1912 switch (wmaversion) {
1914 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1917 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1920 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1923 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1927 if (gst_structure_get_int (structure, "depth", &depth))
1928 audiocontext->bitdepth = depth;
1929 } else if (!strcmp (mimetype, "audio/x-alaw")
1930 || !strcmp (mimetype, "audio/x-mulaw")) {
1931 audiocontext->bitdepth = 8;
1932 if (!strcmp (mimetype, "audio/x-alaw"))
1933 format = GST_RIFF_WAVE_FORMAT_ALAW;
1935 format = GST_RIFF_WAVE_FORMAT_MULAW;
1937 block_align = channels;
1938 bitrate = block_align * samplerate;
1940 g_assert (format != 0);
1942 codec_priv_size = WAVEFORMATEX_SIZE;
1944 codec_priv_size += GST_BUFFER_SIZE (buf);
1946 /* serialize waveformatex structure */
1947 codec_priv = g_malloc0 (codec_priv_size);
1948 GST_WRITE_UINT16_LE (codec_priv, format);
1949 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1950 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1951 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1952 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1953 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1955 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1957 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1959 /* process codec private/initialization data, if any */
1961 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1962 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1965 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1966 gst_matroska_mux_free_codec_priv (context);
1967 context->codec_priv = (gpointer) codec_priv;
1968 context->codec_priv_size = codec_priv_size;
1976 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1977 GST_PAD_NAME (pad), caps);
1982 /* we probably don't have the data at start,
1983 * so have to reserve (a maximum) space to write this at the end.
1984 * bit spacy, but some formats can hold quite some */
1985 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1988 * gst_matroska_mux_subtitle_pad_setcaps:
1989 * @pad: Pad which got the caps.
1992 * Setcaps function for subtitle sink pad.
1994 * Returns: #TRUE on success.
1997 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1999 /* There is now (at least) one such alement (kateenc), and I'm going
2000 to handle it here and claim it works when it can be piped back
2001 through GStreamer and VLC */
2003 GstMatroskaTrackContext *context = NULL;
2004 GstMatroskaTrackSubtitleContext *scontext;
2005 GstMatroskaMux *mux;
2006 GstMatroskaPad *collect_pad;
2007 const gchar *mimetype;
2008 GstStructure *structure;
2009 const GValue *value = NULL;
2010 const GstBuffer *buf = NULL;
2011 gboolean ret = TRUE;
2013 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2016 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2017 g_assert (collect_pad);
2018 context = collect_pad->track;
2020 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2021 scontext = (GstMatroskaTrackSubtitleContext *) context;
2023 structure = gst_caps_get_structure (caps, 0);
2024 mimetype = gst_structure_get_name (structure);
2027 scontext->check_utf8 = 1;
2028 scontext->invalid_utf8 = 0;
2029 context->default_duration = 0;
2031 if (!strcmp (mimetype, "subtitle/x-kate")) {
2032 const GValue *streamheader;
2034 gst_matroska_mux_set_codec_id (context,
2035 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2037 gst_matroska_mux_free_codec_priv (context);
2039 streamheader = gst_structure_get_value (structure, "streamheader");
2040 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2041 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2042 ("kate stream headers missing or malformed"));
2046 } else if (!strcmp (mimetype, "text/plain")) {
2047 gst_matroska_mux_set_codec_id (context,
2048 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2049 } else if (!strcmp (mimetype, "application/x-ssa")) {
2050 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2051 } else if (!strcmp (mimetype, "application/x-ass")) {
2052 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2053 } else if (!strcmp (mimetype, "application/x-usf")) {
2054 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2055 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2056 gst_matroska_mux_set_codec_id (context,
2057 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2063 /* maybe some private data, e.g. vobsub */
2064 value = gst_structure_get_value (structure, "codec_data");
2066 buf = gst_value_get_buffer (value);
2068 guint8 *priv_data = NULL;
2069 guint priv_data_size = 0;
2071 priv_data_size = GST_BUFFER_SIZE (buf);
2072 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2073 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2074 " exceeded maximum (%d); discarding", pad,
2075 SUBTITLE_MAX_CODEC_PRIVATE);
2079 gst_matroska_mux_free_codec_priv (context);
2081 priv_data = g_malloc0 (priv_data_size);
2082 memcpy (priv_data, GST_BUFFER_DATA (buf), priv_data_size);
2083 context->codec_priv = priv_data;
2084 context->codec_priv_size = priv_data_size;
2087 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2088 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2097 * gst_matroska_mux_request_new_pad:
2098 * @element: #GstMatroskaMux.
2099 * @templ: #GstPadTemplate.
2100 * @pad_name: New pad name.
2102 * Request pad function for sink templates.
2104 * Returns: New #GstPad.
2107 gst_matroska_mux_request_new_pad (GstElement * element,
2108 GstPadTemplate * templ, const gchar * req_name)
2110 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2111 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2112 GstMatroskaPad *collect_pad;
2113 GstMatroskamuxPad *newpad;
2115 const gchar *pad_name = NULL;
2116 GstPadSetCapsFunction setcapsfunc = NULL;
2117 GstMatroskaTrackContext *context = NULL;
2119 gboolean locked = TRUE;
2122 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
2123 /* don't mix named and unnamed pads, if the pad already exists we fail when
2124 * trying to add it */
2125 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
2126 pad_name = req_name;
2128 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
2131 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2132 context = (GstMatroskaTrackContext *)
2133 g_new0 (GstMatroskaTrackAudioContext, 1);
2134 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2135 context->name = g_strdup ("Audio");
2136 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
2137 /* don't mix named and unnamed pads, if the pad already exists we fail when
2138 * trying to add it */
2139 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
2140 pad_name = req_name;
2142 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
2145 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2146 context = (GstMatroskaTrackContext *)
2147 g_new0 (GstMatroskaTrackVideoContext, 1);
2148 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2149 context->name = g_strdup ("Video");
2150 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
2151 /* don't mix named and unnamed pads, if the pad already exists we fail when
2152 * trying to add it */
2153 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
2154 pad_name = req_name;
2156 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
2159 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2160 context = (GstMatroskaTrackContext *)
2161 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2162 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2163 context->name = g_strdup ("Subtitle");
2164 /* setcaps may only provide proper one a lot later */
2165 id = g_strdup ("S_SUB_UNKNOWN");
2168 GST_WARNING_OBJECT (mux, "This is not our template!");
2172 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2173 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2176 gst_matroskamux_pad_init (newpad);
2177 collect_pad = (GstMatroskaPad *)
2178 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2179 sizeof (GstMatroskamuxPad),
2180 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2182 collect_pad->track = context;
2183 gst_matroska_pad_reset (collect_pad, FALSE);
2184 collect_pad->track->codec_id = id;
2186 gst_pad_set_setcaps_function (GST_PAD (newpad), setcapsfunc);
2187 gst_pad_set_active (GST_PAD (newpad), TRUE);
2188 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2189 goto pad_add_failed;
2193 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2195 return GST_PAD (newpad);
2200 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2201 gst_object_unref (newpad);
2207 * gst_matroska_mux_release_pad:
2208 * @element: #GstMatroskaMux.
2209 * @pad: Pad to release.
2211 * Release a previously requested pad.
2214 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2216 GstMatroskaMux *mux;
2219 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2221 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2222 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2223 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2225 if (cdata->pad == pad) {
2226 GstClockTime min_dur; /* observed minimum duration */
2228 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2229 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2230 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2231 if (collect_pad->duration < min_dur)
2232 collect_pad->duration = min_dur;
2235 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2236 mux->duration < collect_pad->duration)
2237 mux->duration = collect_pad->duration;
2243 gst_collect_pads2_remove_pad (mux->collect, pad);
2244 if (gst_element_remove_pad (element, pad))
2250 * gst_matroska_mux_track_header:
2251 * @mux: #GstMatroskaMux
2252 * @context: Tack context.
2254 * Write a track header.
2257 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2258 GstMatroskaTrackContext * context)
2260 GstEbmlWrite *ebml = mux->ebml_write;
2263 /* TODO: check if everything necessary is written and check default values */
2265 /* track type goes before the type-specific stuff */
2266 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2267 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2269 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2270 gst_matroska_mux_create_uid ());
2271 if (context->default_duration) {
2272 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2273 context->default_duration);
2275 if (context->language) {
2276 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2280 /* FIXME: until we have a nice way of getting the codecname
2281 * out of the caps, I'm not going to enable this. Too much
2282 * (useless, double, boring) work... */
2283 /* TODO: Use value from tags if any */
2284 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2285 context->codec_name); */
2286 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2288 /* type-specific stuff */
2289 switch (context->type) {
2290 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2291 GstMatroskaTrackVideoContext *videocontext =
2292 (GstMatroskaTrackVideoContext *) context;
2294 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2295 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2296 videocontext->pixel_width);
2297 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2298 videocontext->pixel_height);
2299 if (videocontext->display_width && videocontext->display_height) {
2300 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2301 videocontext->display_width);
2302 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2303 videocontext->display_height);
2305 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2306 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2307 if (videocontext->fourcc) {
2308 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2310 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2311 (gpointer) & fcc_le, 4);
2313 gst_ebml_write_master_finish (ebml, master);
2318 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2319 GstMatroskaTrackAudioContext *audiocontext =
2320 (GstMatroskaTrackAudioContext *) context;
2322 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2323 if (audiocontext->samplerate != 8000)
2324 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2325 audiocontext->samplerate);
2326 if (audiocontext->channels != 1)
2327 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2328 audiocontext->channels);
2329 if (audiocontext->bitdepth) {
2330 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2331 audiocontext->bitdepth);
2333 gst_ebml_write_master_finish (ebml, master);
2338 /* this is what we write for now and must be filled
2339 * and remainder void'ed later on */
2340 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2342 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2345 context->pos = ebml->pos;
2346 /* CodecID is mandatory ... */
2347 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2349 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2350 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2351 SUBTITLE_MAX_CODEC_PRIVATE);
2353 /* real data has to be written at finish */
2357 /* doesn't need type-specific data */
2361 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2362 if (context->codec_priv)
2363 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2364 context->codec_priv, context->codec_priv_size);
2368 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2370 guint64 title_master;
2373 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2375 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2376 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2377 GST_MATROSKA_MUX_CHAPLANG);
2379 gst_ebml_write_master_finish (ebml, title_master);
2383 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2384 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2385 guint64 * master_edition)
2387 guint64 uid, master_chapteratom;
2389 GstTocEntry *cur_entry;
2394 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2396 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2398 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2399 /* create uid for the parent */
2400 uid = gst_matroska_mux_create_uid ();
2401 g_free (edition->uid);
2402 edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2405 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2407 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
2408 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2409 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2410 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2413 uid = gst_matroska_mux_create_uid ();
2414 gst_toc_entry_get_start_stop (entry, &start, &stop);
2416 master_chapteratom =
2417 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2418 g_free (entry->uid);
2419 entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
2420 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2421 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2422 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2423 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2424 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2426 cur = entry->subentries;
2427 while (cur != NULL) {
2428 cur_entry = cur->data;
2429 gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
2434 if (G_LIKELY (entry->tags != NULL)) {
2435 count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
2437 for (i = 0; i < count; ++i) {
2438 gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
2439 gst_matroska_mux_write_chapter_title (title, ebml);
2443 /* remove title tag */
2444 if (G_LIKELY (count > 0))
2445 gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
2448 gst_ebml_write_master_finish (ebml, master_chapteratom);
2452 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2453 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
2455 guint64 master_edition = 0;
2457 GstTocEntry *subentry;
2459 cur = entry->subentries;
2460 while (cur != NULL) {
2461 subentry = cur->data;
2462 gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
2468 if (G_LIKELY (master_edition != 0))
2469 gst_ebml_write_master_finish (ebml, master_edition);
2473 * gst_matroska_mux_start:
2474 * @mux: #GstMatroskaMux
2476 * Start a new matroska file (write headers etc...)
2479 gst_matroska_mux_start (GstMatroskaMux * mux)
2481 GstEbmlWrite *ebml = mux->ebml_write;
2482 const gchar *doctype;
2483 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2484 GST_MATROSKA_ID_TRACKS,
2485 GST_MATROSKA_ID_CHAPTERS,
2486 GST_MATROSKA_ID_CUES,
2487 GST_MATROSKA_ID_TAGS,
2490 guint64 master, child;
2494 GstClockTime duration = 0;
2495 guint32 segment_uid[4];
2496 GTimeVal time = { 0, 0 };
2498 /* if not streaming, check if downstream is seekable */
2499 if (!mux->streamable) {
2503 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2504 if (gst_pad_peer_query (mux->srcpad, query)) {
2505 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2506 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2508 mux->streamable = TRUE;
2509 g_object_notify (G_OBJECT (mux), "streamable");
2510 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2511 "streamable=false. Will ignore that and create streamable output "
2515 /* have to assume seeking is supported if query not handled downstream */
2516 /* FIXME 0.11: change to query not handled => seeking not supported */
2517 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2519 gst_query_unref (query);
2522 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2523 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2525 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2527 /* we start with a EBML header */
2528 doctype = mux->doctype;
2529 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2530 doctype, mux->doctype_version);
2531 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2533 /* the rest of the header is cached */
2534 gst_ebml_write_set_cache (ebml, 0x1000);
2536 /* start a segment */
2538 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2539 mux->segment_master = ebml->pos;
2541 if (!mux->streamable) {
2542 /* seekhead (table of contents) - we set the positions later */
2543 mux->seekhead_pos = ebml->pos;
2544 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2545 for (i = 0; seekhead_id[i] != 0; i++) {
2546 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2547 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2548 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2549 gst_ebml_write_master_finish (ebml, child);
2551 gst_ebml_write_master_finish (ebml, master);
2554 if (mux->streamable) {
2555 const GstTagList *tags;
2558 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2560 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2561 guint64 master_tags, master_tag;
2563 GST_DEBUG_OBJECT (mux, "Writing tags");
2565 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2566 mux->tags_pos = ebml->pos;
2567 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2568 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2569 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2570 gst_ebml_write_master_finish (ebml, master_tag);
2571 gst_ebml_write_master_finish (ebml, master_tags);
2576 mux->info_pos = ebml->pos;
2577 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2578 for (i = 0; i < 4; i++) {
2579 segment_uid[i] = g_random_int ();
2581 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2582 (guint8 *) segment_uid, 16);
2583 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2584 mux->duration_pos = ebml->pos;
2586 if (!mux->streamable) {
2587 for (collected = mux->collect->data; collected;
2588 collected = g_slist_next (collected)) {
2589 GstMatroskaPad *collect_pad;
2590 GstFormat format = GST_FORMAT_TIME;
2592 gint64 trackduration;
2594 collect_pad = (GstMatroskaPad *) collected->data;
2595 thepad = collect_pad->collect.pad;
2597 /* Query the total length of the track. */
2598 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2599 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2600 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2601 GST_TIME_ARGS (trackduration));
2602 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2603 duration = (GstClockTime) trackduration;
2607 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2608 gst_guint64_to_gdouble (duration) /
2609 gst_guint64_to_gdouble (mux->time_scale));
2611 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2612 "GStreamer plugin version " PACKAGE_VERSION);
2613 if (mux->writing_app && mux->writing_app[0]) {
2614 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2616 g_get_current_time (&time);
2617 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2618 gst_ebml_write_master_finish (ebml, master);
2621 mux->tracks_pos = ebml->pos;
2622 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2624 for (collected = mux->collect->data; collected;
2625 collected = g_slist_next (collected)) {
2626 GstMatroskaPad *collect_pad;
2629 collect_pad = (GstMatroskaPad *) collected->data;
2630 thepad = collect_pad->collect.pad;
2632 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2633 collect_pad->track->codec_id != 0) {
2634 collect_pad->track->num = tracknum++;
2635 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2636 gst_matroska_mux_track_header (mux, collect_pad->track);
2637 gst_ebml_write_master_finish (ebml, child);
2638 /* some remaining pad/track setup */
2639 collect_pad->default_duration_scaled =
2640 gst_util_uint64_scale (collect_pad->track->default_duration,
2641 1, mux->time_scale);
2644 gst_ebml_write_master_finish (ebml, master);
2647 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL && !mux->streamable) {
2648 guint64 master_chapters = 0;
2649 GstTocEntry *toc_entry;
2651 GList *cur, *to_write = NULL;
2654 GST_DEBUG ("Writing chapters");
2656 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2658 /* check whether we have editions or chapters at the root level */
2659 toc_entry = toc->entries->data;
2661 if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
2662 toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
2663 gst_toc_entry_set_start_stop (toc_entry, -1, -1);
2665 /* aggregate all chapters without root edition */
2667 while (cur != NULL) {
2668 toc_entry->subentries =
2669 g_list_prepend (toc_entry->subentries, cur->data);
2673 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2674 subentries->data), &start, NULL);
2675 toc_entry->subentries = g_list_reverse (toc_entry->subentries);
2676 gst_toc_entry_get_start_stop (((GstTocEntry *) toc_entry->
2677 subentries->data), NULL, &stop);
2678 gst_toc_entry_set_start_stop (toc_entry, start, stop);
2680 to_write = g_list_append (to_write, toc_entry);
2683 to_write = toc->entries;
2686 /* finally write chapters */
2687 mux->chapters_pos = ebml->pos;
2690 while (cur != NULL) {
2691 gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
2696 /* close master element if any edition was written */
2697 if (G_LIKELY (master_chapters != 0))
2698 gst_ebml_write_master_finish (ebml, master_chapters);
2700 if (toc_entry != NULL) {
2701 g_list_free (toc_entry->subentries);
2702 toc_entry->subentries = NULL;
2703 gst_toc_entry_free (toc_entry);
2704 g_list_free (to_write);
2708 /* lastly, flush the cache */
2709 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2713 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2716 /* TODO: more sensible tag mappings */
2719 const gchar *matroska_tagname;
2720 const gchar *gstreamer_tagname;
2724 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2725 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2726 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2727 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2728 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2729 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2730 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2731 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2732 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2733 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2734 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2735 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2736 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2737 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2738 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2740 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2742 guint64 simpletag_master;
2744 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2745 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2746 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2748 if (strcmp (tagname_gst, tag) == 0) {
2749 GValue src = { 0, };
2752 if (!gst_tag_list_copy_value (&src, list, tag))
2754 if ((dest = gst_value_serialize (&src))) {
2756 simpletag_master = gst_ebml_write_master_start (ebml,
2757 GST_MATROSKA_ID_SIMPLETAG);
2758 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2759 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2760 gst_ebml_write_master_finish (ebml, simpletag_master);
2763 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2765 g_value_unset (&src);
2772 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
2773 const GstTocEntry * entry, guint64 * master_tags)
2775 guint64 master_tag, master_targets;
2779 ebml = mux->ebml_write;
2781 if (G_UNLIKELY (entry->tags != NULL && !gst_tag_list_is_empty (entry->tags))) {
2782 if (*master_tags == 0) {
2783 mux->tags_pos = ebml->pos;
2784 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2787 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2789 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
2791 if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
2792 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
2793 g_ascii_strtoull (entry->uid, NULL, 10));
2795 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
2796 g_ascii_strtoull (entry->uid, NULL, 10));
2798 gst_ebml_write_master_finish (ebml, master_targets);
2799 gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
2800 gst_ebml_write_master_finish (ebml, master_tag);
2803 cur = entry->subentries;
2804 while (cur != NULL) {
2805 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
2811 * gst_matroska_mux_finish:
2812 * @mux: #GstMatroskaMux
2814 * Finish a new matroska file (write index etc...)
2817 gst_matroska_mux_finish (GstMatroskaMux * mux)
2819 GstEbmlWrite *ebml = mux->ebml_write;
2821 guint64 duration = 0;
2823 const GstTagList *tags;
2825 /* finish last cluster */
2827 gst_ebml_write_master_finish (ebml, mux->cluster);
2831 if (mux->index != NULL) {
2833 guint64 master, pointentry_master, trackpos_master;
2835 mux->cues_pos = ebml->pos;
2836 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2837 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2839 for (n = 0; n < mux->num_indexes; n++) {
2840 GstMatroskaIndex *idx = &mux->index[n];
2842 pointentry_master = gst_ebml_write_master_start (ebml,
2843 GST_MATROSKA_ID_POINTENTRY);
2844 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2845 idx->time / mux->time_scale);
2846 trackpos_master = gst_ebml_write_master_start (ebml,
2847 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2848 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2849 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2850 idx->pos - mux->segment_master);
2851 gst_ebml_write_master_finish (ebml, trackpos_master);
2852 gst_ebml_write_master_finish (ebml, pointentry_master);
2855 gst_ebml_write_master_finish (ebml, master);
2856 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2860 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2862 if ((tags != NULL && !gst_tag_list_is_empty (tags))
2863 || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
2864 guint64 master_tags = 0, master_tag;
2868 GST_DEBUG_OBJECT (mux, "Writing tags");
2870 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
2873 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2874 mux->tags_pos = ebml->pos;
2875 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2876 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2879 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2881 gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
2884 gst_ebml_write_master_finish (ebml, master_tag);
2889 while (cur != NULL) {
2890 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
2895 if (master_tags != 0)
2896 gst_ebml_write_master_finish (ebml, master_tags);
2899 /* update seekhead. We know that:
2900 * - a seekhead contains 5 entries.
2901 * - order of entries is as above.
2902 * - a seekhead has a 4-byte header + 8-byte length
2903 * - each entry is 2-byte master, 2-byte ID pointer,
2904 * 2-byte length pointer, all 8/1-byte length, 4-
2905 * byte ID and 8-byte length pointer, where the
2906 * length pointer starts at 20.
2907 * - all entries are local to the segment (so pos - segment_master).
2908 * - so each entry is at 12 + 20 + num * 28. */
2909 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2910 mux->info_pos - mux->segment_master);
2911 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2912 mux->tracks_pos - mux->segment_master);
2913 if (gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL
2914 && mux->chapters_pos > 0) {
2915 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2916 mux->chapters_pos - mux->segment_master);
2919 guint64 my_pos = ebml->pos;
2921 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2922 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2923 gst_ebml_write_seek (ebml, my_pos);
2925 if (mux->index != NULL) {
2926 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2927 mux->cues_pos - mux->segment_master);
2930 guint64 my_pos = ebml->pos;
2932 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2933 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2934 gst_ebml_write_seek (ebml, my_pos);
2938 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
2939 mux->tags_pos - mux->segment_master);
2942 guint64 my_pos = ebml->pos;
2944 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
2945 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2946 gst_ebml_write_seek (ebml, my_pos);
2950 * - first get the overall duration
2951 * (a released track may have left a duration in here)
2952 * - write some track header data for subtitles
2954 duration = mux->duration;
2956 for (collected = mux->collect->data; collected;
2957 collected = g_slist_next (collected)) {
2958 GstMatroskaPad *collect_pad;
2959 GstClockTime min_duration; /* observed minimum duration */
2960 GstMatroskaTrackContext *context;
2961 gint voidleft = 0, fill = 0;
2964 collect_pad = (GstMatroskaPad *) collected->data;
2965 context = collect_pad->track;
2967 GST_DEBUG_OBJECT (mux,
2968 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2969 " end ts %" GST_TIME_FORMAT, collect_pad,
2970 GST_TIME_ARGS (collect_pad->start_ts),
2971 GST_TIME_ARGS (collect_pad->end_ts));
2973 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2974 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2976 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2977 if (collect_pad->duration < min_duration)
2978 collect_pad->duration = min_duration;
2979 GST_DEBUG_OBJECT (collect_pad,
2980 "final track duration: %" GST_TIME_FORMAT,
2981 GST_TIME_ARGS (collect_pad->duration));
2984 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2985 duration < collect_pad->duration)
2986 duration = collect_pad->duration;
2988 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2992 /* write subtitle type and possible private data */
2993 gst_ebml_write_seek (ebml, context->pos);
2994 /* complex way to write ascii to account for extra filling */
2995 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2996 strcpy (codec_id, context->codec_id);
2997 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2998 codec_id, strlen (context->codec_id) + 1 + fill);
3000 if (context->codec_priv)
3001 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
3002 context->codec_priv, context->codec_priv_size);
3003 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
3004 /* void'ify; sigh, variable sized length field */
3005 if (voidleft == 1) {
3008 } else if (voidleft && voidleft <= 128)
3009 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
3010 else if (voidleft >= 130)
3011 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
3012 else if (voidleft == 129) {
3013 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
3014 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
3018 /* seek back (optional, but do anyway) */
3019 gst_ebml_write_seek (ebml, pos);
3021 /* update duration */
3022 if (duration != 0) {
3023 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3024 GST_TIME_ARGS (duration));
3025 pos = mux->ebml_write->pos;
3026 gst_ebml_write_seek (ebml, mux->duration_pos);
3027 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3028 gst_guint64_to_gdouble (duration) /
3029 gst_guint64_to_gdouble (mux->time_scale));
3030 gst_ebml_write_seek (ebml, pos);
3033 guint64 my_pos = ebml->pos;
3035 gst_ebml_write_seek (ebml, mux->duration_pos);
3036 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3037 gst_ebml_write_seek (ebml, my_pos);
3039 GST_DEBUG_OBJECT (mux, "finishing segment");
3040 /* finish segment - this also writes element length */
3041 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3045 * gst_matroska_mux_buffer_header:
3046 * @track: Track context.
3047 * @relative_timestamp: relative timestamp of the buffer
3048 * @flags: Buffer flags.
3050 * Create a buffer containing buffer header.
3052 * Returns: New buffer.
3055 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3056 gint16 relative_timestamp, int flags)
3060 hdr = gst_buffer_new_and_alloc (4);
3061 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3062 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
3063 /* time relative to clustertime */
3064 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
3067 GST_BUFFER_DATA (hdr)[3] = flags;
3072 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3073 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3074 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3077 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3078 GstMatroskaPad * collect_pad, GstBuffer * buf)
3080 GstMatroskaTrackVideoContext *ctx =
3081 (GstMatroskaTrackVideoContext *) collect_pad->track;
3082 const guint8 *data = GST_BUFFER_DATA (buf);
3083 guint size = GST_BUFFER_SIZE (buf);
3085 guint32 next_parse_offset;
3086 GstBuffer *ret = NULL;
3087 gboolean is_muxing_unit = FALSE;
3089 if (GST_BUFFER_SIZE (buf) < 13) {
3090 gst_buffer_unref (buf);
3094 /* Check if this buffer contains a picture or end-of-sequence packet */
3095 while (size >= 13) {
3096 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3097 gst_buffer_unref (buf);
3101 parse_code = GST_READ_UINT8 (data + 4);
3102 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3103 if (ctx->dirac_unit) {
3104 gst_buffer_unref (ctx->dirac_unit);
3105 ctx->dirac_unit = NULL;
3107 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3108 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3109 is_muxing_unit = TRUE;
3113 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3115 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3118 data += next_parse_offset;
3119 size -= next_parse_offset;
3122 if (ctx->dirac_unit)
3123 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
3125 ctx->dirac_unit = gst_buffer_ref (buf);
3127 if (is_muxing_unit) {
3128 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
3129 ctx->dirac_unit = NULL;
3130 gst_buffer_copy_metadata (ret, buf,
3131 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
3132 GST_BUFFER_COPY_CAPS);
3133 gst_buffer_unref (buf);
3135 gst_buffer_unref (buf);
3143 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3147 GValue streamheader = { 0 };
3148 GValue bufval = { 0 };
3149 GstBuffer *streamheader_buffer;
3150 GstEbmlWrite *ebml = mux->ebml_write;
3152 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3153 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
3154 caps = gst_caps_new_simple ("video/webm", NULL);
3156 caps = gst_caps_new_simple ("video/x-matroska", NULL);
3158 s = gst_caps_get_structure (caps, 0);
3159 g_value_init (&streamheader, GST_TYPE_ARRAY);
3160 g_value_init (&bufval, GST_TYPE_BUFFER);
3161 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
3162 gst_value_set_buffer (&bufval, streamheader_buffer);
3163 gst_value_array_append_value (&streamheader, &bufval);
3164 g_value_unset (&bufval);
3165 gst_structure_set_value (s, "streamheader", &streamheader);
3166 g_value_unset (&streamheader);
3167 gst_caps_replace (&ebml->caps, caps);
3168 gst_buffer_unref (streamheader_buffer);
3169 gst_caps_unref (caps);
3173 * gst_matroska_mux_write_data:
3174 * @mux: #GstMatroskaMux
3175 * @collect_pad: #GstMatroskaPad with the data
3177 * Write collected data (called from gst_matroska_mux_collected).
3179 * Returns: Result of the gst_pad_push issued to write the data.
3181 static GstFlowReturn
3182 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3185 GstEbmlWrite *ebml = mux->ebml_write;
3188 gboolean write_duration;
3189 gint16 relative_timestamp;
3190 gint64 relative_timestamp64;
3191 guint64 block_duration;
3192 gboolean is_video_keyframe = FALSE;
3193 GstMatroskamuxPad *pad;
3196 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3198 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3199 if (collect_pad->track->xiph_headers_to_skip > 0) {
3200 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3201 gst_buffer_unref (buf);
3202 --collect_pad->track->xiph_headers_to_skip;
3206 /* for dirac we have to queue up everything up to a picture unit */
3207 if (collect_pad->track->codec_id != NULL &&
3208 strcmp (collect_pad->track->codec_id,
3209 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
3210 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3215 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3216 * this would wreak havoc with time stored in matroska file */
3217 /* TODO: maybe calculate a timestamp by using the previous timestamp
3218 * and default duration */
3219 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3220 GST_WARNING_OBJECT (collect_pad->collect.pad,
3221 "Invalid buffer timestamp; dropping buffer");
3222 gst_buffer_unref (buf);
3226 /* set the timestamp for outgoing buffers */
3227 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
3229 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
3230 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3231 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3232 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
3233 is_video_keyframe = TRUE;
3237 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3238 * or when we may be reaching the limit of the relative timestamp */
3239 if (mux->cluster_time +
3240 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
3241 || is_video_keyframe || mux->force_key_unit_event) {
3242 if (!mux->streamable)
3243 gst_ebml_write_master_finish (ebml, mux->cluster);
3245 /* Forward the GstForceKeyUnit event after finishing the cluster */
3246 if (mux->force_key_unit_event) {
3247 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3248 mux->force_key_unit_event = NULL;
3251 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3252 mux->cluster_pos = ebml->pos;
3253 gst_ebml_write_set_cache (ebml, 0x20);
3255 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3256 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3257 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3259 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3260 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
3262 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3263 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3264 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3265 mux->prev_cluster_size);
3270 mux->cluster_pos = ebml->pos;
3271 gst_ebml_write_set_cache (ebml, 0x20);
3272 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3273 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3274 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
3275 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
3276 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3279 /* update duration of this track */
3280 if (GST_BUFFER_DURATION_IS_VALID (buf))
3281 collect_pad->duration += GST_BUFFER_DURATION (buf);
3283 /* We currently write index entries for all video tracks or for the audio
3284 * track in a single-track audio file. This could be improved by keeping the
3285 * index only for the *first* video track. */
3287 /* TODO: index is useful for every track, should contain the number of
3288 * the block in the cluster which contains the timestamp, should also work
3289 * for files with multiple audio tracks.
3291 if (!mux->streamable &&
3292 (is_video_keyframe ||
3293 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3294 (mux->num_streams == 1)))) {
3297 if (mux->min_index_interval != 0) {
3298 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3299 if (mux->index[last_idx].track == collect_pad->track->num)
3304 if (last_idx < 0 || mux->min_index_interval == 0 ||
3305 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3306 >= mux->min_index_interval)) {
3307 GstMatroskaIndex *idx;
3309 if (mux->num_indexes % 32 == 0) {
3310 mux->index = g_renew (GstMatroskaIndex, mux->index,
3311 mux->num_indexes + 32);
3313 idx = &mux->index[mux->num_indexes++];
3315 idx->pos = mux->cluster_pos;
3316 idx->time = GST_BUFFER_TIMESTAMP (buf);
3317 idx->track = collect_pad->track->num;
3321 /* Check if the duration differs from the default duration. */
3322 write_duration = FALSE;
3324 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3325 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3326 1, mux->time_scale);
3328 /* small difference should be ok. */
3329 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3330 block_duration < collect_pad->default_duration_scaled - 1) {
3331 write_duration = TRUE;
3335 /* write the block, for doctype v2 use SimpleBlock if possible
3336 * one slice (*breath*).
3337 * FIXME: Need to do correct lacing! */
3338 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3339 if (relative_timestamp64 >= 0) {
3340 /* round the timestamp */
3341 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3343 /* round the timestamp */
3344 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3346 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3348 if (mux->doctype_version > 1 && !write_duration) {
3350 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3353 gst_matroska_mux_create_buffer_header (collect_pad->track,
3354 relative_timestamp, flags);
3355 gst_ebml_write_set_cache (ebml, 0x40);
3356 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3357 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3358 gst_ebml_write_buffer (ebml, hdr);
3359 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3360 gst_ebml_write_buffer (ebml, buf);
3362 return gst_ebml_last_write_result (ebml);
3364 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
3365 /* write and call order slightly unnatural,
3366 * but avoids seek and minizes pushing */
3367 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3369 gst_matroska_mux_create_buffer_header (collect_pad->track,
3370 relative_timestamp, 0);
3372 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3373 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3374 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3375 gst_ebml_write_buffer (ebml, hdr);
3376 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
3377 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3378 gst_ebml_write_buffer (ebml, buf);
3380 return gst_ebml_last_write_result (ebml);
3385 * gst_matroska_mux_handle_buffer:
3386 * @pads: #GstCollectPads2
3387 * @uuser_data: #GstMatroskaMux
3389 * Collectpads callback.
3391 * Returns: #GstFlowReturn
3393 static GstFlowReturn
3394 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3395 GstBuffer * buf, gpointer user_data)
3397 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3398 GstEbmlWrite *ebml = mux->ebml_write;
3399 GstMatroskaPad *best;
3400 GstFlowReturn ret = GST_FLOW_OK;
3402 GST_DEBUG_OBJECT (mux, "Collected pads");
3404 /* start with a header */
3405 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3406 if (mux->collect->data == NULL) {
3407 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3408 ("No input streams configured"));
3409 return GST_FLOW_ERROR;
3411 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3412 gst_ebml_start_streamheader (ebml);
3413 gst_matroska_mux_start (mux);
3414 gst_matroska_mux_stop_streamheader (mux);
3415 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3418 /* provided with stream to write from */
3419 best = (GstMatroskaPad *) data;
3421 /* if there is no best pad, we have reached EOS */
3423 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3424 if (!mux->streamable) {
3425 gst_matroska_mux_finish (mux);
3427 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3429 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3430 ret = GST_FLOW_UNEXPECTED;
3434 /* if we have a best stream, should also have a buffer */
3437 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3438 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3439 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3440 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3442 /* make note of first and last encountered timestamps, so we can calculate
3443 * the actual duration later when we send an updated header on eos */
3444 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3445 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3446 GstClockTime end_ts = start_ts;
3448 if (GST_BUFFER_DURATION_IS_VALID (buf))
3449 end_ts += GST_BUFFER_DURATION (buf);
3450 else if (best->track->default_duration)
3451 end_ts += best->track->default_duration;
3453 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3454 best->end_ts = end_ts;
3456 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3457 start_ts < best->start_ts))
3458 best->start_ts = start_ts;
3461 /* write one buffer */
3462 ret = gst_matroska_mux_write_data (mux, best, buf);
3470 * gst_matroska_mux_change_state:
3471 * @element: #GstMatroskaMux
3472 * @transition: State change transition.
3474 * Change the muxer state.
3476 * Returns: #GstStateChangeReturn
3478 static GstStateChangeReturn
3479 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3481 GstStateChangeReturn ret;
3482 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3484 switch (transition) {
3485 case GST_STATE_CHANGE_NULL_TO_READY:
3487 case GST_STATE_CHANGE_READY_TO_PAUSED:
3488 gst_collect_pads2_start (mux->collect);
3490 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3492 case GST_STATE_CHANGE_PAUSED_TO_READY:
3493 gst_collect_pads2_stop (mux->collect);
3499 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3501 switch (transition) {
3502 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3504 case GST_STATE_CHANGE_PAUSED_TO_READY:
3505 gst_matroska_mux_reset (GST_ELEMENT (mux));
3507 case GST_STATE_CHANGE_READY_TO_NULL:
3517 gst_matroska_mux_set_property (GObject * object,
3518 guint prop_id, const GValue * value, GParamSpec * pspec)
3520 GstMatroskaMux *mux;
3522 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3523 mux = GST_MATROSKA_MUX (object);
3526 case ARG_WRITING_APP:
3527 if (!g_value_get_string (value)) {
3528 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3531 g_free (mux->writing_app);
3532 mux->writing_app = g_value_dup_string (value);
3534 case ARG_DOCTYPE_VERSION:
3535 mux->doctype_version = g_value_get_int (value);
3537 case ARG_MIN_INDEX_INTERVAL:
3538 mux->min_index_interval = g_value_get_int64 (value);
3540 case ARG_STREAMABLE:
3541 mux->streamable = g_value_get_boolean (value);
3544 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3550 gst_matroska_mux_get_property (GObject * object,
3551 guint prop_id, GValue * value, GParamSpec * pspec)
3553 GstMatroskaMux *mux;
3555 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3556 mux = GST_MATROSKA_MUX (object);
3559 case ARG_WRITING_APP:
3560 g_value_set_string (value, mux->writing_app);
3562 case ARG_DOCTYPE_VERSION:
3563 g_value_set_int (value, mux->doctype_version);
3565 case ARG_MIN_INDEX_INTERVAL:
3566 g_value_set_int64 (value, mux->min_index_interval);
3568 case ARG_STREAMABLE:
3569 g_value_set_boolean (value, mux->streamable);
3572 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);