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 */
857 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
860 g_assert (context && id);
861 if (context->codec_id)
862 g_free (context->codec_id);
863 context->codec_id = g_strdup (id);
867 * gst_matroska_mux_video_pad_setcaps:
868 * @pad: Pad which got the caps.
871 * Setcaps function for video sink pad.
873 * Returns: #TRUE on success.
876 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
878 GstMatroskaTrackContext *context = NULL;
879 GstMatroskaTrackVideoContext *videocontext;
881 GstMatroskaPad *collect_pad;
882 GstStructure *structure;
883 const gchar *mimetype;
884 const GValue *value = NULL;
885 const GstBuffer *codec_buf = NULL;
886 gint width, height, pixel_width, pixel_height;
888 gboolean interlaced = FALSE;
890 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
893 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
894 g_assert (collect_pad);
895 context = collect_pad->track;
897 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
898 videocontext = (GstMatroskaTrackVideoContext *) context;
900 /* gst -> matroska ID'ing */
901 structure = gst_caps_get_structure (caps, 0);
903 mimetype = gst_structure_get_name (structure);
905 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
907 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
909 if (!strcmp (mimetype, "video/x-theora")) {
910 /* we'll extract the details later from the theora identification header */
914 /* get general properties */
915 /* spec says it is mandatory */
916 if (!gst_structure_get_int (structure, "width", &width) ||
917 !gst_structure_get_int (structure, "height", &height))
920 videocontext->pixel_width = width;
921 videocontext->pixel_height = height;
923 /* set vp8 defaults or let user override it */
924 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
925 && (!strcmp (mimetype, "video/x-vp8")))
926 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
927 DEFAULT_PAD_FRAME_DURATION_VP8;
929 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
930 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
932 context->default_duration =
933 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
934 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
935 GST_TIME_ARGS (context->default_duration));
937 context->default_duration = 0;
939 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
940 &pixel_width, &pixel_height)) {
941 if (pixel_width > pixel_height) {
942 videocontext->display_width = width * pixel_width / pixel_height;
943 videocontext->display_height = height;
944 } else if (pixel_width < pixel_height) {
945 videocontext->display_width = width;
946 videocontext->display_height = height * pixel_height / pixel_width;
948 videocontext->display_width = 0;
949 videocontext->display_height = 0;
952 videocontext->display_width = 0;
953 videocontext->display_height = 0;
958 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
959 videocontext->fourcc = 0;
961 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
962 * data and other settings
966 /* extract codec_data, may turn out needed */
967 value = gst_structure_get_value (structure, "codec_data");
969 codec_buf = gst_value_get_buffer (value);
972 if (!strcmp (mimetype, "video/x-raw-yuv")) {
973 gst_matroska_mux_set_codec_id (context,
974 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
975 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
976 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
977 ||!strcmp (mimetype, "video/x-huffyuv")
978 || !strcmp (mimetype, "video/x-divx")
979 || !strcmp (mimetype, "video/x-dv")
980 || !strcmp (mimetype, "video/x-h263")
981 || !strcmp (mimetype, "video/x-msmpeg")
982 || !strcmp (mimetype, "video/x-wmv")
983 || !strcmp (mimetype, "image/jpeg")) {
984 gst_riff_strf_vids *bih;
985 gint size = sizeof (gst_riff_strf_vids);
988 if (!strcmp (mimetype, "video/x-xvid"))
989 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
990 else if (!strcmp (mimetype, "video/x-huffyuv"))
991 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
992 else if (!strcmp (mimetype, "video/x-dv"))
993 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
994 else if (!strcmp (mimetype, "video/x-h263"))
995 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
996 else if (!strcmp (mimetype, "video/x-divx")) {
999 gst_structure_get_int (structure, "divxversion", &divxversion);
1000 switch (divxversion) {
1002 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1005 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1008 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1011 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1014 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1015 switch (msmpegversion) {
1017 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1020 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1026 } else if (!strcmp (mimetype, "video/x-wmv")) {
1029 if (gst_structure_get_fourcc (structure, "format", &format)) {
1031 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1032 if (wmvversion == 2) {
1033 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1034 } else if (wmvversion == 1) {
1035 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1036 } else if (wmvversion == 3) {
1037 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1040 } else if (!strcmp (mimetype, "image/jpeg")) {
1041 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1047 bih = g_new0 (gst_riff_strf_vids, 1);
1048 GST_WRITE_UINT32_LE (&bih->size, size);
1049 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1050 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1051 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1052 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1053 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1054 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1055 videocontext->pixel_height * 3);
1057 /* process codec private/initialization data, if any */
1059 size += GST_BUFFER_SIZE (codec_buf);
1060 bih = g_realloc (bih, size);
1061 GST_WRITE_UINT32_LE (&bih->size, size);
1062 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
1063 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
1066 gst_matroska_mux_set_codec_id (context,
1067 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1068 context->codec_priv = (gpointer) bih;
1069 context->codec_priv_size = size;
1070 } else if (!strcmp (mimetype, "video/x-h264")) {
1071 gst_matroska_mux_set_codec_id (context,
1072 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1074 if (context->codec_priv != NULL) {
1075 g_free (context->codec_priv);
1076 context->codec_priv = NULL;
1077 context->codec_priv_size = 0;
1080 /* Create avcC header */
1081 if (codec_buf != NULL) {
1082 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1083 context->codec_priv = g_malloc0 (context->codec_priv_size);
1084 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1085 context->codec_priv_size);
1087 } else if (!strcmp (mimetype, "video/x-theora")) {
1088 const GValue *streamheader;
1090 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1092 if (context->codec_priv != NULL) {
1093 g_free (context->codec_priv);
1094 context->codec_priv = NULL;
1095 context->codec_priv_size = 0;
1098 streamheader = gst_structure_get_value (structure, "streamheader");
1099 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1100 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1101 ("theora stream headers missing or malformed"));
1104 } else if (!strcmp (mimetype, "video/x-dirac")) {
1105 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1106 } else if (!strcmp (mimetype, "video/x-vp8")) {
1107 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1108 } else if (!strcmp (mimetype, "video/mpeg")) {
1111 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1112 switch (mpegversion) {
1114 gst_matroska_mux_set_codec_id (context,
1115 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1118 gst_matroska_mux_set_codec_id (context,
1119 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1122 gst_matroska_mux_set_codec_id (context,
1123 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1129 /* global headers may be in codec data */
1130 if (codec_buf != NULL) {
1131 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1132 context->codec_priv = g_malloc0 (context->codec_priv_size);
1133 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1134 context->codec_priv_size);
1136 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1138 /* can only make it here if preceding case verified it was version 3 */
1139 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1140 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1142 const GValue *mdpr_data;
1144 gst_structure_get_int (structure, "rmversion", &rmversion);
1145 switch (rmversion) {
1147 gst_matroska_mux_set_codec_id (context,
1148 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1151 gst_matroska_mux_set_codec_id (context,
1152 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1155 gst_matroska_mux_set_codec_id (context,
1156 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1159 gst_matroska_mux_set_codec_id (context,
1160 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1166 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1167 if (mdpr_data != NULL) {
1168 guint8 *priv_data = NULL;
1169 guint priv_data_size = 0;
1171 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1173 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1174 priv_data = g_malloc0 (priv_data_size);
1176 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1178 context->codec_priv = priv_data;
1179 context->codec_priv_size = priv_data_size;
1188 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1189 GST_PAD_NAME (pad), caps);
1194 /* N > 0 to expect a particular number of headers, negative if the
1195 number of headers is variable */
1197 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1198 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1200 GstBuffer **buf = NULL;
1203 guint bufi, i, offset, priv_data_size;
1205 if (streamheader == NULL)
1206 goto no_stream_headers;
1208 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1211 bufarr = g_value_peek_pointer (streamheader);
1212 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1214 if (N > 0 && bufarr->len != N)
1217 context->xiph_headers_to_skip = bufarr->len;
1219 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1220 for (i = 0; i < bufarr->len; i++) {
1221 GValue *bufval = &g_array_index (bufarr, GValue, i);
1223 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1225 goto wrong_content_type;
1228 buf[i] = g_value_peek_pointer (bufval);
1232 if (bufarr->len > 0) {
1233 for (i = 0; i < bufarr->len - 1; i++) {
1234 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1238 for (i = 0; i < bufarr->len; ++i) {
1239 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1242 priv_data = g_malloc0 (priv_data_size);
1244 priv_data[0] = bufarr->len - 1;
1247 if (bufarr->len > 0) {
1248 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1249 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1250 priv_data[offset++] = 0xff;
1252 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1256 for (i = 0; i < bufarr->len; ++i) {
1257 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1258 GST_BUFFER_SIZE (buf[i]));
1259 offset += GST_BUFFER_SIZE (buf[i]);
1262 context->codec_priv = priv_data;
1263 context->codec_priv_size = priv_data_size;
1266 *p_buf0 = gst_buffer_ref (buf[0]);
1275 GST_WARNING ("required streamheaders missing in sink caps!");
1280 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1281 G_VALUE_TYPE_NAME (streamheader));
1286 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1291 GST_WARNING ("streamheaders array does not contain GstBuffers");
1297 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1298 GstMatroskaTrackContext * context)
1300 GstBuffer *buf0 = NULL;
1302 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1305 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1306 GST_WARNING ("First vorbis header too small, ignoring");
1308 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1309 GstMatroskaTrackAudioContext *audiocontext;
1312 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1313 audiocontext = (GstMatroskaTrackAudioContext *) context;
1314 audiocontext->channels = GST_READ_UINT8 (hdr);
1315 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1320 gst_buffer_unref (buf0);
1326 theora_streamheader_to_codecdata (const GValue * streamheader,
1327 GstMatroskaTrackContext * context)
1329 GstBuffer *buf0 = NULL;
1331 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1334 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1335 GST_WARNING ("First theora header too small, ignoring");
1336 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1337 GST_WARNING ("First header not a theora identification header, ignoring");
1339 GstMatroskaTrackVideoContext *videocontext;
1340 guint fps_num, fps_denom, par_num, par_denom;
1343 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1345 videocontext = (GstMatroskaTrackVideoContext *) context;
1346 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1347 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1348 hdr += 3 + 3 + 1 + 1;
1349 fps_num = GST_READ_UINT32_BE (hdr);
1350 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1351 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1352 fps_denom, fps_num);
1354 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1355 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1356 if (par_num > 0 && par_num > 0) {
1357 if (par_num > par_denom) {
1358 videocontext->display_width =
1359 videocontext->pixel_width * par_num / par_denom;
1360 videocontext->display_height = videocontext->pixel_height;
1361 } else if (par_num < par_denom) {
1362 videocontext->display_width = videocontext->pixel_width;
1363 videocontext->display_height =
1364 videocontext->pixel_height * par_denom / par_num;
1366 videocontext->display_width = 0;
1367 videocontext->display_height = 0;
1370 videocontext->display_width = 0;
1371 videocontext->display_height = 0;
1377 gst_buffer_unref (buf0);
1383 kate_streamheader_to_codecdata (const GValue * streamheader,
1384 GstMatroskaTrackContext * context)
1386 GstBuffer *buf0 = NULL;
1388 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1391 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1392 GST_WARNING ("First kate header too small, ignoring");
1393 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1394 GST_WARNING ("First header not a kate identification header, ignoring");
1398 gst_buffer_unref (buf0);
1404 flac_streamheader_to_codecdata (const GValue * streamheader,
1405 GstMatroskaTrackContext * context)
1412 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1413 GST_WARNING ("No or invalid streamheader field in the caps");
1417 bufarr = g_value_peek_pointer (streamheader);
1418 if (bufarr->len < 2) {
1419 GST_WARNING ("Too few headers in streamheader field");
1423 context->xiph_headers_to_skip = bufarr->len + 1;
1425 bufval = &g_array_index (bufarr, GValue, 0);
1426 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1427 GST_WARNING ("streamheaders array does not contain GstBuffers");
1431 buffer = g_value_peek_pointer (bufval);
1433 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1434 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1435 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1436 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1437 GST_WARNING ("Invalid streamheader for FLAC");
1441 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1442 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1443 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1444 GST_BUFFER_SIZE (buffer) - 9);
1446 for (i = 1; i < bufarr->len; i++) {
1447 bufval = &g_array_index (bufarr, GValue, i);
1449 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1450 g_free (context->codec_priv);
1451 context->codec_priv = NULL;
1452 context->codec_priv_size = 0;
1453 GST_WARNING ("streamheaders array does not contain GstBuffers");
1457 buffer = g_value_peek_pointer (bufval);
1459 context->codec_priv =
1460 g_realloc (context->codec_priv,
1461 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1462 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1463 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1464 context->codec_priv_size =
1465 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1472 speex_streamheader_to_codecdata (const GValue * streamheader,
1473 GstMatroskaTrackContext * context)
1479 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1480 GST_WARNING ("No or invalid streamheader field in the caps");
1484 bufarr = g_value_peek_pointer (streamheader);
1485 if (bufarr->len != 2) {
1486 GST_WARNING ("Too few headers in streamheader field");
1490 context->xiph_headers_to_skip = bufarr->len + 1;
1492 bufval = &g_array_index (bufarr, GValue, 0);
1493 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1494 GST_WARNING ("streamheaders array does not contain GstBuffers");
1498 buffer = g_value_peek_pointer (bufval);
1500 if (GST_BUFFER_SIZE (buffer) < 80
1501 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1502 GST_WARNING ("Invalid streamheader for Speex");
1506 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1507 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1508 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1509 GST_BUFFER_SIZE (buffer));
1511 bufval = &g_array_index (bufarr, GValue, 1);
1513 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1514 g_free (context->codec_priv);
1515 context->codec_priv = NULL;
1516 context->codec_priv_size = 0;
1517 GST_WARNING ("streamheaders array does not contain GstBuffers");
1521 buffer = g_value_peek_pointer (bufval);
1523 context->codec_priv =
1524 g_realloc (context->codec_priv,
1525 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1526 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1527 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1528 context->codec_priv_size =
1529 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1534 static const gchar *
1535 aac_codec_data_to_codec_id (const GstBuffer * buf)
1537 const gchar *result;
1540 /* default to MAIN */
1543 if (GST_BUFFER_SIZE (buf) >= 2) {
1544 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1562 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1571 * gst_matroska_mux_audio_pad_setcaps:
1572 * @pad: Pad which got the caps.
1575 * Setcaps function for audio sink pad.
1577 * Returns: #TRUE on success.
1580 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1582 GstMatroskaTrackContext *context = NULL;
1583 GstMatroskaTrackAudioContext *audiocontext;
1584 GstMatroskaMux *mux;
1585 GstMatroskaPad *collect_pad;
1586 const gchar *mimetype;
1587 gint samplerate = 0, channels = 0;
1588 GstStructure *structure;
1589 const GValue *codec_data = NULL;
1590 const GstBuffer *buf = NULL;
1591 const gchar *stream_format = NULL;
1593 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1596 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1597 g_assert (collect_pad);
1598 context = collect_pad->track;
1600 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1601 audiocontext = (GstMatroskaTrackAudioContext *) context;
1603 structure = gst_caps_get_structure (caps, 0);
1604 mimetype = gst_structure_get_name (structure);
1607 gst_structure_get_int (structure, "rate", &samplerate);
1608 gst_structure_get_int (structure, "channels", &channels);
1610 audiocontext->samplerate = samplerate;
1611 audiocontext->channels = channels;
1612 audiocontext->bitdepth = 0;
1613 context->default_duration = 0;
1615 codec_data = gst_structure_get_value (structure, "codec_data");
1617 buf = gst_value_get_buffer (codec_data);
1619 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1620 * data and other settings
1624 if (!strcmp (mimetype, "audio/mpeg")) {
1625 gint mpegversion = 0;
1627 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1628 switch (mpegversion) {
1634 gst_structure_get_int (structure, "layer", &layer);
1636 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1637 GST_WARNING_OBJECT (mux,
1638 "Unable to determine MPEG audio version, assuming 1");
1644 else if (layer == 2)
1646 else if (version == 2)
1651 context->default_duration =
1652 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1656 gst_matroska_mux_set_codec_id (context,
1657 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1660 gst_matroska_mux_set_codec_id (context,
1661 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1664 gst_matroska_mux_set_codec_id (context,
1665 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1674 stream_format = gst_structure_get_string (structure, "stream-format");
1675 /* check this is raw aac */
1676 if (stream_format) {
1677 if (strcmp (stream_format, "raw") != 0) {
1678 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1682 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1687 if (mpegversion == 2)
1689 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1690 aac_codec_data_to_codec_id (buf));
1691 else if (mpegversion == 4)
1693 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1694 aac_codec_data_to_codec_id (buf));
1696 g_assert_not_reached ();
1698 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1705 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1707 gint endianness = G_LITTLE_ENDIAN;
1708 gboolean signedness = TRUE;
1710 if (!gst_structure_get_int (structure, "width", &width) ||
1711 !gst_structure_get_int (structure, "depth", &depth) ||
1712 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1713 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1718 !gst_structure_get_int (structure, "endianness", &endianness)) {
1719 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1723 if (width != depth) {
1724 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1728 /* FIXME: where is this spec'ed out? (tpm) */
1729 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1730 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1734 audiocontext->bitdepth = depth;
1735 if (endianness == G_BIG_ENDIAN)
1736 gst_matroska_mux_set_codec_id (context,
1737 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1739 gst_matroska_mux_set_codec_id (context,
1740 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1742 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1745 if (!gst_structure_get_int (structure, "width", &width)) {
1746 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1750 audiocontext->bitdepth = width;
1751 gst_matroska_mux_set_codec_id (context,
1752 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1754 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1755 const GValue *streamheader;
1757 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1759 if (context->codec_priv != NULL) {
1760 g_free (context->codec_priv);
1761 context->codec_priv = NULL;
1762 context->codec_priv_size = 0;
1765 streamheader = gst_structure_get_value (structure, "streamheader");
1766 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1767 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1768 ("vorbis stream headers missing or malformed"));
1771 } else if (!strcmp (mimetype, "audio/x-flac")) {
1772 const GValue *streamheader;
1774 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1775 if (context->codec_priv != NULL) {
1776 g_free (context->codec_priv);
1777 context->codec_priv = NULL;
1778 context->codec_priv_size = 0;
1781 streamheader = gst_structure_get_value (structure, "streamheader");
1782 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1783 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1784 ("flac stream headers missing or malformed"));
1787 } else if (!strcmp (mimetype, "audio/x-speex")) {
1788 const GValue *streamheader;
1790 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1791 if (context->codec_priv != NULL) {
1792 g_free (context->codec_priv);
1793 context->codec_priv = NULL;
1794 context->codec_priv_size = 0;
1797 streamheader = gst_structure_get_value (structure, "streamheader");
1798 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1799 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1800 ("speex stream headers missing or malformed"));
1803 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1804 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1805 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1806 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1807 } else if (!strcmp (mimetype, "audio/x-dts")) {
1808 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1809 } else if (!strcmp (mimetype, "audio/x-tta")) {
1812 /* TTA frame duration */
1813 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1815 gst_structure_get_int (structure, "width", &width);
1816 audiocontext->bitdepth = width;
1817 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1819 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1821 const GValue *mdpr_data;
1823 gst_structure_get_int (structure, "raversion", &raversion);
1824 switch (raversion) {
1826 gst_matroska_mux_set_codec_id (context,
1827 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1830 gst_matroska_mux_set_codec_id (context,
1831 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1834 gst_matroska_mux_set_codec_id (context,
1835 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1841 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1842 if (mdpr_data != NULL) {
1843 guint8 *priv_data = NULL;
1844 guint priv_data_size = 0;
1846 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1848 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1849 priv_data = g_malloc0 (priv_data_size);
1851 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1853 context->codec_priv = priv_data;
1854 context->codec_priv_size = priv_data_size;
1857 } else if (!strcmp (mimetype, "audio/x-wma")
1858 || !strcmp (mimetype, "audio/x-alaw")
1859 || !strcmp (mimetype, "audio/x-mulaw")) {
1861 guint codec_priv_size;
1866 if (samplerate == 0 || channels == 0) {
1867 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1871 if (!strcmp (mimetype, "audio/x-wma")) {
1875 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1876 || !gst_structure_get_int (structure, "block_align", &block_align)
1877 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1878 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1883 switch (wmaversion) {
1885 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1888 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1891 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1894 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1898 if (gst_structure_get_int (structure, "depth", &depth))
1899 audiocontext->bitdepth = depth;
1900 } else if (!strcmp (mimetype, "audio/x-alaw")
1901 || !strcmp (mimetype, "audio/x-mulaw")) {
1902 audiocontext->bitdepth = 8;
1903 if (!strcmp (mimetype, "audio/x-alaw"))
1904 format = GST_RIFF_WAVE_FORMAT_ALAW;
1906 format = GST_RIFF_WAVE_FORMAT_MULAW;
1908 block_align = channels;
1909 bitrate = block_align * samplerate;
1911 g_assert (format != 0);
1913 codec_priv_size = WAVEFORMATEX_SIZE;
1915 codec_priv_size += GST_BUFFER_SIZE (buf);
1917 /* serialize waveformatex structure */
1918 codec_priv = g_malloc0 (codec_priv_size);
1919 GST_WRITE_UINT16_LE (codec_priv, format);
1920 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1921 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1922 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1923 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1924 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1926 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1928 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1930 /* process codec private/initialization data, if any */
1932 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1933 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1936 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1937 context->codec_priv = (gpointer) codec_priv;
1938 context->codec_priv_size = codec_priv_size;
1946 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1947 GST_PAD_NAME (pad), caps);
1952 /* we probably don't have the data at start,
1953 * so have to reserve (a maximum) space to write this at the end.
1954 * bit spacy, but some formats can hold quite some */
1955 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1958 * gst_matroska_mux_subtitle_pad_setcaps:
1959 * @pad: Pad which got the caps.
1962 * Setcaps function for subtitle sink pad.
1964 * Returns: #TRUE on success.
1967 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1969 /* There is now (at least) one such alement (kateenc), and I'm going
1970 to handle it here and claim it works when it can be piped back
1971 through GStreamer and VLC */
1973 GstMatroskaTrackContext *context = NULL;
1974 GstMatroskaTrackSubtitleContext *scontext;
1975 GstMatroskaMux *mux;
1976 GstMatroskaPad *collect_pad;
1977 const gchar *mimetype;
1978 GstStructure *structure;
1979 const GValue *value = NULL;
1980 const GstBuffer *buf = NULL;
1982 gboolean ret = TRUE;
1984 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1987 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1988 g_assert (collect_pad);
1989 context = collect_pad->track;
1991 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1992 scontext = (GstMatroskaTrackSubtitleContext *) context;
1994 structure = gst_caps_get_structure (caps, 0);
1995 mimetype = gst_structure_get_name (structure);
1997 /* keep track of default set in request_pad */
1998 id = context->codec_id;
2001 scontext->check_utf8 = 1;
2002 scontext->invalid_utf8 = 0;
2003 context->default_duration = 0;
2005 if (!strcmp (mimetype, "subtitle/x-kate")) {
2006 const GValue *streamheader;
2008 gst_matroska_mux_set_codec_id (context,
2009 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2011 if (context->codec_priv != NULL) {
2012 g_free (context->codec_priv);
2013 context->codec_priv = NULL;
2014 context->codec_priv_size = 0;
2017 streamheader = gst_structure_get_value (structure, "streamheader");
2018 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2019 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2020 ("kate stream headers missing or malformed"));
2024 } else if (!strcmp (mimetype, "text/plain")) {
2025 gst_matroska_mux_set_codec_id (context,
2026 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2027 } else if (!strcmp (mimetype, "application/x-ssa")) {
2028 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2029 } else if (!strcmp (mimetype, "application/x-ass")) {
2030 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2031 } else if (!strcmp (mimetype, "application/x-usf")) {
2032 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2033 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2034 gst_matroska_mux_set_codec_id (context,
2035 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2042 /* maybe some private data, e.g. vobsub */
2043 value = gst_structure_get_value (structure, "codec_data");
2045 buf = gst_value_get_buffer (value);
2047 guint8 *priv_data = NULL;
2048 guint priv_data_size = 0;
2050 priv_data_size = GST_BUFFER_SIZE (buf);
2051 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2052 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2053 " exceeded maximum (%d); discarding", pad,
2054 SUBTITLE_MAX_CODEC_PRIVATE);
2058 if (context->codec_priv != NULL)
2059 g_free (context->codec_priv);
2061 priv_data = g_malloc0 (priv_data_size);
2062 memcpy (priv_data, GST_BUFFER_DATA (buf), priv_data_size);
2063 context->codec_priv = priv_data;
2064 context->codec_priv_size = priv_data_size;
2067 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2068 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2071 /* free default if modified */
2080 * gst_matroska_mux_request_new_pad:
2081 * @element: #GstMatroskaMux.
2082 * @templ: #GstPadTemplate.
2083 * @pad_name: New pad name.
2085 * Request pad function for sink templates.
2087 * Returns: New #GstPad.
2090 gst_matroska_mux_request_new_pad (GstElement * element,
2091 GstPadTemplate * templ, const gchar * req_name)
2093 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2094 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2095 GstMatroskaPad *collect_pad;
2096 GstMatroskamuxPad *newpad;
2098 const gchar *pad_name = NULL;
2099 GstPadSetCapsFunction setcapsfunc = NULL;
2100 GstMatroskaTrackContext *context = NULL;
2102 gboolean locked = TRUE;
2105 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
2106 /* don't mix named and unnamed pads, if the pad already exists we fail when
2107 * trying to add it */
2108 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
2109 pad_name = req_name;
2111 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
2114 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2115 context = (GstMatroskaTrackContext *)
2116 g_new0 (GstMatroskaTrackAudioContext, 1);
2117 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2118 context->name = g_strdup ("Audio");
2119 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
2120 /* don't mix named and unnamed pads, if the pad already exists we fail when
2121 * trying to add it */
2122 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
2123 pad_name = req_name;
2125 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
2128 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2129 context = (GstMatroskaTrackContext *)
2130 g_new0 (GstMatroskaTrackVideoContext, 1);
2131 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2132 context->name = g_strdup ("Video");
2133 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
2134 /* don't mix named and unnamed pads, if the pad already exists we fail when
2135 * trying to add it */
2136 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
2137 pad_name = req_name;
2139 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
2142 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2143 context = (GstMatroskaTrackContext *)
2144 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2145 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2146 context->name = g_strdup ("Subtitle");
2147 /* setcaps may only provide proper one a lot later */
2148 id = g_strdup ("S_SUB_UNKNOWN");
2151 GST_WARNING_OBJECT (mux, "This is not our template!");
2155 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2156 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2159 gst_matroskamux_pad_init (newpad);
2160 collect_pad = (GstMatroskaPad *)
2161 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2162 sizeof (GstMatroskamuxPad),
2163 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2165 collect_pad->track = context;
2166 gst_matroska_pad_reset (collect_pad, FALSE);
2167 collect_pad->track->codec_id = id;
2169 gst_pad_set_setcaps_function (GST_PAD (newpad), setcapsfunc);
2170 gst_pad_set_active (GST_PAD (newpad), TRUE);
2171 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2172 goto pad_add_failed;
2176 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2178 return GST_PAD (newpad);
2183 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2184 gst_object_unref (newpad);
2190 * gst_matroska_mux_release_pad:
2191 * @element: #GstMatroskaMux.
2192 * @pad: Pad to release.
2194 * Release a previously requested pad.
2197 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2199 GstMatroskaMux *mux;
2202 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2204 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2205 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2206 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2208 if (cdata->pad == pad) {
2209 GstClockTime min_dur; /* observed minimum duration */
2211 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2212 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2213 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2214 if (collect_pad->duration < min_dur)
2215 collect_pad->duration = min_dur;
2218 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2219 mux->duration < collect_pad->duration)
2220 mux->duration = collect_pad->duration;
2226 gst_collect_pads2_remove_pad (mux->collect, pad);
2227 if (gst_element_remove_pad (element, pad))
2233 * gst_matroska_mux_track_header:
2234 * @mux: #GstMatroskaMux
2235 * @context: Tack context.
2237 * Write a track header.
2240 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2241 GstMatroskaTrackContext * context)
2243 GstEbmlWrite *ebml = mux->ebml_write;
2246 /* TODO: check if everything necessary is written and check default values */
2248 /* track type goes before the type-specific stuff */
2249 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2250 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2252 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2253 gst_matroska_mux_create_uid ());
2254 if (context->default_duration) {
2255 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2256 context->default_duration);
2258 if (context->language) {
2259 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2263 /* FIXME: until we have a nice way of getting the codecname
2264 * out of the caps, I'm not going to enable this. Too much
2265 * (useless, double, boring) work... */
2266 /* TODO: Use value from tags if any */
2267 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2268 context->codec_name); */
2269 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2271 /* type-specific stuff */
2272 switch (context->type) {
2273 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2274 GstMatroskaTrackVideoContext *videocontext =
2275 (GstMatroskaTrackVideoContext *) context;
2277 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2278 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2279 videocontext->pixel_width);
2280 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2281 videocontext->pixel_height);
2282 if (videocontext->display_width && videocontext->display_height) {
2283 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2284 videocontext->display_width);
2285 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2286 videocontext->display_height);
2288 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2289 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2290 if (videocontext->fourcc) {
2291 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2293 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2294 (gpointer) & fcc_le, 4);
2296 gst_ebml_write_master_finish (ebml, master);
2301 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2302 GstMatroskaTrackAudioContext *audiocontext =
2303 (GstMatroskaTrackAudioContext *) context;
2305 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2306 if (audiocontext->samplerate != 8000)
2307 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2308 audiocontext->samplerate);
2309 if (audiocontext->channels != 1)
2310 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2311 audiocontext->channels);
2312 if (audiocontext->bitdepth) {
2313 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2314 audiocontext->bitdepth);
2316 gst_ebml_write_master_finish (ebml, master);
2321 /* this is what we write for now and must be filled
2322 * and remainder void'ed later on */
2323 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2325 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2328 context->pos = ebml->pos;
2329 /* CodecID is mandatory ... */
2330 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2332 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2333 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2334 SUBTITLE_MAX_CODEC_PRIVATE);
2336 /* real data has to be written at finish */
2340 /* doesn't need type-specific data */
2344 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2345 if (context->codec_priv)
2346 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2347 context->codec_priv, context->codec_priv_size);
2352 * gst_matroska_mux_start:
2353 * @mux: #GstMatroskaMux
2355 * Start a new matroska file (write headers etc...)
2358 gst_matroska_mux_start (GstMatroskaMux * mux)
2360 GstEbmlWrite *ebml = mux->ebml_write;
2361 const gchar *doctype;
2362 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2363 GST_MATROSKA_ID_TRACKS,
2364 GST_MATROSKA_ID_CUES,
2365 GST_MATROSKA_ID_TAGS,
2368 guint64 master, child;
2372 GstClockTime duration = 0;
2373 guint32 segment_uid[4];
2374 GTimeVal time = { 0, 0 };
2376 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2377 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2379 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2381 /* we start with a EBML header */
2382 doctype = mux->doctype;
2383 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2384 doctype, mux->doctype_version);
2385 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2387 /* the rest of the header is cached */
2388 gst_ebml_write_set_cache (ebml, 0x1000);
2390 /* start a segment */
2392 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2393 mux->segment_master = ebml->pos;
2395 if (!mux->streamable) {
2396 /* seekhead (table of contents) - we set the positions later */
2397 mux->seekhead_pos = ebml->pos;
2398 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2399 for (i = 0; seekhead_id[i] != 0; i++) {
2400 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2401 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2402 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2403 gst_ebml_write_master_finish (ebml, child);
2405 gst_ebml_write_master_finish (ebml, master);
2408 if (mux->streamable) {
2409 const GstTagList *tags;
2412 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2414 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2415 guint64 master_tags, master_tag;
2417 GST_DEBUG_OBJECT (mux, "Writing tags");
2419 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2420 mux->tags_pos = ebml->pos;
2421 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2422 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2423 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2424 gst_ebml_write_master_finish (ebml, master_tag);
2425 gst_ebml_write_master_finish (ebml, master_tags);
2430 mux->info_pos = ebml->pos;
2431 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2432 for (i = 0; i < 4; i++) {
2433 segment_uid[i] = g_random_int ();
2435 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2436 (guint8 *) segment_uid, 16);
2437 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2438 mux->duration_pos = ebml->pos;
2440 if (!mux->streamable) {
2441 for (collected = mux->collect->data; collected;
2442 collected = g_slist_next (collected)) {
2443 GstMatroskaPad *collect_pad;
2444 GstFormat format = GST_FORMAT_TIME;
2446 gint64 trackduration;
2448 collect_pad = (GstMatroskaPad *) collected->data;
2449 thepad = collect_pad->collect.pad;
2451 /* Query the total length of the track. */
2452 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2453 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2454 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2455 GST_TIME_ARGS (trackduration));
2456 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2457 duration = (GstClockTime) trackduration;
2461 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2462 gst_guint64_to_gdouble (duration) /
2463 gst_guint64_to_gdouble (mux->time_scale));
2465 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2466 "GStreamer plugin version " PACKAGE_VERSION);
2467 if (mux->writing_app && mux->writing_app[0]) {
2468 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2470 g_get_current_time (&time);
2471 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2472 gst_ebml_write_master_finish (ebml, master);
2475 mux->tracks_pos = ebml->pos;
2476 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2478 for (collected = mux->collect->data; collected;
2479 collected = g_slist_next (collected)) {
2480 GstMatroskaPad *collect_pad;
2483 collect_pad = (GstMatroskaPad *) collected->data;
2484 thepad = collect_pad->collect.pad;
2486 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2487 collect_pad->track->codec_id != 0) {
2488 collect_pad->track->num = tracknum++;
2489 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2490 gst_matroska_mux_track_header (mux, collect_pad->track);
2491 gst_ebml_write_master_finish (ebml, child);
2492 /* some remaining pad/track setup */
2493 collect_pad->default_duration_scaled =
2494 gst_util_uint64_scale (collect_pad->track->default_duration,
2495 1, mux->time_scale);
2498 gst_ebml_write_master_finish (ebml, master);
2500 /* lastly, flush the cache */
2501 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2505 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2508 /* TODO: more sensible tag mappings */
2511 const gchar *matroska_tagname;
2512 const gchar *gstreamer_tagname;
2516 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2517 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2518 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2519 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2520 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2521 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2522 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2523 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2524 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2525 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2526 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2527 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2528 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2529 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2530 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2532 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2534 guint64 simpletag_master;
2536 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2537 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2538 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2540 if (strcmp (tagname_gst, tag) == 0) {
2541 GValue src = { 0, };
2544 if (!gst_tag_list_copy_value (&src, list, tag))
2546 if ((dest = gst_value_serialize (&src))) {
2548 simpletag_master = gst_ebml_write_master_start (ebml,
2549 GST_MATROSKA_ID_SIMPLETAG);
2550 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2551 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2552 gst_ebml_write_master_finish (ebml, simpletag_master);
2555 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2557 g_value_unset (&src);
2565 * gst_matroska_mux_finish:
2566 * @mux: #GstMatroskaMux
2568 * Finish a new matroska file (write index etc...)
2571 gst_matroska_mux_finish (GstMatroskaMux * mux)
2573 GstEbmlWrite *ebml = mux->ebml_write;
2575 guint64 duration = 0;
2577 const GstTagList *tags;
2579 /* finish last cluster */
2581 gst_ebml_write_master_finish (ebml, mux->cluster);
2585 if (mux->index != NULL) {
2587 guint64 master, pointentry_master, trackpos_master;
2589 mux->cues_pos = ebml->pos;
2590 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2591 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2593 for (n = 0; n < mux->num_indexes; n++) {
2594 GstMatroskaIndex *idx = &mux->index[n];
2596 pointentry_master = gst_ebml_write_master_start (ebml,
2597 GST_MATROSKA_ID_POINTENTRY);
2598 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2599 idx->time / mux->time_scale);
2600 trackpos_master = gst_ebml_write_master_start (ebml,
2601 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2602 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2603 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2604 idx->pos - mux->segment_master);
2605 gst_ebml_write_master_finish (ebml, trackpos_master);
2606 gst_ebml_write_master_finish (ebml, pointentry_master);
2609 gst_ebml_write_master_finish (ebml, master);
2610 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2614 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2616 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2617 guint64 master_tags, master_tag;
2619 GST_DEBUG_OBJECT (mux, "Writing tags");
2621 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2622 mux->tags_pos = ebml->pos;
2623 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2624 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2625 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2626 gst_ebml_write_master_finish (ebml, master_tag);
2627 gst_ebml_write_master_finish (ebml, master_tags);
2630 /* update seekhead. We know that:
2631 * - a seekhead contains 4 entries.
2632 * - order of entries is as above.
2633 * - a seekhead has a 4-byte header + 8-byte length
2634 * - each entry is 2-byte master, 2-byte ID pointer,
2635 * 2-byte length pointer, all 8/1-byte length, 4-
2636 * byte ID and 8-byte length pointer, where the
2637 * length pointer starts at 20.
2638 * - all entries are local to the segment (so pos - segment_master).
2639 * - so each entry is at 12 + 20 + num * 28. */
2640 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2641 mux->info_pos - mux->segment_master);
2642 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2643 mux->tracks_pos - mux->segment_master);
2644 if (mux->index != NULL) {
2645 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2646 mux->cues_pos - mux->segment_master);
2649 guint64 my_pos = ebml->pos;
2651 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2652 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2653 gst_ebml_write_seek (ebml, my_pos);
2656 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2657 mux->tags_pos - mux->segment_master);
2660 guint64 my_pos = ebml->pos;
2662 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2663 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2664 gst_ebml_write_seek (ebml, my_pos);
2668 * - first get the overall duration
2669 * (a released track may have left a duration in here)
2670 * - write some track header data for subtitles
2672 duration = mux->duration;
2674 for (collected = mux->collect->data; collected;
2675 collected = g_slist_next (collected)) {
2676 GstMatroskaPad *collect_pad;
2677 GstClockTime min_duration; /* observed minimum duration */
2678 GstMatroskaTrackContext *context;
2679 gint voidleft = 0, fill = 0;
2682 collect_pad = (GstMatroskaPad *) collected->data;
2683 context = collect_pad->track;
2685 GST_DEBUG_OBJECT (mux,
2686 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2687 " end ts %" GST_TIME_FORMAT, collect_pad,
2688 GST_TIME_ARGS (collect_pad->start_ts),
2689 GST_TIME_ARGS (collect_pad->end_ts));
2691 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2692 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2694 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2695 if (collect_pad->duration < min_duration)
2696 collect_pad->duration = min_duration;
2697 GST_DEBUG_OBJECT (collect_pad,
2698 "final track duration: %" GST_TIME_FORMAT,
2699 GST_TIME_ARGS (collect_pad->duration));
2702 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2703 duration < collect_pad->duration)
2704 duration = collect_pad->duration;
2706 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2710 /* write subtitle type and possible private data */
2711 gst_ebml_write_seek (ebml, context->pos);
2712 /* complex way to write ascii to account for extra filling */
2713 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2714 strcpy (codec_id, context->codec_id);
2715 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2716 codec_id, strlen (context->codec_id) + 1 + fill);
2718 if (context->codec_priv)
2719 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2720 context->codec_priv, context->codec_priv_size);
2721 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2722 /* void'ify; sigh, variable sized length field */
2723 if (voidleft == 1) {
2726 } else if (voidleft && voidleft <= 128)
2727 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2728 else if (voidleft >= 130)
2729 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2730 else if (voidleft == 129) {
2731 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2732 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2736 /* seek back (optional, but do anyway) */
2737 gst_ebml_write_seek (ebml, pos);
2739 /* update duration */
2740 if (duration != 0) {
2741 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2742 GST_TIME_ARGS (duration));
2743 pos = mux->ebml_write->pos;
2744 gst_ebml_write_seek (ebml, mux->duration_pos);
2745 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2746 gst_guint64_to_gdouble (duration) /
2747 gst_guint64_to_gdouble (mux->time_scale));
2748 gst_ebml_write_seek (ebml, pos);
2751 guint64 my_pos = ebml->pos;
2753 gst_ebml_write_seek (ebml, mux->duration_pos);
2754 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2755 gst_ebml_write_seek (ebml, my_pos);
2757 GST_DEBUG_OBJECT (mux, "finishing segment");
2758 /* finish segment - this also writes element length */
2759 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2763 * gst_matroska_mux_buffer_header:
2764 * @track: Track context.
2765 * @relative_timestamp: relative timestamp of the buffer
2766 * @flags: Buffer flags.
2768 * Create a buffer containing buffer header.
2770 * Returns: New buffer.
2773 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2774 gint16 relative_timestamp, int flags)
2778 hdr = gst_buffer_new_and_alloc (4);
2779 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2780 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2781 /* time relative to clustertime */
2782 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2785 GST_BUFFER_DATA (hdr)[3] = flags;
2790 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2791 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2792 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2795 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2796 GstMatroskaPad * collect_pad, GstBuffer * buf)
2798 GstMatroskaTrackVideoContext *ctx =
2799 (GstMatroskaTrackVideoContext *) collect_pad->track;
2800 const guint8 *data = GST_BUFFER_DATA (buf);
2801 guint size = GST_BUFFER_SIZE (buf);
2803 guint32 next_parse_offset;
2804 GstBuffer *ret = NULL;
2805 gboolean is_muxing_unit = FALSE;
2807 if (GST_BUFFER_SIZE (buf) < 13) {
2808 gst_buffer_unref (buf);
2812 /* Check if this buffer contains a picture or end-of-sequence packet */
2813 while (size >= 13) {
2814 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2815 gst_buffer_unref (buf);
2819 parse_code = GST_READ_UINT8 (data + 4);
2820 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2821 if (ctx->dirac_unit) {
2822 gst_buffer_unref (ctx->dirac_unit);
2823 ctx->dirac_unit = NULL;
2825 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2826 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2827 is_muxing_unit = TRUE;
2831 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2833 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2836 data += next_parse_offset;
2837 size -= next_parse_offset;
2840 if (ctx->dirac_unit)
2841 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2843 ctx->dirac_unit = gst_buffer_ref (buf);
2845 if (is_muxing_unit) {
2846 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2847 ctx->dirac_unit = NULL;
2848 gst_buffer_copy_metadata (ret, buf,
2849 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2850 GST_BUFFER_COPY_CAPS);
2851 gst_buffer_unref (buf);
2853 gst_buffer_unref (buf);
2861 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2865 GValue streamheader = { 0 };
2866 GValue bufval = { 0 };
2867 GstBuffer *streamheader_buffer;
2868 GstEbmlWrite *ebml = mux->ebml_write;
2870 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2871 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2872 caps = gst_caps_new_simple ("video/webm", NULL);
2874 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2876 s = gst_caps_get_structure (caps, 0);
2877 g_value_init (&streamheader, GST_TYPE_ARRAY);
2878 g_value_init (&bufval, GST_TYPE_BUFFER);
2879 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2880 gst_value_set_buffer (&bufval, streamheader_buffer);
2881 gst_value_array_append_value (&streamheader, &bufval);
2882 g_value_unset (&bufval);
2883 gst_structure_set_value (s, "streamheader", &streamheader);
2884 g_value_unset (&streamheader);
2885 gst_caps_replace (&ebml->caps, caps);
2886 gst_buffer_unref (streamheader_buffer);
2887 gst_caps_unref (caps);
2891 * gst_matroska_mux_write_data:
2892 * @mux: #GstMatroskaMux
2893 * @collect_pad: #GstMatroskaPad with the data
2895 * Write collected data (called from gst_matroska_mux_collected).
2897 * Returns: Result of the gst_pad_push issued to write the data.
2899 static GstFlowReturn
2900 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2903 GstEbmlWrite *ebml = mux->ebml_write;
2906 gboolean write_duration;
2907 gint16 relative_timestamp;
2908 gint64 relative_timestamp64;
2909 guint64 block_duration;
2910 gboolean is_video_keyframe = FALSE;
2911 GstMatroskamuxPad *pad;
2914 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2916 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2917 if (collect_pad->track->xiph_headers_to_skip > 0) {
2918 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2919 gst_buffer_unref (buf);
2920 --collect_pad->track->xiph_headers_to_skip;
2924 /* for dirac we have to queue up everything up to a picture unit */
2925 if (collect_pad->track->codec_id != NULL &&
2926 strcmp (collect_pad->track->codec_id,
2927 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2928 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2933 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2934 * this would wreak havoc with time stored in matroska file */
2935 /* TODO: maybe calculate a timestamp by using the previous timestamp
2936 * and default duration */
2937 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2938 GST_WARNING_OBJECT (collect_pad->collect.pad,
2939 "Invalid buffer timestamp; dropping buffer");
2940 gst_buffer_unref (buf);
2944 /* set the timestamp for outgoing buffers */
2945 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2947 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2948 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2949 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2950 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2951 is_video_keyframe = TRUE;
2955 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2956 * or when we may be reaching the limit of the relative timestamp */
2957 if (mux->cluster_time +
2958 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2959 || is_video_keyframe || mux->force_key_unit_event) {
2960 if (!mux->streamable)
2961 gst_ebml_write_master_finish (ebml, mux->cluster);
2963 /* Forward the GstForceKeyUnit event after finishing the cluster */
2964 if (mux->force_key_unit_event) {
2965 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2966 mux->force_key_unit_event = NULL;
2969 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2970 mux->cluster_pos = ebml->pos;
2971 gst_ebml_write_set_cache (ebml, 0x20);
2973 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2974 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2975 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2977 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2978 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2980 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2981 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2982 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2983 mux->prev_cluster_size);
2988 mux->cluster_pos = ebml->pos;
2989 gst_ebml_write_set_cache (ebml, 0x20);
2990 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2991 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2992 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2993 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2994 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2997 /* update duration of this track */
2998 if (GST_BUFFER_DURATION_IS_VALID (buf))
2999 collect_pad->duration += GST_BUFFER_DURATION (buf);
3001 /* We currently write index entries for all video tracks or for the audio
3002 * track in a single-track audio file. This could be improved by keeping the
3003 * index only for the *first* video track. */
3005 /* TODO: index is useful for every track, should contain the number of
3006 * the block in the cluster which contains the timestamp, should also work
3007 * for files with multiple audio tracks.
3009 if (!mux->streamable &&
3010 (is_video_keyframe ||
3011 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3012 (mux->num_streams == 1)))) {
3015 if (mux->min_index_interval != 0) {
3016 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3017 if (mux->index[last_idx].track == collect_pad->track->num)
3022 if (last_idx < 0 || mux->min_index_interval == 0 ||
3023 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3024 >= mux->min_index_interval)) {
3025 GstMatroskaIndex *idx;
3027 if (mux->num_indexes % 32 == 0) {
3028 mux->index = g_renew (GstMatroskaIndex, mux->index,
3029 mux->num_indexes + 32);
3031 idx = &mux->index[mux->num_indexes++];
3033 idx->pos = mux->cluster_pos;
3034 idx->time = GST_BUFFER_TIMESTAMP (buf);
3035 idx->track = collect_pad->track->num;
3039 /* Check if the duration differs from the default duration. */
3040 write_duration = FALSE;
3042 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3043 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3044 1, mux->time_scale);
3046 /* small difference should be ok. */
3047 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3048 block_duration < collect_pad->default_duration_scaled - 1) {
3049 write_duration = TRUE;
3053 /* write the block, for doctype v2 use SimpleBlock if possible
3054 * one slice (*breath*).
3055 * FIXME: Need to do correct lacing! */
3056 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3057 if (relative_timestamp64 >= 0) {
3058 /* round the timestamp */
3059 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3061 /* round the timestamp */
3062 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3064 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3066 if (mux->doctype_version > 1 && !write_duration) {
3068 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3071 gst_matroska_mux_create_buffer_header (collect_pad->track,
3072 relative_timestamp, flags);
3073 gst_ebml_write_set_cache (ebml, 0x40);
3074 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3075 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3076 gst_ebml_write_buffer (ebml, hdr);
3077 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3078 gst_ebml_write_buffer (ebml, buf);
3080 return gst_ebml_last_write_result (ebml);
3082 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
3083 /* write and call order slightly unnatural,
3084 * but avoids seek and minizes pushing */
3085 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3087 gst_matroska_mux_create_buffer_header (collect_pad->track,
3088 relative_timestamp, 0);
3090 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3091 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3092 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3093 gst_ebml_write_buffer (ebml, hdr);
3094 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
3095 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3096 gst_ebml_write_buffer (ebml, buf);
3098 return gst_ebml_last_write_result (ebml);
3103 * gst_matroska_mux_handle_buffer:
3104 * @pads: #GstCollectPads2
3105 * @uuser_data: #GstMatroskaMux
3107 * Collectpads callback.
3109 * Returns: #GstFlowReturn
3111 static GstFlowReturn
3112 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3113 GstBuffer * buf, gpointer user_data)
3115 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3116 GstEbmlWrite *ebml = mux->ebml_write;
3117 GstMatroskaPad *best;
3118 GstFlowReturn ret = GST_FLOW_OK;
3120 GST_DEBUG_OBJECT (mux, "Collected pads");
3122 /* start with a header */
3123 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3124 if (mux->collect->data == NULL) {
3125 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3126 ("No input streams configured"));
3127 return GST_FLOW_ERROR;
3129 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3130 gst_ebml_start_streamheader (ebml);
3131 gst_matroska_mux_start (mux);
3132 gst_matroska_mux_stop_streamheader (mux);
3133 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3136 /* provided with stream to write from */
3137 best = (GstMatroskaPad *) data;
3139 /* if there is no best pad, we have reached EOS */
3141 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3142 if (!mux->streamable) {
3143 gst_matroska_mux_finish (mux);
3145 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3147 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3148 ret = GST_FLOW_UNEXPECTED;
3152 /* if we have a best stream, should also have a buffer */
3155 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3156 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3157 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3158 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3160 /* make note of first and last encountered timestamps, so we can calculate
3161 * the actual duration later when we send an updated header on eos */
3162 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3163 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3164 GstClockTime end_ts = start_ts;
3166 if (GST_BUFFER_DURATION_IS_VALID (buf))
3167 end_ts += GST_BUFFER_DURATION (buf);
3168 else if (best->track->default_duration)
3169 end_ts += best->track->default_duration;
3171 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3172 best->end_ts = end_ts;
3174 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3175 start_ts < best->start_ts))
3176 best->start_ts = start_ts;
3179 /* write one buffer */
3180 ret = gst_matroska_mux_write_data (mux, best, buf);
3188 * gst_matroska_mux_change_state:
3189 * @element: #GstMatroskaMux
3190 * @transition: State change transition.
3192 * Change the muxer state.
3194 * Returns: #GstStateChangeReturn
3196 static GstStateChangeReturn
3197 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3199 GstStateChangeReturn ret;
3200 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3202 switch (transition) {
3203 case GST_STATE_CHANGE_NULL_TO_READY:
3205 case GST_STATE_CHANGE_READY_TO_PAUSED:
3206 gst_collect_pads2_start (mux->collect);
3208 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3210 case GST_STATE_CHANGE_PAUSED_TO_READY:
3211 gst_collect_pads2_stop (mux->collect);
3217 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3219 switch (transition) {
3220 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3222 case GST_STATE_CHANGE_PAUSED_TO_READY:
3223 gst_matroska_mux_reset (GST_ELEMENT (mux));
3225 case GST_STATE_CHANGE_READY_TO_NULL:
3235 gst_matroska_mux_set_property (GObject * object,
3236 guint prop_id, const GValue * value, GParamSpec * pspec)
3238 GstMatroskaMux *mux;
3240 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3241 mux = GST_MATROSKA_MUX (object);
3244 case ARG_WRITING_APP:
3245 if (!g_value_get_string (value)) {
3246 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3249 g_free (mux->writing_app);
3250 mux->writing_app = g_value_dup_string (value);
3252 case ARG_DOCTYPE_VERSION:
3253 mux->doctype_version = g_value_get_int (value);
3255 case ARG_MIN_INDEX_INTERVAL:
3256 mux->min_index_interval = g_value_get_int64 (value);
3258 case ARG_STREAMABLE:
3259 mux->streamable = g_value_get_boolean (value);
3262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3268 gst_matroska_mux_get_property (GObject * object,
3269 guint prop_id, GValue * value, GParamSpec * pspec)
3271 GstMatroskaMux *mux;
3273 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3274 mux = GST_MATROSKA_MUX (object);
3277 case ARG_WRITING_APP:
3278 g_value_set_string (value, mux->writing_app);
3280 case ARG_DOCTYPE_VERSION:
3281 g_value_set_int (value, mux->doctype_version);
3283 case ARG_MIN_INDEX_INTERVAL:
3284 g_value_set_int64 (value, mux->min_index_interval);
3286 case ARG_STREAMABLE:
3287 g_value_set_boolean (value, mux->streamable);
3290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);