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);
707 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
709 if (context->codec_priv != NULL) {
710 g_free (context->codec_priv);
711 context->codec_priv = NULL;
712 context->codec_priv_size = 0;
717 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
727 /* produce comma-separated list in hex format */
728 for (i = 0; i < 16; ++i) {
730 /* replicate vobsub's slightly off RGB conversion calculation */
731 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
732 u = ((col >> 8) & 0xff) - 128;
733 v = (col & 0xff) - 128;
734 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
735 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
736 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
737 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
740 sclut = g_strjoinv (",", clutv);
742 /* build codec private; only palette for now */
743 gst_matroska_mux_free_codec_priv (context);
744 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
745 /* include terminating 0 */
746 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
748 for (i = 0; i < 16; ++i) {
755 * gst_matroska_mux_handle_sink_event:
756 * @pad: Pad which received the event.
757 * @event: Received event.
759 * handle events - informational ones like tags
761 * Returns: #TRUE on success.
764 gst_matroska_mux_handle_sink_event (GstCollectPads2 * pads,
765 GstCollectData2 * data, GstEvent * event, gpointer user_data)
767 GstMatroskaTrackContext *context;
768 GstMatroskaPad *collect_pad;
773 mux = GST_MATROSKA_MUX (user_data);
774 collect_pad = (GstMatroskaPad *) data;
776 context = collect_pad->track;
779 switch (GST_EVENT_TYPE (event)) {
783 GST_DEBUG_OBJECT (mux, "received tag event");
784 gst_event_parse_tag (event, &list);
786 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
787 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
788 const gchar *lang_code;
790 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
792 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
793 context->language = g_strdup (lang_code);
795 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
800 /* FIXME: what about stream-specific tags? */
801 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
802 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
804 gst_event_unref (event);
805 /* handled this, don't want collectpads to forward it downstream */
809 case GST_EVENT_NEWSEGMENT:{
812 gst_event_parse_new_segment (event, NULL, NULL, &format, NULL, NULL,
814 if (format != GST_FORMAT_TIME) {
815 gst_event_unref (event);
820 case GST_EVENT_CUSTOM_DOWNSTREAM:{
821 const GstStructure *structure;
823 structure = gst_event_get_structure (event);
824 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
825 gst_event_replace (&mux->force_key_unit_event, NULL);
826 mux->force_key_unit_event = event;
828 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
829 !strcmp ("dvd-spu-clut-change",
830 gst_structure_get_string (structure, "event"))) {
835 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
836 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
837 GST_DEBUG_OBJECT (pad, "... discarding");
840 /* first transform event data into table form */
841 for (i = 0; i < 16; i++) {
842 g_snprintf (name, sizeof (name), "clut%02d", i);
843 if (!gst_structure_get_int (structure, name, &value)) {
844 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
845 "contain %s field", name);
851 /* transform into private data for stream; text form */
852 gst_matroska_mux_build_vobsub_private (context, clut);
860 /* now GstCollectPads2 can take care of the rest, e.g. EOS */
868 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
871 g_assert (context && id);
872 if (context->codec_id)
873 g_free (context->codec_id);
874 context->codec_id = g_strdup (id);
878 * gst_matroska_mux_video_pad_setcaps:
879 * @pad: Pad which got the caps.
882 * Setcaps function for video sink pad.
884 * Returns: #TRUE on success.
887 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
889 GstMatroskaTrackContext *context = NULL;
890 GstMatroskaTrackVideoContext *videocontext;
892 GstMatroskaPad *collect_pad;
893 GstStructure *structure;
894 const gchar *mimetype;
895 const GValue *value = NULL;
896 const GstBuffer *codec_buf = NULL;
897 gint width, height, pixel_width, pixel_height;
899 gboolean interlaced = FALSE;
901 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
904 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
905 g_assert (collect_pad);
906 context = collect_pad->track;
908 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
909 videocontext = (GstMatroskaTrackVideoContext *) context;
911 /* gst -> matroska ID'ing */
912 structure = gst_caps_get_structure (caps, 0);
914 mimetype = gst_structure_get_name (structure);
916 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
918 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
920 if (!strcmp (mimetype, "video/x-theora")) {
921 /* we'll extract the details later from the theora identification header */
925 /* get general properties */
926 /* spec says it is mandatory */
927 if (!gst_structure_get_int (structure, "width", &width) ||
928 !gst_structure_get_int (structure, "height", &height))
931 videocontext->pixel_width = width;
932 videocontext->pixel_height = height;
934 /* set vp8 defaults or let user override it */
935 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration_user == FALSE
936 && (!strcmp (mimetype, "video/x-vp8")))
937 GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration =
938 DEFAULT_PAD_FRAME_DURATION_VP8;
940 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
941 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
943 context->default_duration =
944 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
945 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
946 GST_TIME_ARGS (context->default_duration));
948 context->default_duration = 0;
950 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
951 &pixel_width, &pixel_height)) {
952 if (pixel_width > pixel_height) {
953 videocontext->display_width = width * pixel_width / pixel_height;
954 videocontext->display_height = height;
955 } else if (pixel_width < pixel_height) {
956 videocontext->display_width = width;
957 videocontext->display_height = height * pixel_height / pixel_width;
959 videocontext->display_width = 0;
960 videocontext->display_height = 0;
963 videocontext->display_width = 0;
964 videocontext->display_height = 0;
969 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
970 videocontext->fourcc = 0;
972 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
973 * data and other settings
977 /* extract codec_data, may turn out needed */
978 value = gst_structure_get_value (structure, "codec_data");
980 codec_buf = gst_value_get_buffer (value);
983 if (!strcmp (mimetype, "video/x-raw-yuv")) {
984 gst_matroska_mux_set_codec_id (context,
985 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
986 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
987 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
988 ||!strcmp (mimetype, "video/x-huffyuv")
989 || !strcmp (mimetype, "video/x-divx")
990 || !strcmp (mimetype, "video/x-dv")
991 || !strcmp (mimetype, "video/x-h263")
992 || !strcmp (mimetype, "video/x-msmpeg")
993 || !strcmp (mimetype, "video/x-wmv")
994 || !strcmp (mimetype, "image/jpeg")) {
995 gst_riff_strf_vids *bih;
996 gint size = sizeof (gst_riff_strf_vids);
999 if (!strcmp (mimetype, "video/x-xvid"))
1000 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
1001 else if (!strcmp (mimetype, "video/x-huffyuv"))
1002 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1003 else if (!strcmp (mimetype, "video/x-dv"))
1004 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1005 else if (!strcmp (mimetype, "video/x-h263"))
1006 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1007 else if (!strcmp (mimetype, "video/x-divx")) {
1010 gst_structure_get_int (structure, "divxversion", &divxversion);
1011 switch (divxversion) {
1013 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1016 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1019 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1022 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1025 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1026 switch (msmpegversion) {
1028 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1031 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1037 } else if (!strcmp (mimetype, "video/x-wmv")) {
1040 if (gst_structure_get_fourcc (structure, "format", &format)) {
1042 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1043 if (wmvversion == 2) {
1044 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1045 } else if (wmvversion == 1) {
1046 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1047 } else if (wmvversion == 3) {
1048 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1051 } else if (!strcmp (mimetype, "image/jpeg")) {
1052 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1058 bih = g_new0 (gst_riff_strf_vids, 1);
1059 GST_WRITE_UINT32_LE (&bih->size, size);
1060 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1061 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1062 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1063 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1064 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1065 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1066 videocontext->pixel_height * 3);
1068 /* process codec private/initialization data, if any */
1070 size += GST_BUFFER_SIZE (codec_buf);
1071 bih = g_realloc (bih, size);
1072 GST_WRITE_UINT32_LE (&bih->size, size);
1073 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
1074 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
1077 gst_matroska_mux_set_codec_id (context,
1078 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1079 gst_matroska_mux_free_codec_priv (context);
1080 context->codec_priv = (gpointer) bih;
1081 context->codec_priv_size = size;
1082 } else if (!strcmp (mimetype, "video/x-h264")) {
1083 gst_matroska_mux_set_codec_id (context,
1084 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1085 gst_matroska_mux_free_codec_priv (context);
1086 /* Create avcC header */
1087 if (codec_buf != NULL) {
1088 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1089 context->codec_priv = g_malloc0 (context->codec_priv_size);
1090 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1091 context->codec_priv_size);
1093 } else if (!strcmp (mimetype, "video/x-theora")) {
1094 const GValue *streamheader;
1096 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1098 gst_matroska_mux_free_codec_priv (context);
1100 streamheader = gst_structure_get_value (structure, "streamheader");
1101 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1102 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1103 ("theora stream headers missing or malformed"));
1106 } else if (!strcmp (mimetype, "video/x-dirac")) {
1107 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1108 } else if (!strcmp (mimetype, "video/x-vp8")) {
1109 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1110 } else if (!strcmp (mimetype, "video/mpeg")) {
1113 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1114 switch (mpegversion) {
1116 gst_matroska_mux_set_codec_id (context,
1117 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1120 gst_matroska_mux_set_codec_id (context,
1121 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1124 gst_matroska_mux_set_codec_id (context,
1125 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1131 /* global headers may be in codec data */
1132 if (codec_buf != NULL) {
1133 gst_matroska_mux_free_codec_priv (context);
1134 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
1135 context->codec_priv = g_malloc0 (context->codec_priv_size);
1136 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
1137 context->codec_priv_size);
1139 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1141 /* can only make it here if preceding case verified it was version 3 */
1142 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1143 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1145 const GValue *mdpr_data;
1147 gst_structure_get_int (structure, "rmversion", &rmversion);
1148 switch (rmversion) {
1150 gst_matroska_mux_set_codec_id (context,
1151 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1154 gst_matroska_mux_set_codec_id (context,
1155 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1158 gst_matroska_mux_set_codec_id (context,
1159 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1162 gst_matroska_mux_set_codec_id (context,
1163 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1169 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1170 if (mdpr_data != NULL) {
1171 guint8 *priv_data = NULL;
1172 guint priv_data_size = 0;
1174 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1176 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1177 priv_data = g_malloc0 (priv_data_size);
1179 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1181 gst_matroska_mux_free_codec_priv (context);
1182 context->codec_priv = priv_data;
1183 context->codec_priv_size = priv_data_size;
1192 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1193 GST_PAD_NAME (pad), caps);
1198 /* N > 0 to expect a particular number of headers, negative if the
1199 number of headers is variable */
1201 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1202 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1204 GstBuffer **buf = NULL;
1207 guint bufi, i, offset, priv_data_size;
1209 if (streamheader == NULL)
1210 goto no_stream_headers;
1212 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1215 bufarr = g_value_peek_pointer (streamheader);
1216 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1218 if (N > 0 && bufarr->len != N)
1221 context->xiph_headers_to_skip = bufarr->len;
1223 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1224 for (i = 0; i < bufarr->len; i++) {
1225 GValue *bufval = &g_array_index (bufarr, GValue, i);
1227 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1229 goto wrong_content_type;
1232 buf[i] = g_value_peek_pointer (bufval);
1236 if (bufarr->len > 0) {
1237 for (i = 0; i < bufarr->len - 1; i++) {
1238 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1242 for (i = 0; i < bufarr->len; ++i) {
1243 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1246 priv_data = g_malloc0 (priv_data_size);
1248 priv_data[0] = bufarr->len - 1;
1251 if (bufarr->len > 0) {
1252 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1253 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1254 priv_data[offset++] = 0xff;
1256 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1260 for (i = 0; i < bufarr->len; ++i) {
1261 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1262 GST_BUFFER_SIZE (buf[i]));
1263 offset += GST_BUFFER_SIZE (buf[i]);
1266 gst_matroska_mux_free_codec_priv (context);
1267 context->codec_priv = priv_data;
1268 context->codec_priv_size = priv_data_size;
1271 *p_buf0 = gst_buffer_ref (buf[0]);
1280 GST_WARNING ("required streamheaders missing in sink caps!");
1285 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1286 G_VALUE_TYPE_NAME (streamheader));
1291 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1296 GST_WARNING ("streamheaders array does not contain GstBuffers");
1302 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1303 GstMatroskaTrackContext * context)
1305 GstBuffer *buf0 = NULL;
1307 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1310 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1311 GST_WARNING ("First vorbis header too small, ignoring");
1313 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1314 GstMatroskaTrackAudioContext *audiocontext;
1317 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1318 audiocontext = (GstMatroskaTrackAudioContext *) context;
1319 audiocontext->channels = GST_READ_UINT8 (hdr);
1320 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1325 gst_buffer_unref (buf0);
1331 theora_streamheader_to_codecdata (const GValue * streamheader,
1332 GstMatroskaTrackContext * context)
1334 GstBuffer *buf0 = NULL;
1336 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1339 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1340 GST_WARNING ("First theora header too small, ignoring");
1341 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1342 GST_WARNING ("First header not a theora identification header, ignoring");
1344 GstMatroskaTrackVideoContext *videocontext;
1345 guint fps_num, fps_denom, par_num, par_denom;
1348 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1350 videocontext = (GstMatroskaTrackVideoContext *) context;
1351 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1352 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1353 hdr += 3 + 3 + 1 + 1;
1354 fps_num = GST_READ_UINT32_BE (hdr);
1355 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1356 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1357 fps_denom, fps_num);
1359 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1360 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1361 if (par_num > 0 && par_num > 0) {
1362 if (par_num > par_denom) {
1363 videocontext->display_width =
1364 videocontext->pixel_width * par_num / par_denom;
1365 videocontext->display_height = videocontext->pixel_height;
1366 } else if (par_num < par_denom) {
1367 videocontext->display_width = videocontext->pixel_width;
1368 videocontext->display_height =
1369 videocontext->pixel_height * par_denom / par_num;
1371 videocontext->display_width = 0;
1372 videocontext->display_height = 0;
1375 videocontext->display_width = 0;
1376 videocontext->display_height = 0;
1382 gst_buffer_unref (buf0);
1388 kate_streamheader_to_codecdata (const GValue * streamheader,
1389 GstMatroskaTrackContext * context)
1391 GstBuffer *buf0 = NULL;
1393 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1396 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1397 GST_WARNING ("First kate header too small, ignoring");
1398 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1399 GST_WARNING ("First header not a kate identification header, ignoring");
1403 gst_buffer_unref (buf0);
1409 flac_streamheader_to_codecdata (const GValue * streamheader,
1410 GstMatroskaTrackContext * context)
1417 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1418 GST_WARNING ("No or invalid streamheader field in the caps");
1422 bufarr = g_value_peek_pointer (streamheader);
1423 if (bufarr->len < 2) {
1424 GST_WARNING ("Too few headers in streamheader field");
1428 context->xiph_headers_to_skip = bufarr->len + 1;
1430 bufval = &g_array_index (bufarr, GValue, 0);
1431 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1432 GST_WARNING ("streamheaders array does not contain GstBuffers");
1436 buffer = g_value_peek_pointer (bufval);
1438 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1439 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1440 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1441 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1442 GST_WARNING ("Invalid streamheader for FLAC");
1446 gst_matroska_mux_free_codec_priv (context);
1447 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1448 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1449 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1450 GST_BUFFER_SIZE (buffer) - 9);
1452 for (i = 1; i < bufarr->len; i++) {
1453 bufval = &g_array_index (bufarr, GValue, i);
1455 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1456 gst_matroska_mux_free_codec_priv (context);
1457 GST_WARNING ("streamheaders array does not contain GstBuffers");
1461 buffer = g_value_peek_pointer (bufval);
1463 context->codec_priv =
1464 g_realloc (context->codec_priv,
1465 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1466 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1467 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1468 context->codec_priv_size =
1469 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1476 speex_streamheader_to_codecdata (const GValue * streamheader,
1477 GstMatroskaTrackContext * context)
1483 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1484 GST_WARNING ("No or invalid streamheader field in the caps");
1488 bufarr = g_value_peek_pointer (streamheader);
1489 if (bufarr->len != 2) {
1490 GST_WARNING ("Too few headers in streamheader field");
1494 context->xiph_headers_to_skip = bufarr->len + 1;
1496 bufval = &g_array_index (bufarr, GValue, 0);
1497 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1498 GST_WARNING ("streamheaders array does not contain GstBuffers");
1502 buffer = g_value_peek_pointer (bufval);
1504 if (GST_BUFFER_SIZE (buffer) < 80
1505 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1506 GST_WARNING ("Invalid streamheader for Speex");
1510 gst_matroska_mux_free_codec_priv (context);
1511 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1512 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1513 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1514 GST_BUFFER_SIZE (buffer));
1516 bufval = &g_array_index (bufarr, GValue, 1);
1518 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1519 gst_matroska_mux_free_codec_priv (context);
1520 GST_WARNING ("streamheaders array does not contain GstBuffers");
1524 buffer = g_value_peek_pointer (bufval);
1526 context->codec_priv =
1527 g_realloc (context->codec_priv,
1528 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1529 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1530 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1531 context->codec_priv_size =
1532 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1537 static const gchar *
1538 aac_codec_data_to_codec_id (const GstBuffer * buf)
1540 const gchar *result;
1543 /* default to MAIN */
1546 if (GST_BUFFER_SIZE (buf) >= 2) {
1547 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1565 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1574 * gst_matroska_mux_audio_pad_setcaps:
1575 * @pad: Pad which got the caps.
1578 * Setcaps function for audio sink pad.
1580 * Returns: #TRUE on success.
1583 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1585 GstMatroskaTrackContext *context = NULL;
1586 GstMatroskaTrackAudioContext *audiocontext;
1587 GstMatroskaMux *mux;
1588 GstMatroskaPad *collect_pad;
1589 const gchar *mimetype;
1590 gint samplerate = 0, channels = 0;
1591 GstStructure *structure;
1592 const GValue *codec_data = NULL;
1593 const GstBuffer *buf = NULL;
1594 const gchar *stream_format = NULL;
1596 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1599 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1600 g_assert (collect_pad);
1601 context = collect_pad->track;
1603 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1604 audiocontext = (GstMatroskaTrackAudioContext *) context;
1606 structure = gst_caps_get_structure (caps, 0);
1607 mimetype = gst_structure_get_name (structure);
1610 gst_structure_get_int (structure, "rate", &samplerate);
1611 gst_structure_get_int (structure, "channels", &channels);
1613 audiocontext->samplerate = samplerate;
1614 audiocontext->channels = channels;
1615 audiocontext->bitdepth = 0;
1616 context->default_duration = 0;
1618 codec_data = gst_structure_get_value (structure, "codec_data");
1620 buf = gst_value_get_buffer (codec_data);
1622 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1623 * data and other settings
1627 if (!strcmp (mimetype, "audio/mpeg")) {
1628 gint mpegversion = 0;
1630 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1631 switch (mpegversion) {
1637 gst_structure_get_int (structure, "layer", &layer);
1639 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1640 GST_WARNING_OBJECT (mux,
1641 "Unable to determine MPEG audio version, assuming 1");
1647 else if (layer == 2)
1649 else if (version == 2)
1654 context->default_duration =
1655 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1659 gst_matroska_mux_set_codec_id (context,
1660 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1663 gst_matroska_mux_set_codec_id (context,
1664 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1667 gst_matroska_mux_set_codec_id (context,
1668 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1677 stream_format = gst_structure_get_string (structure, "stream-format");
1678 /* check this is raw aac */
1679 if (stream_format) {
1680 if (strcmp (stream_format, "raw") != 0) {
1681 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1685 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1690 if (mpegversion == 2)
1692 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1693 aac_codec_data_to_codec_id (buf));
1694 else if (mpegversion == 4)
1696 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1697 aac_codec_data_to_codec_id (buf));
1699 g_assert_not_reached ();
1701 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1708 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1710 gint endianness = G_LITTLE_ENDIAN;
1711 gboolean signedness = TRUE;
1713 if (!gst_structure_get_int (structure, "width", &width) ||
1714 !gst_structure_get_int (structure, "depth", &depth) ||
1715 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1716 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1721 !gst_structure_get_int (structure, "endianness", &endianness)) {
1722 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1726 if (width != depth) {
1727 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1731 /* FIXME: where is this spec'ed out? (tpm) */
1732 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1733 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1737 audiocontext->bitdepth = depth;
1738 if (endianness == G_BIG_ENDIAN)
1739 gst_matroska_mux_set_codec_id (context,
1740 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1742 gst_matroska_mux_set_codec_id (context,
1743 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1745 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1748 if (!gst_structure_get_int (structure, "width", &width)) {
1749 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1753 audiocontext->bitdepth = width;
1754 gst_matroska_mux_set_codec_id (context,
1755 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1757 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1758 const GValue *streamheader;
1760 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1762 gst_matroska_mux_free_codec_priv (context);
1764 streamheader = gst_structure_get_value (structure, "streamheader");
1765 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1766 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1767 ("vorbis stream headers missing or malformed"));
1770 } else if (!strcmp (mimetype, "audio/x-flac")) {
1771 const GValue *streamheader;
1773 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1775 gst_matroska_mux_free_codec_priv (context);
1777 streamheader = gst_structure_get_value (structure, "streamheader");
1778 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1779 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1780 ("flac stream headers missing or malformed"));
1783 } else if (!strcmp (mimetype, "audio/x-speex")) {
1784 const GValue *streamheader;
1786 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1787 gst_matroska_mux_free_codec_priv (context);
1789 streamheader = gst_structure_get_value (structure, "streamheader");
1790 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1791 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1792 ("speex stream headers missing or malformed"));
1795 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1796 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1797 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1798 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1799 } else if (!strcmp (mimetype, "audio/x-dts")) {
1800 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1801 } else if (!strcmp (mimetype, "audio/x-tta")) {
1804 /* TTA frame duration */
1805 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1807 gst_structure_get_int (structure, "width", &width);
1808 audiocontext->bitdepth = width;
1809 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1811 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1813 const GValue *mdpr_data;
1815 gst_structure_get_int (structure, "raversion", &raversion);
1816 switch (raversion) {
1818 gst_matroska_mux_set_codec_id (context,
1819 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1822 gst_matroska_mux_set_codec_id (context,
1823 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1826 gst_matroska_mux_set_codec_id (context,
1827 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1833 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1834 if (mdpr_data != NULL) {
1835 guint8 *priv_data = NULL;
1836 guint priv_data_size = 0;
1838 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1840 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1841 priv_data = g_malloc0 (priv_data_size);
1843 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1845 gst_matroska_mux_free_codec_priv (context);
1847 context->codec_priv = priv_data;
1848 context->codec_priv_size = priv_data_size;
1851 } else if (!strcmp (mimetype, "audio/x-wma")
1852 || !strcmp (mimetype, "audio/x-alaw")
1853 || !strcmp (mimetype, "audio/x-mulaw")) {
1855 guint codec_priv_size;
1860 if (samplerate == 0 || channels == 0) {
1861 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
1865 if (!strcmp (mimetype, "audio/x-wma")) {
1869 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1870 || !gst_structure_get_int (structure, "block_align", &block_align)
1871 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
1872 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
1877 switch (wmaversion) {
1879 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1882 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1885 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1888 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1892 if (gst_structure_get_int (structure, "depth", &depth))
1893 audiocontext->bitdepth = depth;
1894 } else if (!strcmp (mimetype, "audio/x-alaw")
1895 || !strcmp (mimetype, "audio/x-mulaw")) {
1896 audiocontext->bitdepth = 8;
1897 if (!strcmp (mimetype, "audio/x-alaw"))
1898 format = GST_RIFF_WAVE_FORMAT_ALAW;
1900 format = GST_RIFF_WAVE_FORMAT_MULAW;
1902 block_align = channels;
1903 bitrate = block_align * samplerate;
1905 g_assert (format != 0);
1907 codec_priv_size = WAVEFORMATEX_SIZE;
1909 codec_priv_size += GST_BUFFER_SIZE (buf);
1911 /* serialize waveformatex structure */
1912 codec_priv = g_malloc0 (codec_priv_size);
1913 GST_WRITE_UINT16_LE (codec_priv, format);
1914 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1915 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1916 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1917 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1918 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1920 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1922 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1924 /* process codec private/initialization data, if any */
1926 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1927 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1930 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1931 gst_matroska_mux_free_codec_priv (context);
1932 context->codec_priv = (gpointer) codec_priv;
1933 context->codec_priv_size = codec_priv_size;
1941 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1942 GST_PAD_NAME (pad), caps);
1947 /* we probably don't have the data at start,
1948 * so have to reserve (a maximum) space to write this at the end.
1949 * bit spacy, but some formats can hold quite some */
1950 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
1953 * gst_matroska_mux_subtitle_pad_setcaps:
1954 * @pad: Pad which got the caps.
1957 * Setcaps function for subtitle sink pad.
1959 * Returns: #TRUE on success.
1962 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1964 /* There is now (at least) one such alement (kateenc), and I'm going
1965 to handle it here and claim it works when it can be piped back
1966 through GStreamer and VLC */
1968 GstMatroskaTrackContext *context = NULL;
1969 GstMatroskaTrackSubtitleContext *scontext;
1970 GstMatroskaMux *mux;
1971 GstMatroskaPad *collect_pad;
1972 const gchar *mimetype;
1973 GstStructure *structure;
1974 const GValue *value = NULL;
1975 const GstBuffer *buf = NULL;
1976 gboolean ret = TRUE;
1978 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1981 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1982 g_assert (collect_pad);
1983 context = collect_pad->track;
1985 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1986 scontext = (GstMatroskaTrackSubtitleContext *) context;
1988 structure = gst_caps_get_structure (caps, 0);
1989 mimetype = gst_structure_get_name (structure);
1992 scontext->check_utf8 = 1;
1993 scontext->invalid_utf8 = 0;
1994 context->default_duration = 0;
1996 if (!strcmp (mimetype, "subtitle/x-kate")) {
1997 const GValue *streamheader;
1999 gst_matroska_mux_set_codec_id (context,
2000 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2002 gst_matroska_mux_free_codec_priv (context);
2004 streamheader = gst_structure_get_value (structure, "streamheader");
2005 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2006 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2007 ("kate stream headers missing or malformed"));
2011 } else if (!strcmp (mimetype, "text/plain")) {
2012 gst_matroska_mux_set_codec_id (context,
2013 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2014 } else if (!strcmp (mimetype, "application/x-ssa")) {
2015 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2016 } else if (!strcmp (mimetype, "application/x-ass")) {
2017 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2018 } else if (!strcmp (mimetype, "application/x-usf")) {
2019 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2020 } else if (!strcmp (mimetype, "video/x-dvd-subpicture")) {
2021 gst_matroska_mux_set_codec_id (context,
2022 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2028 /* maybe some private data, e.g. vobsub */
2029 value = gst_structure_get_value (structure, "codec_data");
2031 buf = gst_value_get_buffer (value);
2033 guint8 *priv_data = NULL;
2034 guint priv_data_size = 0;
2036 priv_data_size = GST_BUFFER_SIZE (buf);
2037 if (priv_data_size > SUBTITLE_MAX_CODEC_PRIVATE) {
2038 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2039 " exceeded maximum (%d); discarding", pad,
2040 SUBTITLE_MAX_CODEC_PRIVATE);
2044 gst_matroska_mux_free_codec_priv (context);
2046 priv_data = g_malloc0 (priv_data_size);
2047 memcpy (priv_data, GST_BUFFER_DATA (buf), priv_data_size);
2048 context->codec_priv = priv_data;
2049 context->codec_priv_size = priv_data_size;
2052 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %u",
2053 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2062 * gst_matroska_mux_request_new_pad:
2063 * @element: #GstMatroskaMux.
2064 * @templ: #GstPadTemplate.
2065 * @pad_name: New pad name.
2067 * Request pad function for sink templates.
2069 * Returns: New #GstPad.
2072 gst_matroska_mux_request_new_pad (GstElement * element,
2073 GstPadTemplate * templ, const gchar * req_name)
2075 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2076 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2077 GstMatroskaPad *collect_pad;
2078 GstMatroskamuxPad *newpad;
2080 const gchar *pad_name = NULL;
2081 GstPadSetCapsFunction setcapsfunc = NULL;
2082 GstMatroskaTrackContext *context = NULL;
2084 gboolean locked = TRUE;
2087 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
2088 /* don't mix named and unnamed pads, if the pad already exists we fail when
2089 * trying to add it */
2090 if (req_name != NULL && sscanf (req_name, "audio_%d", &pad_id) == 1) {
2091 pad_name = req_name;
2093 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
2096 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2097 context = (GstMatroskaTrackContext *)
2098 g_new0 (GstMatroskaTrackAudioContext, 1);
2099 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2100 context->name = g_strdup ("Audio");
2101 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
2102 /* don't mix named and unnamed pads, if the pad already exists we fail when
2103 * trying to add it */
2104 if (req_name != NULL && sscanf (req_name, "video_%d", &pad_id) == 1) {
2105 pad_name = req_name;
2107 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
2110 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2111 context = (GstMatroskaTrackContext *)
2112 g_new0 (GstMatroskaTrackVideoContext, 1);
2113 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2114 context->name = g_strdup ("Video");
2115 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
2116 /* don't mix named and unnamed pads, if the pad already exists we fail when
2117 * trying to add it */
2118 if (req_name != NULL && sscanf (req_name, "subtitle_%d", &pad_id) == 1) {
2119 pad_name = req_name;
2121 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
2124 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2125 context = (GstMatroskaTrackContext *)
2126 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2127 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2128 context->name = g_strdup ("Subtitle");
2129 /* setcaps may only provide proper one a lot later */
2130 id = g_strdup ("S_SUB_UNKNOWN");
2133 GST_WARNING_OBJECT (mux, "This is not our template!");
2137 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2138 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2141 gst_matroskamux_pad_init (newpad);
2142 collect_pad = (GstMatroskaPad *)
2143 gst_collect_pads2_add_pad_full (mux->collect, GST_PAD (newpad),
2144 sizeof (GstMatroskamuxPad),
2145 (GstCollectData2DestroyNotify) gst_matroska_pad_free, locked);
2147 collect_pad->track = context;
2148 gst_matroska_pad_reset (collect_pad, FALSE);
2149 collect_pad->track->codec_id = id;
2151 gst_pad_set_setcaps_function (GST_PAD (newpad), setcapsfunc);
2152 gst_pad_set_active (GST_PAD (newpad), TRUE);
2153 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2154 goto pad_add_failed;
2158 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2160 return GST_PAD (newpad);
2165 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2166 gst_object_unref (newpad);
2172 * gst_matroska_mux_release_pad:
2173 * @element: #GstMatroskaMux.
2174 * @pad: Pad to release.
2176 * Release a previously requested pad.
2179 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2181 GstMatroskaMux *mux;
2184 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2186 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2187 GstCollectData2 *cdata = (GstCollectData2 *) walk->data;
2188 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2190 if (cdata->pad == pad) {
2191 GstClockTime min_dur; /* observed minimum duration */
2193 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2194 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2195 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2196 if (collect_pad->duration < min_dur)
2197 collect_pad->duration = min_dur;
2200 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2201 mux->duration < collect_pad->duration)
2202 mux->duration = collect_pad->duration;
2208 gst_collect_pads2_remove_pad (mux->collect, pad);
2209 if (gst_element_remove_pad (element, pad))
2215 * gst_matroska_mux_track_header:
2216 * @mux: #GstMatroskaMux
2217 * @context: Tack context.
2219 * Write a track header.
2222 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2223 GstMatroskaTrackContext * context)
2225 GstEbmlWrite *ebml = mux->ebml_write;
2228 /* TODO: check if everything necessary is written and check default values */
2230 /* track type goes before the type-specific stuff */
2231 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2232 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2234 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
2235 gst_matroska_mux_create_uid ());
2236 if (context->default_duration) {
2237 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2238 context->default_duration);
2240 if (context->language) {
2241 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2245 /* FIXME: until we have a nice way of getting the codecname
2246 * out of the caps, I'm not going to enable this. Too much
2247 * (useless, double, boring) work... */
2248 /* TODO: Use value from tags if any */
2249 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2250 context->codec_name); */
2251 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2253 /* type-specific stuff */
2254 switch (context->type) {
2255 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2256 GstMatroskaTrackVideoContext *videocontext =
2257 (GstMatroskaTrackVideoContext *) context;
2259 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2260 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2261 videocontext->pixel_width);
2262 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2263 videocontext->pixel_height);
2264 if (videocontext->display_width && videocontext->display_height) {
2265 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2266 videocontext->display_width);
2267 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2268 videocontext->display_height);
2270 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2271 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2272 if (videocontext->fourcc) {
2273 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2275 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2276 (gpointer) & fcc_le, 4);
2278 gst_ebml_write_master_finish (ebml, master);
2283 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2284 GstMatroskaTrackAudioContext *audiocontext =
2285 (GstMatroskaTrackAudioContext *) context;
2287 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2288 if (audiocontext->samplerate != 8000)
2289 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2290 audiocontext->samplerate);
2291 if (audiocontext->channels != 1)
2292 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2293 audiocontext->channels);
2294 if (audiocontext->bitdepth) {
2295 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2296 audiocontext->bitdepth);
2298 gst_ebml_write_master_finish (ebml, master);
2303 /* this is what we write for now and must be filled
2304 * and remainder void'ed later on */
2305 #define SUBTITLE_DUMMY_SIZE (1 + 1 + 14 + 1 + 2 + SUBTITLE_MAX_CODEC_PRIVATE)
2307 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2310 context->pos = ebml->pos;
2311 /* CodecID is mandatory ... */
2312 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, "S_SUB_UNKNOWN");
2314 buf = g_malloc0 (SUBTITLE_MAX_CODEC_PRIVATE);
2315 gst_ebml_write_binary (ebml, GST_EBML_ID_VOID, buf,
2316 SUBTITLE_MAX_CODEC_PRIVATE);
2318 /* real data has to be written at finish */
2322 /* doesn't need type-specific data */
2326 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2327 if (context->codec_priv)
2328 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2329 context->codec_priv, context->codec_priv_size);
2334 * gst_matroska_mux_start:
2335 * @mux: #GstMatroskaMux
2337 * Start a new matroska file (write headers etc...)
2340 gst_matroska_mux_start (GstMatroskaMux * mux)
2342 GstEbmlWrite *ebml = mux->ebml_write;
2343 const gchar *doctype;
2344 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2345 GST_MATROSKA_ID_TRACKS,
2346 GST_MATROSKA_ID_CUES,
2347 GST_MATROSKA_ID_TAGS,
2350 guint64 master, child;
2354 GstClockTime duration = 0;
2355 guint32 segment_uid[4];
2356 GTimeVal time = { 0, 0 };
2358 /* if not streaming, check if downstream is seekable */
2359 if (!mux->streamable) {
2363 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2364 if (gst_pad_peer_query (mux->srcpad, query)) {
2365 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2366 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2368 mux->streamable = TRUE;
2369 g_object_notify (G_OBJECT (mux), "streamable");
2370 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2371 "streamable=false. Will ignore that and create streamable output "
2375 /* have to assume seeking is supported if query not handled downstream */
2376 /* FIXME 0.11: change to query not handled => seeking not supported */
2377 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2381 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2382 ebml->caps = gst_caps_new_simple ("video/webm", NULL);
2384 ebml->caps = gst_caps_new_simple ("video/x-matroska", NULL);
2386 /* we start with a EBML header */
2387 doctype = mux->doctype;
2388 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2389 doctype, mux->doctype_version);
2390 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2392 /* the rest of the header is cached */
2393 gst_ebml_write_set_cache (ebml, 0x1000);
2395 /* start a segment */
2397 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2398 mux->segment_master = ebml->pos;
2400 if (!mux->streamable) {
2401 /* seekhead (table of contents) - we set the positions later */
2402 mux->seekhead_pos = ebml->pos;
2403 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2404 for (i = 0; seekhead_id[i] != 0; i++) {
2405 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2406 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2407 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2408 gst_ebml_write_master_finish (ebml, child);
2410 gst_ebml_write_master_finish (ebml, master);
2413 if (mux->streamable) {
2414 const GstTagList *tags;
2417 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2419 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2420 guint64 master_tags, master_tag;
2422 GST_DEBUG_OBJECT (mux, "Writing tags");
2424 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2425 mux->tags_pos = ebml->pos;
2426 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2427 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2428 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2429 gst_ebml_write_master_finish (ebml, master_tag);
2430 gst_ebml_write_master_finish (ebml, master_tags);
2435 mux->info_pos = ebml->pos;
2436 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2437 for (i = 0; i < 4; i++) {
2438 segment_uid[i] = g_random_int ();
2440 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2441 (guint8 *) segment_uid, 16);
2442 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2443 mux->duration_pos = ebml->pos;
2445 if (!mux->streamable) {
2446 for (collected = mux->collect->data; collected;
2447 collected = g_slist_next (collected)) {
2448 GstMatroskaPad *collect_pad;
2449 GstFormat format = GST_FORMAT_TIME;
2451 gint64 trackduration;
2453 collect_pad = (GstMatroskaPad *) collected->data;
2454 thepad = collect_pad->collect.pad;
2456 /* Query the total length of the track. */
2457 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2458 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2459 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2460 GST_TIME_ARGS (trackduration));
2461 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2462 duration = (GstClockTime) trackduration;
2466 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2467 gst_guint64_to_gdouble (duration) /
2468 gst_guint64_to_gdouble (mux->time_scale));
2470 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2471 "GStreamer plugin version " PACKAGE_VERSION);
2472 if (mux->writing_app && mux->writing_app[0]) {
2473 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2475 g_get_current_time (&time);
2476 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2477 gst_ebml_write_master_finish (ebml, master);
2480 mux->tracks_pos = ebml->pos;
2481 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2483 for (collected = mux->collect->data; collected;
2484 collected = g_slist_next (collected)) {
2485 GstMatroskaPad *collect_pad;
2488 collect_pad = (GstMatroskaPad *) collected->data;
2489 thepad = collect_pad->collect.pad;
2491 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2492 collect_pad->track->codec_id != 0) {
2493 collect_pad->track->num = tracknum++;
2494 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2495 gst_matroska_mux_track_header (mux, collect_pad->track);
2496 gst_ebml_write_master_finish (ebml, child);
2497 /* some remaining pad/track setup */
2498 collect_pad->default_duration_scaled =
2499 gst_util_uint64_scale (collect_pad->track->default_duration,
2500 1, mux->time_scale);
2503 gst_ebml_write_master_finish (ebml, master);
2505 /* lastly, flush the cache */
2506 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2510 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2513 /* TODO: more sensible tag mappings */
2516 const gchar *matroska_tagname;
2517 const gchar *gstreamer_tagname;
2521 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2522 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
2523 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2524 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2525 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2526 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2527 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2528 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2529 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2530 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2531 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2532 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2533 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2534 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2535 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2537 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2539 guint64 simpletag_master;
2541 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2542 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2543 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2545 if (strcmp (tagname_gst, tag) == 0) {
2546 GValue src = { 0, };
2549 if (!gst_tag_list_copy_value (&src, list, tag))
2551 if ((dest = gst_value_serialize (&src))) {
2553 simpletag_master = gst_ebml_write_master_start (ebml,
2554 GST_MATROSKA_ID_SIMPLETAG);
2555 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2556 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2557 gst_ebml_write_master_finish (ebml, simpletag_master);
2560 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2562 g_value_unset (&src);
2570 * gst_matroska_mux_finish:
2571 * @mux: #GstMatroskaMux
2573 * Finish a new matroska file (write index etc...)
2576 gst_matroska_mux_finish (GstMatroskaMux * mux)
2578 GstEbmlWrite *ebml = mux->ebml_write;
2580 guint64 duration = 0;
2582 const GstTagList *tags;
2584 /* finish last cluster */
2586 gst_ebml_write_master_finish (ebml, mux->cluster);
2590 if (mux->index != NULL) {
2592 guint64 master, pointentry_master, trackpos_master;
2594 mux->cues_pos = ebml->pos;
2595 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2596 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2598 for (n = 0; n < mux->num_indexes; n++) {
2599 GstMatroskaIndex *idx = &mux->index[n];
2601 pointentry_master = gst_ebml_write_master_start (ebml,
2602 GST_MATROSKA_ID_POINTENTRY);
2603 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2604 idx->time / mux->time_scale);
2605 trackpos_master = gst_ebml_write_master_start (ebml,
2606 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2607 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2608 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2609 idx->pos - mux->segment_master);
2610 gst_ebml_write_master_finish (ebml, trackpos_master);
2611 gst_ebml_write_master_finish (ebml, pointentry_master);
2614 gst_ebml_write_master_finish (ebml, master);
2615 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2619 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2621 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2622 guint64 master_tags, master_tag;
2624 GST_DEBUG_OBJECT (mux, "Writing tags");
2626 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2627 mux->tags_pos = ebml->pos;
2628 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2629 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2630 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2631 gst_ebml_write_master_finish (ebml, master_tag);
2632 gst_ebml_write_master_finish (ebml, master_tags);
2635 /* update seekhead. We know that:
2636 * - a seekhead contains 4 entries.
2637 * - order of entries is as above.
2638 * - a seekhead has a 4-byte header + 8-byte length
2639 * - each entry is 2-byte master, 2-byte ID pointer,
2640 * 2-byte length pointer, all 8/1-byte length, 4-
2641 * byte ID and 8-byte length pointer, where the
2642 * length pointer starts at 20.
2643 * - all entries are local to the segment (so pos - segment_master).
2644 * - so each entry is at 12 + 20 + num * 28. */
2645 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2646 mux->info_pos - mux->segment_master);
2647 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2648 mux->tracks_pos - mux->segment_master);
2649 if (mux->index != NULL) {
2650 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2651 mux->cues_pos - mux->segment_master);
2654 guint64 my_pos = ebml->pos;
2656 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2657 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2658 gst_ebml_write_seek (ebml, my_pos);
2661 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2662 mux->tags_pos - mux->segment_master);
2665 guint64 my_pos = ebml->pos;
2667 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2668 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2669 gst_ebml_write_seek (ebml, my_pos);
2673 * - first get the overall duration
2674 * (a released track may have left a duration in here)
2675 * - write some track header data for subtitles
2677 duration = mux->duration;
2679 for (collected = mux->collect->data; collected;
2680 collected = g_slist_next (collected)) {
2681 GstMatroskaPad *collect_pad;
2682 GstClockTime min_duration; /* observed minimum duration */
2683 GstMatroskaTrackContext *context;
2684 gint voidleft = 0, fill = 0;
2687 collect_pad = (GstMatroskaPad *) collected->data;
2688 context = collect_pad->track;
2690 GST_DEBUG_OBJECT (mux,
2691 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2692 " end ts %" GST_TIME_FORMAT, collect_pad,
2693 GST_TIME_ARGS (collect_pad->start_ts),
2694 GST_TIME_ARGS (collect_pad->end_ts));
2696 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2697 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2699 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2700 if (collect_pad->duration < min_duration)
2701 collect_pad->duration = min_duration;
2702 GST_DEBUG_OBJECT (collect_pad,
2703 "final track duration: %" GST_TIME_FORMAT,
2704 GST_TIME_ARGS (collect_pad->duration));
2707 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2708 duration < collect_pad->duration)
2709 duration = collect_pad->duration;
2711 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE || !context->pos)
2715 /* write subtitle type and possible private data */
2716 gst_ebml_write_seek (ebml, context->pos);
2717 /* complex way to write ascii to account for extra filling */
2718 codec_id = g_malloc0 (strlen (context->codec_id) + 1 + fill);
2719 strcpy (codec_id, context->codec_id);
2720 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECID,
2721 codec_id, strlen (context->codec_id) + 1 + fill);
2723 if (context->codec_priv)
2724 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2725 context->codec_priv, context->codec_priv_size);
2726 voidleft = SUBTITLE_DUMMY_SIZE - (ebml->pos - context->pos);
2727 /* void'ify; sigh, variable sized length field */
2728 if (voidleft == 1) {
2731 } else if (voidleft && voidleft <= 128)
2732 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 2);
2733 else if (voidleft >= 130)
2734 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, voidleft - 3);
2735 else if (voidleft == 129) {
2736 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 64);
2737 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 63);
2741 /* seek back (optional, but do anyway) */
2742 gst_ebml_write_seek (ebml, pos);
2744 /* update duration */
2745 if (duration != 0) {
2746 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2747 GST_TIME_ARGS (duration));
2748 pos = mux->ebml_write->pos;
2749 gst_ebml_write_seek (ebml, mux->duration_pos);
2750 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2751 gst_guint64_to_gdouble (duration) /
2752 gst_guint64_to_gdouble (mux->time_scale));
2753 gst_ebml_write_seek (ebml, pos);
2756 guint64 my_pos = ebml->pos;
2758 gst_ebml_write_seek (ebml, mux->duration_pos);
2759 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2760 gst_ebml_write_seek (ebml, my_pos);
2762 GST_DEBUG_OBJECT (mux, "finishing segment");
2763 /* finish segment - this also writes element length */
2764 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2768 * gst_matroska_mux_buffer_header:
2769 * @track: Track context.
2770 * @relative_timestamp: relative timestamp of the buffer
2771 * @flags: Buffer flags.
2773 * Create a buffer containing buffer header.
2775 * Returns: New buffer.
2778 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2779 gint16 relative_timestamp, int flags)
2783 hdr = gst_buffer_new_and_alloc (4);
2784 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2785 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2786 /* time relative to clustertime */
2787 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2790 GST_BUFFER_DATA (hdr)[3] = flags;
2795 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2796 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2797 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2800 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2801 GstMatroskaPad * collect_pad, GstBuffer * buf)
2803 GstMatroskaTrackVideoContext *ctx =
2804 (GstMatroskaTrackVideoContext *) collect_pad->track;
2805 const guint8 *data = GST_BUFFER_DATA (buf);
2806 guint size = GST_BUFFER_SIZE (buf);
2808 guint32 next_parse_offset;
2809 GstBuffer *ret = NULL;
2810 gboolean is_muxing_unit = FALSE;
2812 if (GST_BUFFER_SIZE (buf) < 13) {
2813 gst_buffer_unref (buf);
2817 /* Check if this buffer contains a picture or end-of-sequence packet */
2818 while (size >= 13) {
2819 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2820 gst_buffer_unref (buf);
2824 parse_code = GST_READ_UINT8 (data + 4);
2825 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2826 if (ctx->dirac_unit) {
2827 gst_buffer_unref (ctx->dirac_unit);
2828 ctx->dirac_unit = NULL;
2830 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2831 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2832 is_muxing_unit = TRUE;
2836 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2838 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
2841 data += next_parse_offset;
2842 size -= next_parse_offset;
2845 if (ctx->dirac_unit)
2846 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2848 ctx->dirac_unit = gst_buffer_ref (buf);
2850 if (is_muxing_unit) {
2851 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2852 ctx->dirac_unit = NULL;
2853 gst_buffer_copy_metadata (ret, buf,
2854 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2855 GST_BUFFER_COPY_CAPS);
2856 gst_buffer_unref (buf);
2858 gst_buffer_unref (buf);
2866 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2870 GValue streamheader = { 0 };
2871 GValue bufval = { 0 };
2872 GstBuffer *streamheader_buffer;
2873 GstEbmlWrite *ebml = mux->ebml_write;
2875 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2876 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2877 caps = gst_caps_new_simple ("video/webm", NULL);
2879 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2881 s = gst_caps_get_structure (caps, 0);
2882 g_value_init (&streamheader, GST_TYPE_ARRAY);
2883 g_value_init (&bufval, GST_TYPE_BUFFER);
2884 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2885 gst_value_set_buffer (&bufval, streamheader_buffer);
2886 gst_value_array_append_value (&streamheader, &bufval);
2887 g_value_unset (&bufval);
2888 gst_structure_set_value (s, "streamheader", &streamheader);
2889 g_value_unset (&streamheader);
2890 gst_caps_replace (&ebml->caps, caps);
2891 gst_buffer_unref (streamheader_buffer);
2892 gst_caps_unref (caps);
2896 * gst_matroska_mux_write_data:
2897 * @mux: #GstMatroskaMux
2898 * @collect_pad: #GstMatroskaPad with the data
2900 * Write collected data (called from gst_matroska_mux_collected).
2902 * Returns: Result of the gst_pad_push issued to write the data.
2904 static GstFlowReturn
2905 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
2908 GstEbmlWrite *ebml = mux->ebml_write;
2911 gboolean write_duration;
2912 gint16 relative_timestamp;
2913 gint64 relative_timestamp64;
2914 guint64 block_duration;
2915 gboolean is_video_keyframe = FALSE;
2916 GstMatroskamuxPad *pad;
2919 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
2921 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2922 if (collect_pad->track->xiph_headers_to_skip > 0) {
2923 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2924 gst_buffer_unref (buf);
2925 --collect_pad->track->xiph_headers_to_skip;
2929 /* for dirac we have to queue up everything up to a picture unit */
2930 if (collect_pad->track->codec_id != NULL &&
2931 strcmp (collect_pad->track->codec_id,
2932 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2933 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2938 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2939 * this would wreak havoc with time stored in matroska file */
2940 /* TODO: maybe calculate a timestamp by using the previous timestamp
2941 * and default duration */
2942 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2943 GST_WARNING_OBJECT (collect_pad->collect.pad,
2944 "Invalid buffer timestamp; dropping buffer");
2945 gst_buffer_unref (buf);
2949 /* set the timestamp for outgoing buffers */
2950 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2952 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2953 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2954 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2955 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2956 is_video_keyframe = TRUE;
2960 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
2961 * or when we may be reaching the limit of the relative timestamp */
2962 if (mux->cluster_time +
2963 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2964 || is_video_keyframe || mux->force_key_unit_event) {
2965 if (!mux->streamable)
2966 gst_ebml_write_master_finish (ebml, mux->cluster);
2968 /* Forward the GstForceKeyUnit event after finishing the cluster */
2969 if (mux->force_key_unit_event) {
2970 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
2971 mux->force_key_unit_event = NULL;
2974 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2975 mux->cluster_pos = ebml->pos;
2976 gst_ebml_write_set_cache (ebml, 0x20);
2978 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2979 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2980 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2982 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2983 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2985 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2986 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2987 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2988 mux->prev_cluster_size);
2993 mux->cluster_pos = ebml->pos;
2994 gst_ebml_write_set_cache (ebml, 0x20);
2995 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2996 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2997 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2998 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2999 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
3002 /* update duration of this track */
3003 if (GST_BUFFER_DURATION_IS_VALID (buf))
3004 collect_pad->duration += GST_BUFFER_DURATION (buf);
3006 /* We currently write index entries for all video tracks or for the audio
3007 * track in a single-track audio file. This could be improved by keeping the
3008 * index only for the *first* video track. */
3010 /* TODO: index is useful for every track, should contain the number of
3011 * the block in the cluster which contains the timestamp, should also work
3012 * for files with multiple audio tracks.
3014 if (!mux->streamable &&
3015 (is_video_keyframe ||
3016 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3017 (mux->num_streams == 1)))) {
3020 if (mux->min_index_interval != 0) {
3021 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3022 if (mux->index[last_idx].track == collect_pad->track->num)
3027 if (last_idx < 0 || mux->min_index_interval == 0 ||
3028 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
3029 >= mux->min_index_interval)) {
3030 GstMatroskaIndex *idx;
3032 if (mux->num_indexes % 32 == 0) {
3033 mux->index = g_renew (GstMatroskaIndex, mux->index,
3034 mux->num_indexes + 32);
3036 idx = &mux->index[mux->num_indexes++];
3038 idx->pos = mux->cluster_pos;
3039 idx->time = GST_BUFFER_TIMESTAMP (buf);
3040 idx->track = collect_pad->track->num;
3044 /* Check if the duration differs from the default duration. */
3045 write_duration = FALSE;
3047 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3048 block_duration = gst_util_uint64_scale (GST_BUFFER_DURATION (buf),
3049 1, mux->time_scale);
3051 /* small difference should be ok. */
3052 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3053 block_duration < collect_pad->default_duration_scaled - 1) {
3054 write_duration = TRUE;
3058 /* write the block, for doctype v2 use SimpleBlock if possible
3059 * one slice (*breath*).
3060 * FIXME: Need to do correct lacing! */
3061 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
3062 if (relative_timestamp64 >= 0) {
3063 /* round the timestamp */
3064 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3066 /* round the timestamp */
3067 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3069 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3071 if (mux->doctype_version > 1 && !write_duration) {
3073 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
3076 gst_matroska_mux_create_buffer_header (collect_pad->track,
3077 relative_timestamp, flags);
3078 gst_ebml_write_set_cache (ebml, 0x40);
3079 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3080 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3081 gst_ebml_write_buffer (ebml, hdr);
3082 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3083 gst_ebml_write_buffer (ebml, buf);
3085 return gst_ebml_last_write_result (ebml);
3087 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
3088 /* write and call order slightly unnatural,
3089 * but avoids seek and minizes pushing */
3090 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
3092 gst_matroska_mux_create_buffer_header (collect_pad->track,
3093 relative_timestamp, 0);
3095 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
3096 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
3097 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
3098 gst_ebml_write_buffer (ebml, hdr);
3099 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
3100 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
3101 gst_ebml_write_buffer (ebml, buf);
3103 return gst_ebml_last_write_result (ebml);
3108 * gst_matroska_mux_handle_buffer:
3109 * @pads: #GstCollectPads2
3110 * @uuser_data: #GstMatroskaMux
3112 * Collectpads callback.
3114 * Returns: #GstFlowReturn
3116 static GstFlowReturn
3117 gst_matroska_mux_handle_buffer (GstCollectPads2 * pads, GstCollectData2 * data,
3118 GstBuffer * buf, gpointer user_data)
3120 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
3121 GstEbmlWrite *ebml = mux->ebml_write;
3122 GstMatroskaPad *best;
3123 GstFlowReturn ret = GST_FLOW_OK;
3125 GST_DEBUG_OBJECT (mux, "Collected pads");
3127 /* start with a header */
3128 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
3129 if (mux->collect->data == NULL) {
3130 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
3131 ("No input streams configured"));
3132 return GST_FLOW_ERROR;
3134 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
3135 gst_ebml_start_streamheader (ebml);
3136 gst_matroska_mux_start (mux);
3137 gst_matroska_mux_stop_streamheader (mux);
3138 mux->state = GST_MATROSKA_MUX_STATE_DATA;
3141 /* provided with stream to write from */
3142 best = (GstMatroskaPad *) data;
3144 /* if there is no best pad, we have reached EOS */
3146 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
3147 if (!mux->streamable) {
3148 gst_matroska_mux_finish (mux);
3150 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
3152 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
3153 ret = GST_FLOW_UNEXPECTED;
3157 /* if we have a best stream, should also have a buffer */
3160 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
3161 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
3162 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
3163 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
3165 /* make note of first and last encountered timestamps, so we can calculate
3166 * the actual duration later when we send an updated header on eos */
3167 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3168 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (buf);
3169 GstClockTime end_ts = start_ts;
3171 if (GST_BUFFER_DURATION_IS_VALID (buf))
3172 end_ts += GST_BUFFER_DURATION (buf);
3173 else if (best->track->default_duration)
3174 end_ts += best->track->default_duration;
3176 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
3177 best->end_ts = end_ts;
3179 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
3180 start_ts < best->start_ts))
3181 best->start_ts = start_ts;
3184 /* write one buffer */
3185 ret = gst_matroska_mux_write_data (mux, best, buf);
3193 * gst_matroska_mux_change_state:
3194 * @element: #GstMatroskaMux
3195 * @transition: State change transition.
3197 * Change the muxer state.
3199 * Returns: #GstStateChangeReturn
3201 static GstStateChangeReturn
3202 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
3204 GstStateChangeReturn ret;
3205 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
3207 switch (transition) {
3208 case GST_STATE_CHANGE_NULL_TO_READY:
3210 case GST_STATE_CHANGE_READY_TO_PAUSED:
3211 gst_collect_pads2_start (mux->collect);
3213 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3215 case GST_STATE_CHANGE_PAUSED_TO_READY:
3216 gst_collect_pads2_stop (mux->collect);
3222 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3224 switch (transition) {
3225 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3227 case GST_STATE_CHANGE_PAUSED_TO_READY:
3228 gst_matroska_mux_reset (GST_ELEMENT (mux));
3230 case GST_STATE_CHANGE_READY_TO_NULL:
3240 gst_matroska_mux_set_property (GObject * object,
3241 guint prop_id, const GValue * value, GParamSpec * pspec)
3243 GstMatroskaMux *mux;
3245 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3246 mux = GST_MATROSKA_MUX (object);
3249 case ARG_WRITING_APP:
3250 if (!g_value_get_string (value)) {
3251 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
3254 g_free (mux->writing_app);
3255 mux->writing_app = g_value_dup_string (value);
3257 case ARG_DOCTYPE_VERSION:
3258 mux->doctype_version = g_value_get_int (value);
3260 case ARG_MIN_INDEX_INTERVAL:
3261 mux->min_index_interval = g_value_get_int64 (value);
3263 case ARG_STREAMABLE:
3264 mux->streamable = g_value_get_boolean (value);
3267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3273 gst_matroska_mux_get_property (GObject * object,
3274 guint prop_id, GValue * value, GParamSpec * pspec)
3276 GstMatroskaMux *mux;
3278 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
3279 mux = GST_MATROSKA_MUX (object);
3282 case ARG_WRITING_APP:
3283 g_value_set_string (value, mux->writing_app);
3285 case ARG_DOCTYPE_VERSION:
3286 g_value_set_int (value, mux->doctype_version);
3288 case ARG_MIN_INDEX_INTERVAL:
3289 g_value_set_int64 (value, mux->min_index_interval);
3291 case ARG_STREAMABLE:
3292 g_value_set_boolean (value, mux->streamable);
3295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);