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 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
60 #define GST_CAT_DEFAULT matroskamux_debug
67 ARG_MIN_INDEX_INTERVAL,
71 #define DEFAULT_DOCTYPE_VERSION 2
72 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
73 #define DEFAULT_MIN_INDEX_INTERVAL 0
74 #define DEFAULT_STREAMABLE FALSE
76 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
77 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
79 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
82 GST_STATIC_CAPS ("video/x-matroska")
85 #define COMMON_VIDEO_CAPS \
86 "width = (int) [ 16, 4096 ], " \
87 "height = (int) [ 16, 4096 ], " \
88 "framerate = (fraction) [ 0, MAX ]"
90 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
91 "width = (int) [ 16, 4096 ], " \
92 "height = (int) [ 16, 4096 ] "
95 * * require codec data, etc as needed
98 static GstStaticPadTemplate videosink_templ =
99 GST_STATIC_PAD_TEMPLATE ("video_%d",
102 GST_STATIC_CAPS ("video/mpeg, "
103 "mpegversion = (int) { 1, 2, 4 }, "
104 "systemstream = (boolean) false, "
105 COMMON_VIDEO_CAPS "; "
106 "video/x-h264, stream-format=avc, alignment=au, "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS "; "
119 COMMON_VIDEO_CAPS "; "
121 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
124 COMMON_VIDEO_CAPS "; "
125 "video/x-pn-realvideo, "
126 "rmversion = (int) [1, 4], "
127 COMMON_VIDEO_CAPS "; "
129 COMMON_VIDEO_CAPS "; "
131 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
132 COMMON_VIDEO_CAPS "; "
133 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
136 #define COMMON_AUDIO_CAPS \
137 "channels = (int) [ 1, MAX ], " \
138 "rate = (int) [ 1, MAX ]"
141 * * require codec data, etc as needed
143 static GstStaticPadTemplate audiosink_templ =
144 GST_STATIC_PAD_TEMPLATE ("audio_%d",
147 GST_STATIC_CAPS ("audio/mpeg, "
148 "mpegversion = (int) 1, "
149 "layer = (int) [ 1, 3 ], "
150 COMMON_AUDIO_CAPS "; "
152 "mpegversion = (int) { 2, 4 }, "
153 "stream-format = (string) raw, "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
160 COMMON_AUDIO_CAPS "; "
162 COMMON_AUDIO_CAPS "; "
164 COMMON_AUDIO_CAPS "; "
166 COMMON_AUDIO_CAPS "; "
170 "signed = (boolean) false, "
171 COMMON_AUDIO_CAPS ";"
175 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
176 "signed = (boolean) true, "
177 COMMON_AUDIO_CAPS ";"
181 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
182 "signed = (boolean) true, "
183 COMMON_AUDIO_CAPS ";"
187 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
188 "signed = (boolean) true, "
189 COMMON_AUDIO_CAPS ";"
190 "audio/x-raw-float, "
191 "width = (int) [ 32, 64 ], "
192 "endianness = (int) LITTLE_ENDIAN, "
193 COMMON_AUDIO_CAPS ";"
195 "width = (int) { 8, 16, 24 }, "
196 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
197 "audio/x-pn-realaudio, "
198 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
199 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
200 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
201 COMMON_AUDIO_CAPS ";"
203 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
205 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]")
208 static GstStaticPadTemplate subtitlesink_templ =
209 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
212 GST_STATIC_CAPS ("subtitle/x-kate; "
213 "text/plain; application/x-ssa; application/x-ass; "
214 "application/x-usf; video/x-dvd-subpicture; "
215 "application/x-subtitle-unknown")
218 static GArray *used_uids;
219 G_LOCK_DEFINE_STATIC (used_uids);
221 static void gst_matroska_mux_add_interfaces (GType type);
223 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
224 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
226 /* Matroska muxer destructor */
227 static void gst_matroska_mux_finalize (GObject * object);
229 /* Pads collected callback */
230 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads2 * pads,
231 GstCollectData2 * data, GstBuffer * buf, gpointer user_data);
232 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
233 GstCollectData2 * data, GstEvent * event, gpointer user_data);
236 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
238 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
239 GstPadTemplate * templ, const gchar * name);
240 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
242 /* gst internal change state handler */
243 static GstStateChangeReturn
244 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
246 /* gobject bla bla */
247 static void gst_matroska_mux_set_property (GObject * object,
248 guint prop_id, const GValue * value, GParamSpec * pspec);
249 static void gst_matroska_mux_get_property (GObject * object,
250 guint prop_id, GValue * value, GParamSpec * pspec);
253 static void gst_matroska_mux_reset (GstElement * element);
256 static guint64 gst_matroska_mux_create_uid ();
258 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
259 GstMatroskaTrackContext * context);
260 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
261 GstMatroskaTrackContext * context);
262 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
263 GstMatroskaTrackContext * context);
264 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
265 GstMatroskaTrackContext * context);
266 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
267 GstMatroskaTrackContext * context);
269 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
273 gst_matroska_mux_add_interfaces (GType type)
275 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
277 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
281 gst_matroska_mux_base_init (gpointer g_class)
286 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
288 GObjectClass *gobject_class;
289 GstElementClass *gstelement_class;
291 gobject_class = (GObjectClass *) klass;
292 gstelement_class = (GstElementClass *) klass;
294 gst_element_class_add_static_pad_template (gstelement_class,
296 gst_element_class_add_static_pad_template (gstelement_class,
298 gst_element_class_add_static_pad_template (gstelement_class,
299 &subtitlesink_templ);
300 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
301 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
303 "Muxes video/audio/subtitle streams into a matroska stream",
304 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
306 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
309 gobject_class->finalize = gst_matroska_mux_finalize;
311 gobject_class->get_property = gst_matroska_mux_get_property;
312 gobject_class->set_property = gst_matroska_mux_set_property;
314 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
315 g_param_spec_string ("writing-app", "Writing application.",
316 "The name the application that creates the matroska file.",
317 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
318 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
319 g_param_spec_int ("version", "DocType version",
320 "This parameter determines what Matroska features can be used.",
321 1, 2, DEFAULT_DOCTYPE_VERSION,
322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
323 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
324 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
325 "entries", "An index entry is created every so many nanoseconds.",
326 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
327 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
328 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
329 g_param_spec_boolean ("streamable", "Determines whether output should "
330 "be streamable", "If set to true, the output should be as if it is "
331 "to be streamed and hence no indexes written or duration written.",
333 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
335 gstelement_class->change_state =
336 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
337 gstelement_class->request_new_pad =
338 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
339 gstelement_class->release_pad =
340 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
344 * Start of pad option handler code
346 #define DEFAULT_PAD_FRAME_DURATION TRUE
347 #define DEFAULT_PAD_FRAME_DURATION_VP8 FALSE
352 PROP_PAD_FRAME_DURATION
358 gboolean frame_duration;
359 gboolean frame_duration_user;
362 static void gst_matroskamux_pad_class_init (GstPadClass * klass);
365 gst_matroskamux_pad_get_type (void)
367 static GType type = 0;
369 if (G_UNLIKELY (type == 0)) {
370 type = g_type_register_static_simple (GST_TYPE_PAD,
371 g_intern_static_string ("GstMatroskamuxPad"), sizeof (GstPadClass),
372 (GClassInitFunc) gst_matroskamux_pad_class_init,
373 sizeof (GstMatroskamuxPad), NULL, 0);
378 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
379 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
380 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
381 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
384 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
385 GValue * value, GParamSpec * pspec)
387 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
390 case PROP_PAD_FRAME_DURATION:
391 g_value_set_boolean (value, pad->frame_duration);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
401 const GValue * value, GParamSpec * pspec)
403 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
406 case PROP_PAD_FRAME_DURATION:
407 pad->frame_duration = g_value_get_boolean (value);
408 pad->frame_duration_user = TRUE;
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 gst_matroskamux_pad_class_init (GstPadClass * klass)
419 GObjectClass *gobject_class = (GObjectClass *) klass;
421 gobject_class->set_property = gst_matroskamux_pad_set_property;
422 gobject_class->get_property = gst_matroskamux_pad_get_property;
424 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
425 g_param_spec_boolean ("frame-duration", "Frame duration",
426 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
427 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
431 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
433 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
434 pad->frame_duration_user = FALSE;
438 * End of pad option handler code
442 * gst_matroska_mux_init:
443 * @mux: #GstMatroskaMux that should be initialized.
444 * @g_class: Class of the muxer.
446 * Matroska muxer constructor.
449 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
451 GstPadTemplate *templ;
454 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
455 mux->srcpad = gst_pad_new_from_template (templ, "src");
457 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
458 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
460 mux->collect = gst_collect_pads2_new ();
461 gst_collect_pads2_set_clip_function (mux->collect,
462 GST_DEBUG_FUNCPTR (gst_collect_pads2_clip_running_time), mux);
463 gst_collect_pads2_set_buffer_function (mux->collect,
464 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
465 gst_collect_pads2_set_event_function (mux->collect,
466 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
468 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
469 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
471 /* property defaults */
472 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
473 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
474 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
475 mux->streamable = DEFAULT_STREAMABLE;
477 /* initialize internal variables */
479 mux->num_streams = 0;
480 mux->num_a_streams = 0;
481 mux->num_t_streams = 0;
482 mux->num_v_streams = 0;
484 /* initialize remaining variables */
485 gst_matroska_mux_reset (GST_ELEMENT (mux));
490 * gst_matroska_mux_finalize:
491 * @object: #GstMatroskaMux that should be finalized.
493 * Finalize matroska muxer.
496 gst_matroska_mux_finalize (GObject * object)
498 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
500 gst_event_replace (&mux->force_key_unit_event, NULL);
502 gst_object_unref (mux->collect);
503 gst_object_unref (mux->ebml_write);
504 if (mux->writing_app)
505 g_free (mux->writing_app);
507 G_OBJECT_CLASS (parent_class)->finalize (object);
512 * gst_matroska_mux_create_uid:
514 * Generate new unused track UID.
516 * Returns: New track UID.
519 gst_matroska_mux_create_uid (void)
526 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
531 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
532 for (i = 0; i < used_uids->len; i++) {
533 if (g_array_index (used_uids, guint64, i) == uid) {
538 g_array_append_val (used_uids, uid);
541 G_UNLOCK (used_uids);
547 * gst_matroska_pad_reset:
548 * @collect_pad: the #GstMatroskaPad
550 * Reset and/or release resources of a matroska collect pad.
553 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
556 GstMatroskaTrackType type = 0;
558 /* free track information */
559 if (collect_pad->track != NULL) {
560 /* retrieve for optional later use */
561 name = collect_pad->track->name;
562 type = collect_pad->track->type;
563 /* extra for video */
564 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
565 GstMatroskaTrackVideoContext *ctx =
566 (GstMatroskaTrackVideoContext *) collect_pad->track;
568 if (ctx->dirac_unit) {
569 gst_buffer_unref (ctx->dirac_unit);
570 ctx->dirac_unit = NULL;
573 g_free (collect_pad->track->codec_id);
574 g_free (collect_pad->track->codec_name);
576 g_free (collect_pad->track->name);
577 g_free (collect_pad->track->language);
578 g_free (collect_pad->track->codec_priv);
579 g_free (collect_pad->track);
580 collect_pad->track = NULL;
583 if (!full && type != 0) {
584 GstMatroskaTrackContext *context;
586 /* create a fresh context */
588 case GST_MATROSKA_TRACK_TYPE_VIDEO:
589 context = (GstMatroskaTrackContext *)
590 g_new0 (GstMatroskaTrackVideoContext, 1);
592 case GST_MATROSKA_TRACK_TYPE_AUDIO:
593 context = (GstMatroskaTrackContext *)
594 g_new0 (GstMatroskaTrackAudioContext, 1);
596 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
597 context = (GstMatroskaTrackContext *)
598 g_new0 (GstMatroskaTrackSubtitleContext, 1);
601 g_assert_not_reached ();
605 context->type = type;
606 context->name = name;
607 /* TODO: check default values for the context */
608 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
609 collect_pad->track = context;
610 collect_pad->duration = 0;
611 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
612 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
617 * gst_matroska_pad_free:
618 * @collect_pad: the #GstMatroskaPad
620 * Release resources of a matroska collect pad.
623 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
625 gst_matroska_pad_reset (collect_pad, TRUE);
630 * gst_matroska_mux_reset:
631 * @element: #GstMatroskaMux that should be reseted.
633 * Reset matroska muxer back to initial state.
636 gst_matroska_mux_reset (GstElement * element)
638 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
641 /* reset EBML write */
642 gst_ebml_write_reset (mux->ebml_write);
645 mux->state = GST_MATROSKA_MUX_STATE_START;
647 /* clean up existing streams */
649 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
650 GstMatroskaPad *collect_pad;
652 collect_pad = (GstMatroskaPad *) walk->data;
654 /* reset collect pad to pristine state */
655 gst_matroska_pad_reset (collect_pad, FALSE);
659 mux->num_indexes = 0;
664 mux->time_scale = GST_MSECOND;
665 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
670 mux->cluster_time = 0;
671 mux->cluster_pos = 0;
672 mux->prev_cluster_size = 0;
675 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
679 * gst_matroska_mux_handle_src_event:
680 * @pad: Pad which received the event.
681 * @event: Received event.
683 * handle events - copied from oggmux without understanding
685 * Returns: #TRUE on success.
688 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
692 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
696 /* disable seeking for now */
702 return gst_pad_event_default (pad, event);
706 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
716 /* produce comma-separated list in hex format */
717 for (i = 0; i < 16; ++i) {
719 /* replicate vobsub's slightly off RGB conversion calculation */
720 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
721 u = ((col >> 8) & 0xff) - 128;
722 v = (col & 0xff) - 128;
723 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
724 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
725 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
726 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
729 sclut = g_strjoinv (",", clutv);
731 /* build codec private; only palette for now */
732 g_free (context->codec_priv);
733 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
734 /* include terminating 0 */
735 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
737 for (i = 0; i < 16; ++i) {
744 * gst_matroska_mux_handle_sink_event:
745 * @pad: Pad which received the event.
746 * @event: Received event.
748 * handle events - informational ones like tags
750 * Returns: #TRUE on success.
753 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
754 GstCollectData2 * data, GstEvent * event, gpointer user_data)
756 GstMatroskaTrackContext *context;
757 GstMatroskaPad *collect_pad;
762 mux = GST_MATROSKA_MUX (user_data);
763 collect_pad = (GstMatroskaPad *) data;
765 context = collect_pad->track;
768 switch (GST_EVENT_TYPE (event)) {
772 GST_DEBUG_OBJECT (mux, "received tag event");
773 gst_event_parse_tag (event, &list);
775 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
776 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
777 const gchar *lang_code;
779 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
781 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
782 context->language = g_strdup (lang_code);
784 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
789 /* FIXME: what about stream-specific tags? */
790 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
791 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
793 gst_event_unref (event);
794 /* handled this, don't want collectpads to forward it downstream */
798 case GST_EVENT_NEWSEGMENT:{
801 gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
803 if (format != GST_FORMAT_TIME) {
804 gst_event_unref (event);
809 case GST_EVENT_CUSTOM_DOWNSTREAM:{
810 const GstStructure *structure;
812 structure = gst_event_get_structure (event);
813 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
814 gst_event_replace (&mux->force_key_unit_event, NULL);
815 mux->force_key_unit_event = event;
817 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
818 !strcmp ("dvd-spu-clut-change",
819 gst_structure_get_string (structure, "event"))) {
824 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
825 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
826 GST_DEBUG_OBJECT (pad, "... discarding");
829 /* first transform event data into table form */
830 for (i = 0; i < 16; i++) {
831 g_snprintf (name, sizeof (name), "clut%02d", i);
832 if (!gst_structure_get_int (structure, name, &value)) {
833 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
834 "contain %s field", name);
840 /* transform into private data for stream; text form */
841 gst_matroska_mux_build_vobsub_private (context, clut);
849 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
858 * gst_matroska_mux_video_pad_setcaps:
859 * @pad: Pad which got the caps.
862 * Setcaps function for video sink pad.
864 * Returns: #TRUE on success.
867 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
869 GstMatroskaTrackContext *context = NULL;
870 GstMatroskaTrackVideoContext *videocontext;
872 GstMatroskaPad *collect_pad;
873 GstStructure *structure;
874 const gchar *mimetype;
875 const GValue *value = NULL;
876 const GstBuffer *codec_buf = NULL;
877 gint width, height, pixel_width, pixel_height;
879 gboolean interlaced = FALSE;
881 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
884 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
885 g_assert (collect_pad);
886 context = collect_pad->track;
888 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
889 videocontext = (GstMatroskaTrackVideoContext *) context;
891 /* gst -> matroska ID'ing */
892 structure = gst_caps_get_structure (caps, 0);
894 mimetype = gst_structure_get_name (structure);
896 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
898 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
900 if (!strcmp (mimetype, "video/x-theora")) {
901 /* we'll extract the details later from the theora identification header */
905 /* get general properties */
906 /* spec says it is mandatory */
907 if (!gst_structure_get_int (structure, "width", &width) ||
908 !gst_structure_get_int (structure, "height", &height))
911 videocontext->pixel_width = width;
912 videocontext->pixel_height = height;
914 /* set vp8 defaults or let user override it */
915 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
916 && (!strcmp (mimetype, "video/x-vp8")))
917 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
918 DEFAULT_PAD_FRAME_DURATION_VP8;
920 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
921 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
923 context->default_duration =
924 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
925 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
926 GST_TIME_ARGS (context->default_duration));
928 context->default_duration = 0;
930 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
931 &pixel_width, &pixel_height)) {
932 if (pixel_width > pixel_height) {
933 videocontext->display_width = width * pixel_width / pixel_height;
934 videocontext->display_height = height;
935 } else if (pixel_width < pixel_height) {
936 videocontext->display_width = width;
937 videocontext->display_height = height * pixel_height / pixel_width;
939 videocontext->display_width = 0;
940 videocontext->display_height = 0;
943 videocontext->display_width = 0;
944 videocontext->display_height = 0;
949 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
950 videocontext->fourcc = 0;
952 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
953 * data and other settings
957 /* extract codec_data, may turn out needed */
958 value = gst_structure_get_value (structure, "codec_data");
960 codec_buf = gst_value_get_buffer (value);
963 if (!strcmp (mimetype, "video/x-raw-yuv")) {
964 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
965 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
966 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
967 ||!strcmp (mimetype, "video/x-huffyuv")
968 || !strcmp (mimetype, "video/x-divx")
969 || !strcmp (mimetype, "video/x-dv")
970 || !strcmp (mimetype, "video/x-h263")
971 || !strcmp (mimetype, "video/x-msmpeg")
972 || !strcmp (mimetype, "video/x-wmv")
973 || !strcmp (mimetype, "image/jpeg")) {
974 gst_riff_strf_vids *bih;
975 gint size = sizeof (gst_riff_strf_vids);
978 if (!strcmp (mimetype, "video/x-xvid"))
979 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
980 else if (!strcmp (mimetype, "video/x-huffyuv"))
981 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
982 else if (!strcmp (mimetype, "video/x-dv"))
983 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
984 else if (!strcmp (mimetype, "video/x-h263"))
985 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
986 else if (!strcmp (mimetype, "video/x-divx")) {
989 gst_structure_get_int (structure, "divxversion", &divxversion);
990 switch (divxversion) {
992 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
995 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
998 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1001 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1004 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1005 switch (msmpegversion) {
1007 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1010 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1016 } else if (!strcmp (mimetype, "video/x-wmv")) {
1019 if (gst_structure_get_fourcc (structure, "format", &format)) {
1021 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1022 if (wmvversion == 2) {
1023 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1024 } else if (wmvversion == 1) {
1025 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1026 } else if (wmvversion == 3) {
1027 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1030 } else if (!strcmp (mimetype, "image/jpeg")) {
1031 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1037 bih = g_new0 (gst_riff_strf_vids, 1);
1038 GST_WRITE_UINT32_LE (&bih->size, size);
1039 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1040 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1041 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1042 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1043 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1044 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1045 videocontext->pixel_height * 3);
1047 /* process codec private/initialization data, if any */
1049 size += GST_BUFFER_SIZE (codec_buf);
1050 bih = g_realloc (bih, size);
1051 GST_WRITE_UINT32_LE (&bih->size, size);
1052 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
1053 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
1056 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1057 context->codec_priv = (gpointer) bih;
1058 context->codec_priv_size = size;
1059 } else if (!strcmp (mimetype, "video/x-h264")) {
1060 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1062 if (context->codec_priv != NULL) {
1063 g_free (context->codec_priv);
1064 context->codec_priv = NULL;
1065 context->codec_priv_size = 0;
1068 /* Create avcC header */
1069 if (codec_buf != NULL) {
1070 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1071 context->codec_priv = g_malloc0 (context->codec_priv_size);
1072 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1073 context->codec_priv_size);
1075 } else if (!strcmp (mimetype, "video/x-theora")) {
1076 const GValue *streamheader;
1078 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1080 if (context->codec_priv != NULL) {
1081 g_free (context->codec_priv);
1082 context->codec_priv = NULL;
1083 context->codec_priv_size = 0;
1086 streamheader = gst_structure_get_value (structure, "streamheader");
1087 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1088 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1089 ("theora stream headers missing or malformed"));
1092 } else if (!strcmp (mimetype, "video/x-dirac")) {
1093 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1094 } else if (!strcmp (mimetype, "video/x-vp8")) {
1095 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1096 } else if (!strcmp (mimetype, "video/mpeg")) {
1099 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1100 switch (mpegversion) {
1102 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1105 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1108 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1114 /* global headers may be in codec data */
1115 if (codec_buf != NULL) {
1116 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1117 context->codec_priv = g_malloc0 (context->codec_priv_size);
1118 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1119 context->codec_priv_size);
1121 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1123 /* can only make it here if preceding case verified it was version 3 */
1124 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1125 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1127 const GValue *mdpr_data;
1129 gst_structure_get_int (structure, "rmversion", &rmversion);
1130 switch (rmversion) {
1132 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1135 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1138 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1141 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1147 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1148 if (mdpr_data != NULL) {
1149 guint8 *priv_data = NULL;
1150 guint priv_data_size = 0;
1152 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1154 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1155 priv_data = g_malloc0 (priv_data_size);
1157 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1159 context->codec_priv = priv_data;
1160 context->codec_priv_size = priv_data_size;
1169 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1170 GST_PAD_NAME (pad), caps);
1175 /* N > 0 to expect a particular number of headers, negative if the
1176 number of headers is variable */
1178 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1179 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1181 GstBuffer **buf = NULL;
1184 guint bufi, i, offset, priv_data_size;
1186 if (streamheader == NULL)
1187 goto no_stream_headers;
1189 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1192 bufarr = g_value_peek_pointer (streamheader);
1193 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1195 if (N > 0 && bufarr->len != N)
1198 context->xiph_headers_to_skip = bufarr->len;
1200 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1201 for (i = 0; i < bufarr->len; i++) {
1202 GValue *bufval = &g_array_index (bufarr, GValue, i);
1204 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1206 goto wrong_content_type;
1209 buf[i] = g_value_peek_pointer (bufval);
1213 if (bufarr->len > 0) {
1214 for (i = 0; i < bufarr->len - 1; i++) {
1215 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1219 for (i = 0; i < bufarr->len; ++i) {
1220 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1223 priv_data = g_malloc0 (priv_data_size);
1225 priv_data[0] = bufarr->len - 1;
1228 if (bufarr->len > 0) {
1229 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1230 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1231 priv_data[offset++] = 0xff;
1233 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1237 for (i = 0; i < bufarr->len; ++i) {
1238 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1239 GST_BUFFER_SIZE (buf[i]));
1240 offset += GST_BUFFER_SIZE (buf[i]);
1243 context->codec_priv = priv_data;
1244 context->codec_priv_size = priv_data_size;
1247 *p_buf0 = gst_buffer_ref (buf[0]);
1256 GST_WARNING ("required streamheaders missing in sink caps!");
1261 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1262 G_VALUE_TYPE_NAME (streamheader));
1267 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1272 GST_WARNING ("streamheaders array does not contain GstBuffers");
1278 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1279 GstMatroskaTrackContext * context)
1281 GstBuffer *buf0 = NULL;
1283 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1286 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1287 GST_WARNING ("First vorbis header too small, ignoring");
1289 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1290 GstMatroskaTrackAudioContext *audiocontext;
1293 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1294 audiocontext = (GstMatroskaTrackAudioContext *) context;
1295 audiocontext->channels = GST_READ_UINT8 (hdr);
1296 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1301 gst_buffer_unref (buf0);
1307 theora_streamheader_to_codecdata (const GValue * streamheader,
1308 GstMatroskaTrackContext * context)
1310 GstBuffer *buf0 = NULL;
1312 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1315 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1316 GST_WARNING ("First theora header too small, ignoring");
1317 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1318 GST_WARNING ("First header not a theora identification header, ignoring");
1320 GstMatroskaTrackVideoContext *videocontext;
1321 guint fps_num, fps_denom, par_num, par_denom;
1324 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1326 videocontext = (GstMatroskaTrackVideoContext *) context;
1327 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1328 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1329 hdr += 3 + 3 + 1 + 1;
1330 fps_num = GST_READ_UINT32_BE (hdr);
1331 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1332 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1333 fps_denom, fps_num);
1335 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1336 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1337 if (par_num > 0 && par_num > 0) {
1338 if (par_num > par_denom) {
1339 videocontext->display_width =
1340 videocontext->pixel_width * par_num / par_denom;
1341 videocontext->display_height = videocontext->pixel_height;
1342 } else if (par_num < par_denom) {
1343 videocontext->display_width = videocontext->pixel_width;
1344 videocontext->display_height =
1345 videocontext->pixel_height * par_denom / par_num;
1347 videocontext->display_width = 0;
1348 videocontext->display_height = 0;
1351 videocontext->display_width = 0;
1352 videocontext->display_height = 0;
1358 gst_buffer_unref (buf0);
1364 kate_streamheader_to_codecdata (const GValue * streamheader,
1365 GstMatroskaTrackContext * context)
1367 GstBuffer *buf0 = NULL;
1369 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1372 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1373 GST_WARNING ("First kate header too small, ignoring");
1374 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1375 GST_WARNING ("First header not a kate identification header, ignoring");
1379 gst_buffer_unref (buf0);
1385 flac_streamheader_to_codecdata (const GValue * streamheader,
1386 GstMatroskaTrackContext * context)
1393 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1394 GST_WARNING ("No or invalid streamheader field in the caps");
1398 bufarr = g_value_peek_pointer (streamheader);
1399 if (bufarr->len < 2) {
1400 GST_WARNING ("Too few headers in streamheader field");
1404 context->xiph_headers_to_skip = bufarr->len + 1;
1406 bufval = &g_array_index (bufarr, GValue, 0);
1407 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1408 GST_WARNING ("streamheaders array does not contain GstBuffers");
1412 buffer = g_value_peek_pointer (bufval);
1414 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1415 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1416 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1417 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1418 GST_WARNING ("Invalid streamheader for FLAC");
1422 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1423 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1424 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1425 GST_BUFFER_SIZE (buffer) - 9);
1427 for (i = 1; i < bufarr->len; i++) {
1428 bufval = &g_array_index (bufarr, GValue, i);
1430 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1431 g_free (context->codec_priv);
1432 context->codec_priv = NULL;
1433 context->codec_priv_size = 0;
1434 GST_WARNING ("streamheaders array does not contain GstBuffers");
1438 buffer = g_value_peek_pointer (bufval);
1440 context->codec_priv =
1441 g_realloc (context->codec_priv,
1442 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1443 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1444 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1445 context->codec_priv_size =
1446 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1453 speex_streamheader_to_codecdata (const GValue * streamheader,
1454 GstMatroskaTrackContext * context)
1460 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1461 GST_WARNING ("No or invalid streamheader field in the caps");
1465 bufarr = g_value_peek_pointer (streamheader);
1466 if (bufarr->len != 2) {
1467 GST_WARNING ("Too few headers in streamheader field");
1471 context->xiph_headers_to_skip = bufarr->len + 1;
1473 bufval = &g_array_index (bufarr, GValue, 0);
1474 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1475 GST_WARNING ("streamheaders array does not contain GstBuffers");
1479 buffer = g_value_peek_pointer (bufval);
1481 if (GST_BUFFER_SIZE (buffer) < 80
1482 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1483 GST_WARNING ("Invalid streamheader for Speex");
1487 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1488 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1489 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1490 GST_BUFFER_SIZE (buffer));
1492 bufval = &g_array_index (bufarr, GValue, 1);
1494 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1495 g_free (context->codec_priv);
1496 context->codec_priv = NULL;
1497 context->codec_priv_size = 0;
1498 GST_WARNING ("streamheaders array does not contain GstBuffers");
1502 buffer = g_value_peek_pointer (bufval);
1504 context->codec_priv =
1505 g_realloc (context->codec_priv,
1506 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1507 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1508 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1509 context->codec_priv_size =
1510 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1515 static const gchar *
1516 aac_codec_data_to_codec_id (const GstBuffer * buf)
1518 const gchar *result;
1521 /* default to MAIN */
1524 if (GST_BUFFER_SIZE (buf) >= 2) {
1525 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1543 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1552 * gst_matroska_mux_audio_pad_setcaps:
1553 * @pad: Pad which got the caps.
1556 * Setcaps function for audio sink pad.
1558 * Returns: #TRUE on success.
1561 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1563 GstMatroskaTrackContext *context = NULL;
1564 GstMatroskaTrackAudioContext *audiocontext;
1565 GstMatroskaMux *mux;
1566 GstMatroskaPad *collect_pad;
1567 const gchar *mimetype;
1568 gint samplerate = 0, channels = 0;
1569 GstStructure *structure;
1570 const GValue *codec_data = NULL;
1571 const GstBuffer *buf = NULL;
1572 const gchar *stream_format = NULL;
1574 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1577 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1578 g_assert (collect_pad);
1579 context = collect_pad->track;
1581 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1582 audiocontext = (GstMatroskaTrackAudioContext *) context;
1584 structure = gst_caps_get_structure (caps, 0);
1585 mimetype = gst_structure_get_name (structure);
1588 gst_structure_get_int (structure, "rate", &samplerate);
1589 gst_structure_get_int (structure, "channels", &channels);
1591 audiocontext->samplerate = samplerate;
1592 audiocontext->channels = channels;
1593 audiocontext->bitdepth = 0;
1594 context->default_duration = 0;
1596 codec_data = gst_structure_get_value (structure, "codec_data");
1598 buf = gst_value_get_buffer (codec_data);
1600 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1601 * data and other settings
1605 if (!strcmp (mimetype, "audio/mpeg")) {
1606 gint mpegversion = 0;
1608 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1609 switch (mpegversion) {
1615 gst_structure_get_int (structure, "layer", &layer);
1617 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1618 GST_WARNING_OBJECT (mux,
1619 "Unable to determine MPEG audio version, assuming 1");
1625 else if (layer == 2)
1627 else if (version == 2)
1632 context->default_duration =
1633 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1637 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1640 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1643 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1652 stream_format = gst_structure_get_string (structure, "stream-format");
1653 /* check this is raw aac */
1654 if (stream_format) {
1655 if (strcmp (stream_format, "raw") != 0) {
1656 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1660 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1665 if (mpegversion == 2)
1667 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1668 aac_codec_data_to_codec_id (buf));
1669 else if (mpegversion == 4)
1671 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1672 aac_codec_data_to_codec_id (buf));
1674 g_assert_not_reached ();
1676 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1683 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1685 gint endianness = G_LITTLE_ENDIAN;
1686 gboolean signedness = TRUE;
1688 if (!gst_structure_get_int (structure, "width", &width) ||
1689 !gst_structure_get_int (structure, "depth", &depth) ||
1690 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1691 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1696 !gst_structure_get_int (structure, "endianness", &endianness)) {
1697 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1701 if (width != depth) {
1702 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1706 /* FIXME: where is this spec'ed out? (tpm) */
1707 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1708 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1712 audiocontext->bitdepth = depth;
1713 if (endianness == G_BIG_ENDIAN)
1714 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1716 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1718 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1721 if (!gst_structure_get_int (structure, "width", &width)) {
1722 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1726 audiocontext->bitdepth = width;
1727 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1729 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1730 const GValue *streamheader;
1732 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1734 if (context->codec_priv != NULL) {
1735 g_free (context->codec_priv);
1736 context->codec_priv = NULL;
1737 context->codec_priv_size = 0;
1740 streamheader = gst_structure_get_value (structure, "streamheader");
1741 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1742 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1743 ("vorbis stream headers missing or malformed"));
1746 } else if (!strcmp (mimetype, "audio/x-flac")) {
1747 const GValue *streamheader;
1749 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1750 if (context->codec_priv != NULL) {
1751 g_free (context->codec_priv);
1752 context->codec_priv = NULL;
1753 context->codec_priv_size = 0;
1756 streamheader = gst_structure_get_value (structure, "streamheader");
1757 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1758 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1759 ("flac stream headers missing or malformed"));
1762 } else if (!strcmp (mimetype, "audio/x-speex")) {
1763 const GValue *streamheader;
1765 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1766 if (context->codec_priv != NULL) {
1767 g_free (context->codec_priv);
1768 context->codec_priv = NULL;
1769 context->codec_priv_size = 0;
1772 streamheader = gst_structure_get_value (structure, "streamheader");
1773 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1774 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1775 ("speex stream headers missing or malformed"));
1778 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1779 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1780 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1781 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1782 } else if (!strcmp (mimetype, "audio/x-dts")) {
1783 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1784 } else if (!strcmp (mimetype, "audio/x-tta")) {
1787 /* TTA frame duration */
1788 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1790 gst_structure_get_int (structure, "width", &width);
1791 audiocontext->bitdepth = width;
1792 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1794 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1796 const GValue *mdpr_data;
1798 gst_structure_get_int (structure, "raversion", &raversion);
1799 switch (raversion) {
1801 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1804 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1807 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1813 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1814 if (mdpr_data != NULL) {
1815 guint8 *priv_data = NULL;
1816 guint priv_data_size = 0;
1818 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1820 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1821 priv_data = g_malloc0 (priv_data_size);
1823 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1825 context->codec_priv = priv_data;
1826 context->codec_priv_size = priv_data_size;
1829 } else if (!strcmp (mimetype, "audio/x-wma")
1830 || !strcmp (mimetype, "audio/x-alaw")
1831 || !strcmp (mimetype, "audio/x-mulaw")) {
1833 guint codec_priv_size;
1838 if (samplerate == 0 || channels == 0) {
1839 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1843 if (!strcmp (mimetype, "audio/x-wma")) {
1847 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1848 || !gst_structure_get_int (structure, "block_align", &block_align)
1849 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1850 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1855 switch (wmaversion) {
1857 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1860 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1863 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1866 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1870 if (gst_structure_get_int (structure, "depth", &depth))
1871 audiocontext->bitdepth = depth;
1872 } else if (!strcmp (mimetype, "audio/x-alaw")
1873 || !strcmp (mimetype, "audio/x-mulaw")) {
1874 audiocontext->bitdepth = 8;
1875 if (!strcmp (mimetype, "audio/x-alaw"))
1876 format = GST_RIFF_WAVE_FORMAT_ALAW;
1878 format = GST_RIFF_WAVE_FORMAT_MULAW;
1880 block_align = channels;
1881 bitrate = block_align * samplerate;
1883 g_assert (format != 0);
1885 codec_priv_size = WAVEFORMATEX_SIZE;
1887 codec_priv_size += GST_BUFFER_SIZE (buf);
1889 /* serialize waveformatex structure */
1890 codec_priv = g_malloc0 (codec_priv_size);
1891 GST_WRITE_UINT16_LE (codec_priv, format);
1892 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1893 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1894 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1895 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1896 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1898 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1900 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1902 /* process codec private/initialization data, if any */
1904 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1905 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1909 context->codec_priv = (gpointer) codec_priv;
1910 context->codec_priv_size = codec_priv_size;
1918 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1919 GST_PAD_NAME (pad), caps);
1924 /* we probably don't have the data at start,
1925 * so have to reserve (a maximum) space to write this at the end.
1926 * bit spacy, but some formats can hold quite some */
1927 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1930 * gst_matroska_mux_subtitle_pad_setcaps:
1931 * @pad: Pad which got the caps.
1934 * Setcaps function for subtitle sink pad.
1936 * Returns: #TRUE on success.
1939 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1941 /* There is now (at least) one such alement (kateenc), and I'm going
1942 to handle it here and claim it works when it can be piped back
1943 through GStreamer and VLC */
1945 GstMatroskaTrackContext *context = NULL;
1946 GstMatroskaTrackSubtitleContext *scontext;
1947 GstMatroskaMux *mux;
1948 GstMatroskaPad *collect_pad;
1949 const gchar *mimetype;
1950 GstStructure *structure;
1951 const GValue *value = NULL;
1952 const GstBuffer *buf = NULL;
1954 gboolean ret = TRUE;
1956 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1959 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1960 g_assert (collect_pad);
1961 context = collect_pad->track;
1963 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1964 scontext = (GstMatroskaTrackSubtitleContext *) context;
1966 structure = gst_caps_get_structure (caps, 0);
1967 mimetype = gst_structure_get_name (structure);
1969 /* keep track of default set in request_pad */
1970 id = context->codec_id;
1973 scontext->check_utf8 = 1;
1974 scontext->invalid_utf8 = 0;
1975 context->default_duration = 0;
1977 if (!strcmp (mimetype, "subtitle/x-kate")) {
1978 const GValue *streamheader;
1980 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1982 if (context->codec_priv != NULL) {
1983 g_free (context->codec_priv);
1984 context->codec_priv = NULL;
1985 context->codec_priv_size = 0;
1988 streamheader = gst_structure_get_value (structure, "streamheader");
1989 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1990 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1991 ("kate stream headers missing or malformed"));
1995 } else if (!strcmp (mimetype, "text/plain")) {
1996 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
1997 } else if (!strcmp (mimetype, "application/x-ssa")) {
1998 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
1999 } else if (!strcmp (mimetype, "application/x-ass")) {
2000 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2001 } else if (!strcmp (mimetype, "application/x-usf")) {
2002 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2003 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2004 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2011 /* maybe some private data, e.g. vobsub */
2012 value = gst_structure_get_value (structure, "codec_data");
2014 buf = gst_value_get_buffer (value);
2016 guint8 *priv_data = NULL;
2017 guint priv_data_size = 0;
2019 priv_data_size = GST_BUFFER_SIZE (buf);
2020 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2021 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2022 " exceeded maximum (%d); discarding", pad,
2023 SUBTITLE_MAX_CODEC_PRIVATE);
2027 if (context->codec_priv != NULL)
2028 g_free (context->codec_priv);
2030 priv_data = g_malloc0 (priv_data_size);
2031 memcpy (priv_data, GST_BUFFER_DATA (buf), priv_data_size);
2032 context->codec_priv = priv_data;
2033 context->codec_priv_size = priv_data_size;
2036 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2037 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2040 /* free default if modified */
2049 * gst_matroska_mux_request_new_pad:
2050 * @element: #GstMatroskaMux.
2051 * @templ: #GstPadTemplate.
2052 * @pad_name: New pad name.
2054 * Request pad function for sink templates.
2056 * Returns: New #GstPad.
2059 gst_matroska_mux_request_new_pad (GstElement * element,
2060 GstPadTemplate * templ, const gchar * req_name)
2062 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2063 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2064 GstMatroskaPad *collect_pad;
2065 GstMatroskamuxPad *newpad;
2067 const gchar *pad_name = NULL;
2068 GstPadSetCapsFunction setcapsfunc = NULL;
2069 GstMatroskaTrackContext *context = NULL;
2071 gboolean locked = TRUE;
2074 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
2075 /* don't mix named and unnamed pads, if the pad already exists we fail when
2076 * trying to add it */
2077 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
2078 pad_name = req_name;
2080 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
2083 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2084 context = (GstMatroskaTrackContext *)
2085 g_new0 (GstMatroskaTrackAudioContext, 1);
2086 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2087 context->name = g_strdup ("Audio");
2088 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
2089 /* don't mix named and unnamed pads, if the pad already exists we fail when
2090 * trying to add it */
2091 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
2092 pad_name = req_name;
2094 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
2097 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2098 context = (GstMatroskaTrackContext *)
2099 g_new0 (GstMatroskaTrackVideoContext, 1);
2100 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2101 context->name = g_strdup ("Video");
2102 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
2103 /* don't mix named and unnamed pads, if the pad already exists we fail when
2104 * trying to add it */
2105 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
2106 pad_name = req_name;
2108 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
2111 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2112 context = (GstMatroskaTrackContext *)
2113 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2114 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2115 context->name = g_strdup ("Subtitle");
2116 /* setcaps may only provide proper one a lot later */
2117 id = g_strdup ("S_SUB_UNKNOWN");
2120 GST_WARNING_OBJECT (mux, "This is not our template!");
2124 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2125 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2128 gst_matroskamux_pad_init (newpad);
2129 collect_pad = (GstMatroskaPad *)
2130 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2131 sizeof (GstMatroskamuxPad),
2132 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2134 collect_pad->track = context;
2135 gst_matroska_pad_reset (collect_pad, FALSE);
2136 collect_pad->track->codec_id = id;
2138 gst_pad_set_setcaps_function (GST_PAD (newpad), setcapsfunc);
2139 gst_pad_set_active (GST_PAD (newpad), TRUE);
2140 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2141 goto pad_add_failed;
2145 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2147 return GST_PAD (newpad);
2152 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2153 gst_object_unref (newpad);
2159 * gst_matroska_mux_release_pad:
2160 * @element: #GstMatroskaMux.
2161 * @pad: Pad to release.
2163 * Release a previously requested pad.
2166 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2168 GstMatroskaMux *mux;
2171 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2173 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2174 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2175 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2177 if (cdata->pad == pad) {
2178 GstClockTime min_dur; /* observed minimum duration */
2180 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2181 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2182 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2183 if (collect_pad->duration < min_dur)
2184 collect_pad->duration = min_dur;
2187 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2188 mux->duration < collect_pad->duration)
2189 mux->duration = collect_pad->duration;
2195 gst_collect_pads2_remove_pad (mux->collect, pad);
2196 if (gst_element_remove_pad (element, pad))
2202 * gst_matroska_mux_track_header:
2203 * @mux: #GstMatroskaMux
2204 * @context: Tack context.
2206 * Write a track header.
2209 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2210 GstMatroskaTrackContext * context)
2212 GstEbmlWrite *ebml = mux->ebml_write;
2215 /* TODO: check if everything necessary is written and check default values */
2217 /* track type goes before the type-specific stuff */
2218 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2219 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2221 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2222 gst_matroska_mux_create_uid ());
2223 if (context->default_duration) {
2224 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2225 context->default_duration);
2227 if (context->language) {
2228 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2232 /* FIXME: until we have a nice way of getting the codecname
2233 * out of the caps, I'm not going to enable this. Too much
2234 * (useless, double, boring) work... */
2235 /* TODO: Use value from tags if any */
2236 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2237 context->codec_name); */
2238 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2240 /* type-specific stuff */
2241 switch (context->type) {
2242 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2243 GstMatroskaTrackVideoContext *videocontext =
2244 (GstMatroskaTrackVideoContext *) context;
2246 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2247 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2248 videocontext->pixel_width);
2249 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2250 videocontext->pixel_height);
2251 if (videocontext->display_width && videocontext->display_height) {
2252 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2253 videocontext->display_width);
2254 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2255 videocontext->display_height);
2257 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2258 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2259 if (videocontext->fourcc) {
2260 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2262 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2263 (gpointer) & fcc_le, 4);
2265 gst_ebml_write_master_finish (ebml, master);
2270 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2271 GstMatroskaTrackAudioContext *audiocontext =
2272 (GstMatroskaTrackAudioContext *) context;
2274 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2275 if (audiocontext->samplerate != 8000)
2276 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2277 audiocontext->samplerate);
2278 if (audiocontext->channels != 1)
2279 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2280 audiocontext->channels);
2281 if (audiocontext->bitdepth) {
2282 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2283 audiocontext->bitdepth);
2285 gst_ebml_write_master_finish (ebml, master);
2290 /* this is what we write for now and must be filled
2291 * and remainder void'ed later on */
2292 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2294 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2297 context->pos = ebml->pos;
2298 /* CodecID is mandatory ... */
2299 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2301 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2302 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2303 SUBTITLE_MAX_CODEC_PRIVATE);
2305 /* real data has to be written at finish */
2309 /* doesn't need type-specific data */
2313 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2314 if (context->codec_priv)
2315 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2316 context->codec_priv, context->codec_priv_size);
2321 * gst_matroska_mux_start:
2322 * @mux: #GstMatroskaMux
2324 * Start a new matroska file (write headers etc...)
2327 gst_matroska_mux_start (GstMatroskaMux * mux)
2329 GstEbmlWrite *ebml = mux->ebml_write;
2330 const gchar *doctype;
2331 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2332 GST_MATROSKA_ID_TRACKS,
2333 GST_MATROSKA_ID_CUES,
2334 GST_MATROSKA_ID_TAGS,
2337 guint64 master, child;
2341 GstClockTime duration = 0;
2342 guint32 segment_uid[4];
2343 GTimeVal time = { 0, 0 };
2345 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2346 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2348 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2350 /* we start with a EBML header */
2351 doctype = mux->doctype;
2352 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2353 doctype, mux->doctype_version);
2354 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2356 /* the rest of the header is cached */
2357 gst_ebml_write_set_cache (ebml, 0x1000);
2359 /* start a segment */
2361 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2362 mux->segment_master = ebml->pos;
2364 if (!mux->streamable) {
2365 /* seekhead (table of contents) - we set the positions later */
2366 mux->seekhead_pos = ebml->pos;
2367 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2368 for (i = 0; seekhead_id[i] != 0; i++) {
2369 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2370 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2371 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2372 gst_ebml_write_master_finish (ebml, child);
2374 gst_ebml_write_master_finish (ebml, master);
2377 if (mux->streamable) {
2378 const GstTagList *tags;
2381 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2383 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2384 guint64 master_tags, master_tag;
2386 GST_DEBUG_OBJECT (mux, "Writing tags");
2388 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2389 mux->tags_pos = ebml->pos;
2390 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2391 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2392 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2393 gst_ebml_write_master_finish (ebml, master_tag);
2394 gst_ebml_write_master_finish (ebml, master_tags);
2399 mux->info_pos = ebml->pos;
2400 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2401 for (i = 0; i < 4; i++) {
2402 segment_uid[i] = g_random_int ();
2404 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2405 (guint8 *) segment_uid, 16);
2406 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2407 mux->duration_pos = ebml->pos;
2409 if (!mux->streamable) {
2410 for (collected = mux->collect->data; collected;
2411 collected = g_slist_next (collected)) {
2412 GstMatroskaPad *collect_pad;
2413 GstFormat format = GST_FORMAT_TIME;
2415 gint64 trackduration;
2417 collect_pad = (GstMatroskaPad *) collected->data;
2418 thepad = collect_pad->collect.pad;
2420 /* Query the total length of the track. */
2421 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2422 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2423 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2424 GST_TIME_ARGS (trackduration));
2425 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2426 duration = (GstClockTime) trackduration;
2430 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2431 gst_guint64_to_gdouble (duration) /
2432 gst_guint64_to_gdouble (mux->time_scale));
2434 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2435 "GStreamer plugin version " PACKAGE_VERSION);
2436 if (mux->writing_app && mux->writing_app[0]) {
2437 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2439 g_get_current_time (&time);
2440 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2441 gst_ebml_write_master_finish (ebml, master);
2444 mux->tracks_pos = ebml->pos;
2445 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2447 for (collected = mux->collect->data; collected;
2448 collected = g_slist_next (collected)) {
2449 GstMatroskaPad *collect_pad;
2452 collect_pad = (GstMatroskaPad *) collected->data;
2453 thepad = collect_pad->collect.pad;
2455 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2456 collect_pad->track->codec_id != 0) {
2457 collect_pad->track->num = tracknum++;
2458 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2459 gst_matroska_mux_track_header (mux, collect_pad->track);
2460 gst_ebml_write_master_finish (ebml, child);
2461 /* some remaining pad/track setup */
2462 collect_pad->default_duration_scaled =
2463 gst_util_uint64_scale (collect_pad->track->default_duration,
2464 1, mux->time_scale);
2467 gst_ebml_write_master_finish (ebml, master);
2469 /* lastly, flush the cache */
2470 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2474 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2477 /* TODO: more sensible tag mappings */
2480 const gchar *matroska_tagname;
2481 const gchar *gstreamer_tagname;
2485 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2486 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2487 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2488 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2489 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2490 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2491 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2492 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2493 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2494 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2495 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2496 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2497 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2498 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2499 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2501 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2503 guint64 simpletag_master;
2505 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2506 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2507 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2509 if (strcmp (tagname_gst, tag) == 0) {
2510 GValue src = { 0, };
2513 if (!gst_tag_list_copy_value (&src, list, tag))
2515 if ((dest = gst_value_serialize (&src))) {
2517 simpletag_master = gst_ebml_write_master_start (ebml,
2518 GST_MATROSKA_ID_SIMPLETAG);
2519 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2520 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2521 gst_ebml_write_master_finish (ebml, simpletag_master);
2524 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2526 g_value_unset (&src);
2534 * gst_matroska_mux_finish:
2535 * @mux: #GstMatroskaMux
2537 * Finish a new matroska file (write index etc...)
2540 gst_matroska_mux_finish (GstMatroskaMux * mux)
2542 GstEbmlWrite *ebml = mux->ebml_write;
2544 guint64 duration = 0;
2546 const GstTagList *tags;
2548 /* finish last cluster */
2550 gst_ebml_write_master_finish (ebml, mux->cluster);
2554 if (mux->index != NULL) {
2556 guint64 master, pointentry_master, trackpos_master;
2558 mux->cues_pos = ebml->pos;
2559 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2560 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2562 for (n = 0; n < mux->num_indexes; n++) {
2563 GstMatroskaIndex *idx = &mux->index[n];
2565 pointentry_master = gst_ebml_write_master_start (ebml,
2566 GST_MATROSKA_ID_POINTENTRY);
2567 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2568 idx->time / mux->time_scale);
2569 trackpos_master = gst_ebml_write_master_start (ebml,
2570 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2571 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2572 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2573 idx->pos - mux->segment_master);
2574 gst_ebml_write_master_finish (ebml, trackpos_master);
2575 gst_ebml_write_master_finish (ebml, pointentry_master);
2578 gst_ebml_write_master_finish (ebml, master);
2579 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2583 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2585 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2586 guint64 master_tags, master_tag;
2588 GST_DEBUG_OBJECT (mux, "Writing tags");
2590 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2591 mux->tags_pos = ebml->pos;
2592 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2593 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2594 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2595 gst_ebml_write_master_finish (ebml, master_tag);
2596 gst_ebml_write_master_finish (ebml, master_tags);
2599 /* update seekhead. We know that:
2600 * - a seekhead contains 4 entries.
2601 * - order of entries is as above.
2602 * - a seekhead has a 4-byte header + 8-byte length
2603 * - each entry is 2-byte master, 2-byte ID pointer,
2604 * 2-byte length pointer, all 8/1-byte length, 4-
2605 * byte ID and 8-byte length pointer, where the
2606 * length pointer starts at 20.
2607 * - all entries are local to the segment (so pos - segment_master).
2608 * - so each entry is at 12 + 20 + num * 28. */
2609 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2610 mux->info_pos - mux->segment_master);
2611 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2612 mux->tracks_pos - mux->segment_master);
2613 if (mux->index != NULL) {
2614 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2615 mux->cues_pos - mux->segment_master);
2618 guint64 my_pos = ebml->pos;
2620 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2621 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2622 gst_ebml_write_seek (ebml, my_pos);
2625 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2626 mux->tags_pos - mux->segment_master);
2629 guint64 my_pos = ebml->pos;
2631 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2632 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2633 gst_ebml_write_seek (ebml, my_pos);
2637 * - first get the overall duration
2638 * (a released track may have left a duration in here)
2639 * - write some track header data for subtitles
2641 duration = mux->duration;
2643 for (collected = mux->collect->data; collected;
2644 collected = g_slist_next (collected)) {
2645 GstMatroskaPad *collect_pad;
2646 GstClockTime min_duration; /* observed minimum duration */
2647 GstMatroskaTrackContext *context;
2648 gint voidleft = 0, fill = 0;
2651 collect_pad = (GstMatroskaPad *) collected->data;
2652 context = collect_pad->track;
2654 GST_DEBUG_OBJECT (mux,
2655 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2656 " end ts %" GST_TIME_FORMAT, collect_pad,
2657 GST_TIME_ARGS (collect_pad->start_ts),
2658 GST_TIME_ARGS (collect_pad->end_ts));
2660 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2661 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2663 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2664 if (collect_pad->duration < min_duration)
2665 collect_pad->duration = min_duration;
2666 GST_DEBUG_OBJECT (collect_pad,
2667 "final track duration: %" GST_TIME_FORMAT,
2668 GST_TIME_ARGS (collect_pad->duration));
2671 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2672 duration < collect_pad->duration)
2673 duration = collect_pad->duration;
2675 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2679 /* write subtitle type and possible private data */
2680 gst_ebml_write_seek (ebml, context->pos);
2681 /* complex way to write ascii to account for extra filling */
2682 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2683 strcpy (codec_id, context->codec_id);
2684 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2685 codec_id, strlen (context->codec_id) + 1 + fill);
2687 if (context->codec_priv)
2688 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2689 context->codec_priv, context->codec_priv_size);
2690 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2691 /* void'ify; sigh, variable sized length field */
2692 if (voidleft == 1) {
2695 } else if (voidleft && voidleft <= 128)
2696 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2697 else if (voidleft >= 130)
2698 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2699 else if (voidleft == 129) {
2700 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2701 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2705 /* seek back (optional, but do anyway) */
2706 gst_ebml_write_seek (ebml, pos);
2708 /* update duration */
2709 if (duration != 0) {
2710 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2711 GST_TIME_ARGS (duration));
2712 pos = mux->ebml_write->pos;
2713 gst_ebml_write_seek (ebml, mux->duration_pos);
2714 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2715 gst_guint64_to_gdouble (duration) /
2716 gst_guint64_to_gdouble (mux->time_scale));
2717 gst_ebml_write_seek (ebml, pos);
2720 guint64 my_pos = ebml->pos;
2722 gst_ebml_write_seek (ebml, mux->duration_pos);
2723 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2724 gst_ebml_write_seek (ebml, my_pos);
2726 GST_DEBUG_OBJECT (mux, "finishing segment");
2727 /* finish segment - this also writes element length */
2728 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2732 * gst_matroska_mux_buffer_header:
2733 * @track: Track context.
2734 * @relative_timestamp: relative timestamp of the buffer
2735 * @flags: Buffer flags.
2737 * Create a buffer containing buffer header.
2739 * Returns: New buffer.
2742 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2743 gint16 relative_timestamp, int flags)
2747 hdr = gst_buffer_new_and_alloc (4);
2748 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2749 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2750 /* time relative to clustertime */
2751 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2754 GST_BUFFER_DATA (hdr)[3] = flags;
2759 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2760 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2761 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2764 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2765 GstMatroskaPad * collect_pad, GstBuffer * buf)
2767 GstMatroskaTrackVideoContext *ctx =
2768 (GstMatroskaTrackVideoContext *) collect_pad->track;
2769 const guint8 *data = GST_BUFFER_DATA (buf);
2770 guint size = GST_BUFFER_SIZE (buf);
2772 guint32 next_parse_offset;
2773 GstBuffer *ret = NULL;
2774 gboolean is_muxing_unit = FALSE;
2776 if (GST_BUFFER_SIZE (buf) < 13) {
2777 gst_buffer_unref (buf);
2781 /* Check if this buffer contains a picture or end-of-sequence packet */
2782 while (size >= 13) {
2783 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2784 gst_buffer_unref (buf);
2788 parse_code = GST_READ_UINT8 (data + 4);
2789 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2790 if (ctx->dirac_unit) {
2791 gst_buffer_unref (ctx->dirac_unit);
2792 ctx->dirac_unit = NULL;
2794 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2795 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2796 is_muxing_unit = TRUE;
2800 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2802 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2805 data += next_parse_offset;
2806 size -= next_parse_offset;
2809 if (ctx->dirac_unit)
2810 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2812 ctx->dirac_unit = gst_buffer_ref (buf);
2814 if (is_muxing_unit) {
2815 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2816 ctx->dirac_unit = NULL;
2817 gst_buffer_copy_metadata (ret, buf,
2818 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2819 GST_BUFFER_COPY_CAPS);
2820 gst_buffer_unref (buf);
2822 gst_buffer_unref (buf);
2830 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2834 GValue streamheader = { 0 };
2835 GValue bufval = { 0 };
2836 GstBuffer *streamheader_buffer;
2837 GstEbmlWrite *ebml = mux->ebml_write;
2839 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2840 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2841 caps = gst_caps_new_simple ("video/webm", NULL);
2843 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2845 s = gst_caps_get_structure (caps, 0);
2846 g_value_init (&streamheader, GST_TYPE_ARRAY);
2847 g_value_init (&bufval, GST_TYPE_BUFFER);
2848 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2849 gst_value_set_buffer (&bufval, streamheader_buffer);
2850 gst_value_array_append_value (&streamheader, &bufval);
2851 g_value_unset (&bufval);
2852 gst_structure_set_value (s, "streamheader", &streamheader);
2853 g_value_unset (&streamheader);
2854 gst_caps_replace (&ebml->caps, caps);
2855 gst_buffer_unref (streamheader_buffer);
2856 gst_caps_unref (caps);
2860 * gst_matroska_mux_write_data:
2861 * @mux: #GstMatroskaMux
2862 * @collect_pad: #GstMatroskaPad with the data
2864 * Write collected data (called from gst_matroska_mux_collected).
2866 * Returns: Result of the gst_pad_push issued to write the data.
2868 static GstFlowReturn
2869 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2872 GstEbmlWrite *ebml = mux->ebml_write;
2875 gboolean write_duration;
2876 gint16 relative_timestamp;
2877 gint64 relative_timestamp64;
2878 guint64 block_duration;
2879 gboolean is_video_keyframe = FALSE;
2880 GstMatroskamuxPad *pad;
2883 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2885 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2886 if (collect_pad->track->xiph_headers_to_skip > 0) {
2887 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2888 gst_buffer_unref (buf);
2889 --collect_pad->track->xiph_headers_to_skip;
2893 /* for dirac we have to queue up everything up to a picture unit */
2894 if (collect_pad->track->codec_id != NULL &&
2895 strcmp (collect_pad->track->codec_id,
2896 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2897 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2902 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2903 * this would wreak havoc with time stored in matroska file */
2904 /* TODO: maybe calculate a timestamp by using the previous timestamp
2905 * and default duration */
2906 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2907 GST_WARNING_OBJECT (collect_pad->collect.pad,
2908 "Invalid buffer timestamp; dropping buffer");
2909 gst_buffer_unref (buf);
2913 /* set the timestamp for outgoing buffers */
2914 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2916 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2917 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2918 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2919 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2920 is_video_keyframe = TRUE;
2924 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2925 * or when we may be reaching the limit of the relative timestamp */
2926 if (mux->cluster_time +
2927 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2928 || is_video_keyframe || mux->force_key_unit_event) {
2929 if (!mux->streamable)
2930 gst_ebml_write_master_finish (ebml, mux->cluster);
2932 /* Forward the GstForceKeyUnit event after finishing the cluster */
2933 if (mux->force_key_unit_event) {
2934 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2935 mux->force_key_unit_event = NULL;
2938 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2939 mux->cluster_pos = ebml->pos;
2940 gst_ebml_write_set_cache (ebml, 0x20);
2942 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2943 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2944 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2946 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2947 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2949 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2950 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2951 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2952 mux->prev_cluster_size);
2957 mux->cluster_pos = ebml->pos;
2958 gst_ebml_write_set_cache (ebml, 0x20);
2959 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2960 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2961 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2962 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2963 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2966 /* update duration of this track */
2967 if (GST_BUFFER_DURATION_IS_VALID (buf))
2968 collect_pad->duration += GST_BUFFER_DURATION (buf);
2970 /* We currently write index entries for all video tracks or for the audio
2971 * track in a single-track audio file. This could be improved by keeping the
2972 * index only for the *first* video track. */
2974 /* TODO: index is useful for every track, should contain the number of
2975 * the block in the cluster which contains the timestamp, should also work
2976 * for files with multiple audio tracks.
2978 if (!mux->streamable &&
2979 (is_video_keyframe ||
2980 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2981 (mux->num_streams == 1)))) {
2984 if (mux->min_index_interval != 0) {
2985 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2986 if (mux->index[last_idx].track == collect_pad->track->num)
2991 if (last_idx < 0 || mux->min_index_interval == 0 ||
2992 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2993 >= mux->min_index_interval)) {
2994 GstMatroskaIndex *idx;
2996 if (mux->num_indexes % 32 == 0) {
2997 mux->index = g_renew (GstMatroskaIndex, mux->index,
2998 mux->num_indexes + 32);
3000 idx = &mux->index[mux->num_indexes++];
3002 idx->pos = mux->cluster_pos;
3003 idx->time = GST_BUFFER_TIMESTAMP (buf);
3004 idx->track = collect_pad->track->num;
3008 /* Check if the duration differs from the default duration. */
3009 write_duration = FALSE;
3011 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3012 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3013 1, mux->time_scale);
3015 /* small difference should be ok. */
3016 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3017 block_duration < collect_pad->default_duration_scaled - 1) {
3018 write_duration = TRUE;
3022 /* write the block, for doctype v2 use SimpleBlock if possible
3023 * one slice (*breath*).
3024 * FIXME: Need to do correct lacing! */
3025 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3026 if (relative_timestamp64 >= 0) {
3027 /* round the timestamp */
3028 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3030 /* round the timestamp */
3031 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3033 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3035 if (mux->doctype_version > 1 && !write_duration) {
3037 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3040 gst_matroska_mux_create_buffer_header (collect_pad->track,
3041 relative_timestamp, flags);
3042 gst_ebml_write_set_cache (ebml, 0x40);
3043 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3044 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3045 gst_ebml_write_buffer (ebml, hdr);
3046 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3047 gst_ebml_write_buffer (ebml, buf);
3049 return gst_ebml_last_write_result (ebml);
3051 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
3052 /* write and call order slightly unnatural,
3053 * but avoids seek and minizes pushing */
3054 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3056 gst_matroska_mux_create_buffer_header (collect_pad->track,
3057 relative_timestamp, 0);
3059 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3060 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3061 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3062 gst_ebml_write_buffer (ebml, hdr);
3063 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
3064 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3065 gst_ebml_write_buffer (ebml, buf);
3067 return gst_ebml_last_write_result (ebml);
3072 * gst_matroska_mux_handle_buffer:
3073 * @pads: #GstCollectPads2
3074 * @uuser_data: #GstMatroskaMux
3076 * Collectpads callback.
3078 * Returns: #GstFlowReturn
3080 static GstFlowReturn
3081 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3082 GstBuffer * buf, gpointer user_data)
3084 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3085 GstEbmlWrite *ebml = mux->ebml_write;
3086 GstMatroskaPad *best;
3087 GstFlowReturn ret = GST_FLOW_OK;
3089 GST_DEBUG_OBJECT (mux, "Collected pads");
3091 /* start with a header */
3092 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3093 if (mux->collect->data == NULL) {
3094 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3095 ("No input streams configured"));
3096 return GST_FLOW_ERROR;
3098 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3099 gst_ebml_start_streamheader (ebml);
3100 gst_matroska_mux_start (mux);
3101 gst_matroska_mux_stop_streamheader (mux);
3102 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3105 /* provided with stream to write from */
3106 best = (GstMatroskaPad *) data;
3108 /* if there is no best pad, we have reached EOS */
3110 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3111 if (!mux->streamable) {
3112 gst_matroska_mux_finish (mux);
3114 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3116 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3117 ret = GST_FLOW_UNEXPECTED;
3121 /* if we have a best stream, should also have a buffer */
3124 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3125 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3126 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3127 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3129 /* make note of first and last encountered timestamps, so we can calculate
3130 * the actual duration later when we send an updated header on eos */
3131 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3132 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3133 GstClockTime end_ts = start_ts;
3135 if (GST_BUFFER_DURATION_IS_VALID (buf))
3136 end_ts += GST_BUFFER_DURATION (buf);
3137 else if (best->track->default_duration)
3138 end_ts += best->track->default_duration;
3140 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3141 best->end_ts = end_ts;
3143 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3144 start_ts < best->start_ts))
3145 best->start_ts = start_ts;
3148 /* write one buffer */
3149 ret = gst_matroska_mux_write_data (mux, best, buf);
3157 * gst_matroska_mux_change_state:
3158 * @element: #GstMatroskaMux
3159 * @transition: State change transition.
3161 * Change the muxer state.
3163 * Returns: #GstStateChangeReturn
3165 static GstStateChangeReturn
3166 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3168 GstStateChangeReturn ret;
3169 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3171 switch (transition) {
3172 case GST_STATE_CHANGE_NULL_TO_READY:
3174 case GST_STATE_CHANGE_READY_TO_PAUSED:
3175 gst_collect_pads2_start (mux->collect);
3177 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3179 case GST_STATE_CHANGE_PAUSED_TO_READY:
3180 gst_collect_pads2_stop (mux->collect);
3186 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3188 switch (transition) {
3189 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3191 case GST_STATE_CHANGE_PAUSED_TO_READY:
3192 gst_matroska_mux_reset (GST_ELEMENT (mux));
3194 case GST_STATE_CHANGE_READY_TO_NULL:
3204 gst_matroska_mux_set_property (GObject * object,
3205 guint prop_id, const GValue * value, GParamSpec * pspec)
3207 GstMatroskaMux *mux;
3209 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3210 mux = GST_MATROSKA_MUX (object);
3213 case ARG_WRITING_APP:
3214 if (!g_value_get_string (value)) {
3215 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3218 g_free (mux->writing_app);
3219 mux->writing_app = g_value_dup_string (value);
3221 case ARG_DOCTYPE_VERSION:
3222 mux->doctype_version = g_value_get_int (value);
3224 case ARG_MIN_INDEX_INTERVAL:
3225 mux->min_index_interval = g_value_get_int64 (value);
3227 case ARG_STREAMABLE:
3228 mux->streamable = g_value_get_boolean (value);
3231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3237 gst_matroska_mux_get_property (GObject * object,
3238 guint prop_id, GValue * value, GParamSpec * pspec)
3240 GstMatroskaMux *mux;
3242 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3243 mux = GST_MATROSKA_MUX (object);
3246 case ARG_WRITING_APP:
3247 g_value_set_string (value, mux->writing_app);
3249 case ARG_DOCTYPE_VERSION:
3250 g_value_set_int (value, mux->doctype_version);
3252 case ARG_MIN_INDEX_INTERVAL:
3253 g_value_set_int64 (value, mux->min_index_interval);
3255 case ARG_STREAMABLE:
3256 g_value_set_boolean (value, mux->streamable);
3259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);