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>
6 * matroska-mux.c: matroska file/stream muxer
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 /* TODO: - check everywhere that we don't write invalid values
25 * - make sure timestamps are correctly scaled everywhere
29 * SECTION:element-matroskamux
31 * matroskamux muxes different input streams into a Matroska file.
34 * <title>Example launch line</title>
36 * 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.
37 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
39 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
40 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
51 #include <gst/riff/riff-media.h>
52 #include <gst/tag/tag.h>
54 #include "matroska-mux.h"
55 #include "matroska-ids.h"
57 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
58 #define GST_CAT_DEFAULT matroskamux_debug
65 ARG_MIN_INDEX_INTERVAL
68 #define DEFAULT_MATROSKA_VERSION 1
69 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
70 #define DEFAULT_MIN_INDEX_INTERVAL 0
72 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
73 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
75 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
78 GST_STATIC_CAPS ("video/x-matroska")
81 #define COMMON_VIDEO_CAPS \
82 "width = (int) [ 16, 4096 ], " \
83 "height = (int) [ 16, 4096 ], " \
84 "framerate = (fraction) [ 0, MAX ]"
86 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ] "
91 * * require codec data, etc as needed
94 static GstStaticPadTemplate videosink_templ =
95 GST_STATIC_PAD_TEMPLATE ("video_%d",
98 GST_STATIC_CAPS ("video/mpeg, "
99 "mpegversion = (int) { 1, 2, 4 }, "
100 "systemstream = (boolean) false, "
101 COMMON_VIDEO_CAPS "; "
103 COMMON_VIDEO_CAPS "; "
105 COMMON_VIDEO_CAPS "; "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
120 COMMON_VIDEO_CAPS "; "
121 "video/x-pn-realvideo, "
122 "rmversion = (int) [1, 4], "
123 COMMON_VIDEO_CAPS "; "
125 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
126 COMMON_VIDEO_CAPS "; "
127 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
130 #define COMMON_AUDIO_CAPS \
131 "channels = (int) [ 1, MAX ], " \
132 "rate = (int) [ 1, MAX ]"
135 * * require codec data, etc as needed
137 static GstStaticPadTemplate audiosink_templ =
138 GST_STATIC_PAD_TEMPLATE ("audio_%d",
141 GST_STATIC_CAPS ("audio/mpeg, "
142 "mpegversion = (int) 1, "
143 "layer = (int) [ 1, 3 ], "
144 "stream-format = (string) { raw }, "
145 COMMON_AUDIO_CAPS "; "
147 "mpegversion = (int) { 2, 4 }, "
148 COMMON_AUDIO_CAPS "; "
150 COMMON_AUDIO_CAPS "; "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
160 "signed = (boolean) false, "
161 COMMON_AUDIO_CAPS ";"
165 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
166 "signed = (boolean) true, "
167 COMMON_AUDIO_CAPS ";"
171 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
172 "signed = (boolean) true, "
173 COMMON_AUDIO_CAPS ";"
177 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
178 "signed = (boolean) true, "
179 COMMON_AUDIO_CAPS ";"
180 "audio/x-raw-float, "
181 "width = (int) [ 32, 64 ], "
182 "endianness = (int) LITTLE_ENDIAN, "
183 COMMON_AUDIO_CAPS ";"
185 "width = (int) { 8, 16, 24 }, "
186 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
187 "audio/x-pn-realaudio, "
188 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
189 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
190 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
194 static GstStaticPadTemplate subtitlesink_templ =
195 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
198 GST_STATIC_CAPS_ANY);
200 static GArray *used_uids;
201 G_LOCK_DEFINE_STATIC (used_uids);
203 static void gst_matroska_mux_add_interfaces (GType type);
205 GType gst_matroska_mux_get_type (void);
206 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
207 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
209 /* Matroska muxer destructor */
210 static void gst_matroska_mux_finalize (GObject * object);
212 /* Pads collected callback */
214 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
217 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
219 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
220 GstPadTemplate * templ, const gchar * name);
221 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
223 /* gst internal change state handler */
224 static GstStateChangeReturn
225 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
227 /* gobject bla bla */
228 static void gst_matroska_mux_set_property (GObject * object,
229 guint prop_id, const GValue * value, GParamSpec * pspec);
230 static void gst_matroska_mux_get_property (GObject * object,
231 guint prop_id, GValue * value, GParamSpec * pspec);
234 static void gst_matroska_mux_reset (GstElement * element);
237 static guint64 gst_matroska_mux_create_uid ();
239 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
251 gst_matroska_mux_add_interfaces (GType type)
253 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
255 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
259 gst_matroska_mux_base_init (gpointer g_class)
261 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
263 gst_element_class_add_pad_template (element_class,
264 gst_static_pad_template_get (&videosink_templ));
265 gst_element_class_add_pad_template (element_class,
266 gst_static_pad_template_get (&audiosink_templ));
267 gst_element_class_add_pad_template (element_class,
268 gst_static_pad_template_get (&subtitlesink_templ));
269 gst_element_class_add_pad_template (element_class,
270 gst_static_pad_template_get (&src_templ));
271 gst_element_class_set_details_simple (element_class, "Matroska muxer",
273 "Muxes video/audio/subtitle streams into a matroska stream",
274 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
276 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
281 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
283 GObjectClass *gobject_class;
284 GstElementClass *gstelement_class;
286 gobject_class = (GObjectClass *) klass;
287 gstelement_class = (GstElementClass *) klass;
289 gobject_class->finalize = gst_matroska_mux_finalize;
291 gobject_class->get_property = gst_matroska_mux_get_property;
292 gobject_class->set_property = gst_matroska_mux_set_property;
294 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
295 g_param_spec_string ("writing-app", "Writing application.",
296 "The name the application that creates the matroska file.",
297 NULL, G_PARAM_READWRITE));
298 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
299 g_param_spec_int ("version", "Matroska version",
300 "This parameter determines what matroska features can be used.",
301 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
302 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
303 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
304 "entries", "An index entry is created every so many nanoseconds.",
305 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
307 gstelement_class->change_state =
308 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
309 gstelement_class->request_new_pad =
310 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
311 gstelement_class->release_pad =
312 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
317 * gst_matroska_mux_init:
318 * @mux: #GstMatroskaMux that should be initialized.
319 * @g_class: Class of the muxer.
321 * Matroska muxer constructor.
324 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
326 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
327 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
328 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
330 mux->collect = gst_collect_pads_new ();
331 gst_collect_pads_set_function (mux->collect,
332 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
335 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
337 /* property defaults */
338 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
339 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
340 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
342 /* initialize internal variables */
344 mux->num_streams = 0;
345 mux->num_a_streams = 0;
346 mux->num_t_streams = 0;
347 mux->num_v_streams = 0;
349 /* initialize remaining variables */
350 gst_matroska_mux_reset (GST_ELEMENT (mux));
355 * gst_matroska_mux_finalize:
356 * @object: #GstMatroskaMux that should be finalized.
358 * Finalize matroska muxer.
361 gst_matroska_mux_finalize (GObject * object)
363 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
365 gst_object_unref (mux->collect);
366 gst_object_unref (mux->ebml_write);
367 if (mux->writing_app)
368 g_free (mux->writing_app);
370 G_OBJECT_CLASS (parent_class)->finalize (object);
375 * gst_matroska_mux_create_uid:
377 * Generate new unused track UID.
379 * Returns: New track UID.
382 gst_matroska_mux_create_uid (void)
389 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
394 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
395 for (i = 0; i < used_uids->len; i++) {
396 if (g_array_index (used_uids, guint64, i) == uid) {
401 g_array_append_val (used_uids, uid);
404 G_UNLOCK (used_uids);
410 * gst_matroska_pad_reset:
411 * @collect_pad: the #GstMatroskaPad
413 * Reset and/or release resources of a matroska collect pad.
416 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
419 GstMatroskaTrackType type = 0;
421 /* free track information */
422 if (collect_pad->track != NULL) {
423 /* retrieve for optional later use */
424 name = collect_pad->track->name;
425 type = collect_pad->track->type;
426 /* extra for video */
427 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
428 GstMatroskaTrackVideoContext *ctx =
429 (GstMatroskaTrackVideoContext *) collect_pad->track;
431 if (ctx->dirac_unit) {
432 gst_buffer_unref (ctx->dirac_unit);
433 ctx->dirac_unit = NULL;
436 g_free (collect_pad->track->codec_id);
437 g_free (collect_pad->track->codec_name);
439 g_free (collect_pad->track->name);
440 g_free (collect_pad->track->language);
441 g_free (collect_pad->track->codec_priv);
442 g_free (collect_pad->track);
443 collect_pad->track = NULL;
446 /* free cached buffer */
447 if (collect_pad->buffer != NULL) {
448 gst_buffer_unref (collect_pad->buffer);
449 collect_pad->buffer = NULL;
452 if (!full && type != 0) {
453 GstMatroskaTrackContext *context;
455 /* create a fresh context */
457 case GST_MATROSKA_TRACK_TYPE_VIDEO:
458 context = (GstMatroskaTrackContext *)
459 g_new0 (GstMatroskaTrackVideoContext, 1);
461 case GST_MATROSKA_TRACK_TYPE_AUDIO:
462 context = (GstMatroskaTrackContext *)
463 g_new0 (GstMatroskaTrackAudioContext, 1);
465 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
466 context = (GstMatroskaTrackContext *)
467 g_new0 (GstMatroskaTrackSubtitleContext, 1);
470 g_assert_not_reached ();
474 context->type = type;
475 context->name = name;
476 /* TODO: check default values for the context */
477 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
478 collect_pad->track = context;
479 collect_pad->buffer = NULL;
480 collect_pad->duration = 0;
481 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
482 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
487 * gst_matroska_pad_free:
488 * @collect_pad: the #GstMatroskaPad
490 * Release resources of a matroska collect pad.
493 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
495 gst_matroska_pad_reset (collect_pad, TRUE);
500 * gst_matroska_mux_reset:
501 * @element: #GstMatroskaMux that should be reseted.
503 * Reset matroska muxer back to initial state.
506 gst_matroska_mux_reset (GstElement * element)
508 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
511 /* reset EBML write */
512 gst_ebml_write_reset (mux->ebml_write);
515 mux->state = GST_MATROSKA_MUX_STATE_START;
517 /* clean up existing streams */
519 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
520 GstMatroskaPad *collect_pad;
522 collect_pad = (GstMatroskaPad *) walk->data;
524 /* reset collect pad to pristine state */
525 gst_matroska_pad_reset (collect_pad, FALSE);
529 mux->num_indexes = 0;
534 mux->time_scale = GST_MSECOND;
539 mux->cluster_time = 0;
540 mux->cluster_pos = 0;
543 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
547 * gst_matroska_mux_handle_src_event:
548 * @pad: Pad which received the event.
549 * @event: Received event.
551 * handle events - copied from oggmux without understanding
553 * Returns: #TRUE on success.
556 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
560 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
564 /* disable seeking for now */
570 return gst_pad_event_default (pad, event);
574 * gst_matroska_mux_handle_sink_event:
575 * @pad: Pad which received the event.
576 * @event: Received event.
578 * handle events - informational ones like tags
580 * Returns: #TRUE on success.
583 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
585 GstMatroskaTrackContext *context;
586 GstMatroskaPad *collect_pad;
591 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
593 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
594 switch (GST_EVENT_TYPE (event)) {
598 GST_DEBUG_OBJECT (mux, "received tag event");
599 gst_event_parse_tag (event, &list);
601 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
602 g_assert (collect_pad);
603 context = collect_pad->track;
606 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
607 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
608 const gchar *lang_code;
610 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
612 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
613 context->language = g_strdup (lang_code);
615 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
620 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
621 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
624 case GST_EVENT_NEWSEGMENT:
625 /* We don't support NEWSEGMENT events */
627 gst_event_unref (event);
633 /* now GstCollectPads can take care of the rest, e.g. EOS */
635 ret = mux->collect_event (pad, event);
636 gst_object_unref (mux);
643 * gst_matroska_mux_video_pad_setcaps:
644 * @pad: Pad which got the caps.
647 * Setcaps function for video sink pad.
649 * Returns: #TRUE on success.
652 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
654 GstMatroskaTrackContext *context = NULL;
655 GstMatroskaTrackVideoContext *videocontext;
657 GstMatroskaPad *collect_pad;
658 GstStructure *structure;
659 const gchar *mimetype;
660 const GValue *value = NULL;
661 const GstBuffer *codec_buf = NULL;
662 gint width, height, pixel_width, pixel_height;
665 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
668 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
669 g_assert (collect_pad);
670 context = collect_pad->track;
672 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
673 videocontext = (GstMatroskaTrackVideoContext *) context;
675 /* gst -> matroska ID'ing */
676 structure = gst_caps_get_structure (caps, 0);
678 mimetype = gst_structure_get_name (structure);
680 if (!strcmp (mimetype, "video/x-theora")) {
681 /* we'll extract the details later from the theora identification header */
685 /* get general properties */
686 /* spec says it is mandatory */
687 if (!gst_structure_get_int (structure, "width", &width) ||
688 !gst_structure_get_int (structure, "height", &height))
691 videocontext->pixel_width = width;
692 videocontext->pixel_height = height;
693 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
695 context->default_duration =
696 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
697 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
698 GST_TIME_ARGS (context->default_duration));
700 context->default_duration = 0;
702 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
703 &pixel_width, &pixel_height)) {
704 if (pixel_width > pixel_height) {
705 videocontext->display_width = width * pixel_width / pixel_height;
706 videocontext->display_height = height;
707 } else if (pixel_width < pixel_height) {
708 videocontext->display_width = width;
709 videocontext->display_height = height * pixel_height / pixel_width;
711 videocontext->display_width = 0;
712 videocontext->display_height = 0;
715 videocontext->display_width = 0;
716 videocontext->display_height = 0;
721 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
722 videocontext->fourcc = 0;
724 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
725 * data and other settings
729 /* extract codec_data, may turn out needed */
730 value = gst_structure_get_value (structure, "codec_data");
732 codec_buf = gst_value_get_buffer (value);
735 if (!strcmp (mimetype, "video/x-raw-yuv")) {
736 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
737 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
738 } else if (!strcmp (mimetype, "image/jpeg")) {
739 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
740 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
741 ||!strcmp (mimetype, "video/x-huffyuv")
742 || !strcmp (mimetype, "video/x-divx")
743 || !strcmp (mimetype, "video/x-dv")
744 || !strcmp (mimetype, "video/x-h263")
745 || !strcmp (mimetype, "video/x-msmpeg")
746 || !strcmp (mimetype, "video/x-wmv")) {
747 gst_riff_strf_vids *bih;
748 gint size = sizeof (gst_riff_strf_vids);
751 if (!strcmp (mimetype, "video/x-xvid"))
752 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
753 else if (!strcmp (mimetype, "video/x-huffyuv"))
754 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
755 else if (!strcmp (mimetype, "video/x-dv"))
756 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
757 else if (!strcmp (mimetype, "video/x-h263"))
758 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
759 else if (!strcmp (mimetype, "video/x-divx")) {
762 gst_structure_get_int (structure, "divxversion", &divxversion);
763 switch (divxversion) {
765 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
768 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
771 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
774 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
777 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
778 switch (msmpegversion) {
780 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
783 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
789 } else if (!strcmp (mimetype, "video/x-wmv")) {
792 if (gst_structure_get_fourcc (structure, "format", &format)) {
794 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
795 if (wmvversion == 2) {
796 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
797 } else if (wmvversion == 1) {
798 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
799 } else if (wmvversion == 3) {
800 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
808 bih = g_new0 (gst_riff_strf_vids, 1);
809 GST_WRITE_UINT32_LE (&bih->size, size);
810 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
811 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
812 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
813 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
814 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
815 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
816 videocontext->pixel_height * 3);
818 /* process codec private/initialization data, if any */
820 size += GST_BUFFER_SIZE (codec_buf);
821 bih = g_realloc (bih, size);
822 GST_WRITE_UINT32_LE (&bih->size, size);
823 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
824 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
827 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
828 context->codec_priv = (gpointer) bih;
829 context->codec_priv_size = size;
830 } else if (!strcmp (mimetype, "video/x-h264")) {
831 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
833 if (context->codec_priv != NULL) {
834 g_free (context->codec_priv);
835 context->codec_priv = NULL;
836 context->codec_priv_size = 0;
839 /* Create avcC header */
840 if (codec_buf != NULL) {
841 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
842 context->codec_priv = g_malloc0 (context->codec_priv_size);
843 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
844 context->codec_priv_size);
846 } else if (!strcmp (mimetype, "video/x-theora")) {
847 const GValue *streamheader;
849 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
851 if (context->codec_priv != NULL) {
852 g_free (context->codec_priv);
853 context->codec_priv = NULL;
854 context->codec_priv_size = 0;
857 streamheader = gst_structure_get_value (structure, "streamheader");
858 if (!theora_streamheader_to_codecdata (streamheader, context)) {
859 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
860 ("theora stream headers missing or malformed"));
863 } else if (!strcmp (mimetype, "video/x-dirac")) {
864 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
865 } else if (!strcmp (mimetype, "video/mpeg")) {
868 gst_structure_get_int (structure, "mpegversion", &mpegversion);
869 switch (mpegversion) {
871 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
874 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
877 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
883 /* global headers may be in codec data */
884 if (codec_buf != NULL) {
885 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
886 context->codec_priv = g_malloc0 (context->codec_priv_size);
887 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
888 context->codec_priv_size);
890 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
892 /* can only make it here if preceding case verified it was version 3 */
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
894 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
896 const GValue *mdpr_data;
898 gst_structure_get_int (structure, "rmversion", &rmversion);
901 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
904 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
907 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
910 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
916 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
917 if (mdpr_data != NULL) {
918 guint8 *priv_data = NULL;
919 guint priv_data_size = 0;
921 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
923 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
924 priv_data = g_malloc0 (priv_data_size);
926 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
928 context->codec_priv = priv_data;
929 context->codec_priv_size = priv_data_size;
938 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
939 GST_PAD_NAME (pad), caps);
944 /* N > 0 to expect a particular number of headers, negative if the
945 number of headers is variable */
947 xiphN_streamheader_to_codecdata (const GValue * streamheader,
948 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
950 GstBuffer **buf = NULL;
953 guint bufi, i, offset, priv_data_size;
955 if (streamheader == NULL)
956 goto no_stream_headers;
958 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
961 bufarr = g_value_peek_pointer (streamheader);
962 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
964 if (N > 0 && bufarr->len != N)
967 context->xiph_headers_to_skip = bufarr->len;
969 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
970 for (i = 0; i < bufarr->len; i++) {
971 GValue *bufval = &g_array_index (bufarr, GValue, i);
973 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
975 goto wrong_content_type;
978 buf[i] = g_value_peek_pointer (bufval);
982 if (bufarr->len > 0) {
983 for (i = 0; i < bufarr->len - 1; i++) {
984 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
988 for (i = 0; i < bufarr->len; ++i) {
989 priv_data_size += GST_BUFFER_SIZE (buf[i]);
992 priv_data = g_malloc0 (priv_data_size);
994 priv_data[0] = bufarr->len - 1;
997 if (bufarr->len > 0) {
998 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
999 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1000 priv_data[offset++] = 0xff;
1002 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1006 for (i = 0; i < bufarr->len; ++i) {
1007 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1008 GST_BUFFER_SIZE (buf[i]));
1009 offset += GST_BUFFER_SIZE (buf[i]);
1012 context->codec_priv = priv_data;
1013 context->codec_priv_size = priv_data_size;
1016 *p_buf0 = gst_buffer_ref (buf[0]);
1025 GST_WARNING ("required streamheaders missing in sink caps!");
1030 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1031 G_VALUE_TYPE_NAME (streamheader));
1036 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1041 GST_WARNING ("streamheaders array does not contain GstBuffers");
1047 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1048 GstMatroskaTrackContext * context)
1050 GstBuffer *buf0 = NULL;
1052 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1055 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1056 GST_WARNING ("First vorbis header too small, ignoring");
1058 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1059 GstMatroskaTrackAudioContext *audiocontext;
1062 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1063 audiocontext = (GstMatroskaTrackAudioContext *) context;
1064 audiocontext->channels = GST_READ_UINT8 (hdr);
1065 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1070 gst_buffer_unref (buf0);
1076 theora_streamheader_to_codecdata (const GValue * streamheader,
1077 GstMatroskaTrackContext * context)
1079 GstBuffer *buf0 = NULL;
1081 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1084 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1085 GST_WARNING ("First theora header too small, ignoring");
1086 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1087 GST_WARNING ("First header not a theora identification header, ignoring");
1089 GstMatroskaTrackVideoContext *videocontext;
1090 guint fps_num, fps_denom, par_num, par_denom;
1093 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1095 videocontext = (GstMatroskaTrackVideoContext *) context;
1096 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1097 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1098 hdr += 3 + 3 + 1 + 1;
1099 fps_num = GST_READ_UINT32_BE (hdr);
1100 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1101 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1102 fps_denom, fps_num);
1104 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1105 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1106 if (par_num > 0 && par_num > 0) {
1107 if (par_num > par_denom) {
1108 videocontext->display_width =
1109 videocontext->pixel_width * par_num / par_denom;
1110 videocontext->display_height = videocontext->pixel_height;
1111 } else if (par_num < par_denom) {
1112 videocontext->display_width = videocontext->pixel_width;
1113 videocontext->display_height =
1114 videocontext->pixel_height * par_denom / par_num;
1116 videocontext->display_width = 0;
1117 videocontext->display_height = 0;
1120 videocontext->display_width = 0;
1121 videocontext->display_height = 0;
1127 gst_buffer_unref (buf0);
1133 kate_streamheader_to_codecdata (const GValue * streamheader,
1134 GstMatroskaTrackContext * context)
1136 GstBuffer *buf0 = NULL;
1138 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1141 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1142 GST_WARNING ("First kate header too small, ignoring");
1143 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1144 GST_WARNING ("First header not a kate identification header, ignoring");
1148 gst_buffer_unref (buf0);
1154 flac_streamheader_to_codecdata (const GValue * streamheader,
1155 GstMatroskaTrackContext * context)
1162 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1163 GST_WARNING ("No or invalid streamheader field in the caps");
1167 bufarr = g_value_peek_pointer (streamheader);
1168 if (bufarr->len < 2) {
1169 GST_WARNING ("Too few headers in streamheader field");
1173 context->xiph_headers_to_skip = bufarr->len + 1;
1175 bufval = &g_array_index (bufarr, GValue, 0);
1176 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1177 GST_WARNING ("streamheaders array does not contain GstBuffers");
1181 buffer = g_value_peek_pointer (bufval);
1183 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1184 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1185 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1186 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1187 GST_WARNING ("Invalid streamheader for FLAC");
1191 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1192 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1193 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1194 GST_BUFFER_SIZE (buffer) - 9);
1196 for (i = 1; i < bufarr->len; i++) {
1197 bufval = &g_array_index (bufarr, GValue, i);
1199 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1200 g_free (context->codec_priv);
1201 context->codec_priv = NULL;
1202 context->codec_priv_size = 0;
1203 GST_WARNING ("streamheaders array does not contain GstBuffers");
1207 buffer = g_value_peek_pointer (bufval);
1209 context->codec_priv =
1210 g_realloc (context->codec_priv,
1211 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1212 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1213 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1214 context->codec_priv_size =
1215 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1222 speex_streamheader_to_codecdata (const GValue * streamheader,
1223 GstMatroskaTrackContext * context)
1229 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1230 GST_WARNING ("No or invalid streamheader field in the caps");
1234 bufarr = g_value_peek_pointer (streamheader);
1235 if (bufarr->len != 2) {
1236 GST_WARNING ("Too few headers in streamheader field");
1240 context->xiph_headers_to_skip = bufarr->len + 1;
1242 bufval = &g_array_index (bufarr, GValue, 0);
1243 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1244 GST_WARNING ("streamheaders array does not contain GstBuffers");
1248 buffer = g_value_peek_pointer (bufval);
1250 if (GST_BUFFER_SIZE (buffer) < 80
1251 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1252 GST_WARNING ("Invalid streamheader for Speex");
1256 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1257 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1258 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1259 GST_BUFFER_SIZE (buffer));
1261 bufval = &g_array_index (bufarr, GValue, 1);
1263 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1264 g_free (context->codec_priv);
1265 context->codec_priv = NULL;
1266 context->codec_priv_size = 0;
1267 GST_WARNING ("streamheaders array does not contain GstBuffers");
1271 buffer = g_value_peek_pointer (bufval);
1273 context->codec_priv =
1274 g_realloc (context->codec_priv,
1275 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1276 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1277 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1278 context->codec_priv_size =
1279 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1284 static const gchar *
1285 aac_codec_data_to_codec_id (const GstBuffer * buf)
1287 const gchar *result;
1290 /* default to MAIN */
1293 if (GST_BUFFER_SIZE (buf) >= 2) {
1294 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1312 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1321 * gst_matroska_mux_audio_pad_setcaps:
1322 * @pad: Pad which got the caps.
1325 * Setcaps function for audio sink pad.
1327 * Returns: #TRUE on success.
1330 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1332 GstMatroskaTrackContext *context = NULL;
1333 GstMatroskaTrackAudioContext *audiocontext;
1334 GstMatroskaMux *mux;
1335 GstMatroskaPad *collect_pad;
1336 const gchar *mimetype;
1337 gint samplerate = 0, channels = 0;
1338 GstStructure *structure;
1339 const GValue *codec_data = NULL;
1340 const GstBuffer *buf = NULL;
1341 const gchar *stream_format = NULL;
1343 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1346 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1347 g_assert (collect_pad);
1348 context = collect_pad->track;
1350 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1351 audiocontext = (GstMatroskaTrackAudioContext *) context;
1353 structure = gst_caps_get_structure (caps, 0);
1354 mimetype = gst_structure_get_name (structure);
1357 gst_structure_get_int (structure, "rate", &samplerate);
1358 gst_structure_get_int (structure, "channels", &channels);
1360 audiocontext->samplerate = samplerate;
1361 audiocontext->channels = channels;
1362 audiocontext->bitdepth = 0;
1363 context->default_duration = 0;
1365 codec_data = gst_structure_get_value (structure, "codec_data");
1367 buf = gst_value_get_buffer (codec_data);
1369 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1370 * data and other settings
1374 if (!strcmp (mimetype, "audio/mpeg")) {
1375 gint mpegversion = 0;
1377 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1378 switch (mpegversion) {
1384 gst_structure_get_int (structure, "layer", &layer);
1386 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1387 GST_WARNING_OBJECT (mux,
1388 "Unable to determine MPEG audio version, assuming 1");
1394 else if (layer == 2)
1396 else if (version == 2)
1401 context->default_duration =
1402 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1406 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1409 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1412 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1421 stream_format = gst_structure_get_string (structure, "stream-format");
1422 /* check this is raw aac */
1423 if (stream_format) {
1424 if (strcmp (stream_format, "raw") != 0) {
1425 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1429 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1434 if (mpegversion == 2)
1436 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1437 aac_codec_data_to_codec_id (buf));
1438 else if (mpegversion == 4)
1440 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1441 aac_codec_data_to_codec_id (buf));
1443 g_assert_not_reached ();
1445 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1452 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1454 gint endianness = G_LITTLE_ENDIAN;
1455 gboolean signedness = TRUE;
1457 if (!gst_structure_get_int (structure, "width", &width) ||
1458 !gst_structure_get_int (structure, "depth", &depth) ||
1459 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1460 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1465 !gst_structure_get_int (structure, "endianness", &endianness)) {
1466 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1470 if (width != depth) {
1471 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1475 /* FIXME: where is this spec'ed out? (tpm) */
1476 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1477 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1481 audiocontext->bitdepth = depth;
1482 if (endianness == G_BIG_ENDIAN)
1483 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1485 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1487 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1490 if (!gst_structure_get_int (structure, "width", &width)) {
1491 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1495 audiocontext->bitdepth = width;
1496 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1498 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1499 const GValue *streamheader;
1501 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1503 if (context->codec_priv != NULL) {
1504 g_free (context->codec_priv);
1505 context->codec_priv = NULL;
1506 context->codec_priv_size = 0;
1509 streamheader = gst_structure_get_value (structure, "streamheader");
1510 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1511 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1512 ("vorbis stream headers missing or malformed"));
1515 } else if (!strcmp (mimetype, "audio/x-flac")) {
1516 const GValue *streamheader;
1518 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1519 if (context->codec_priv != NULL) {
1520 g_free (context->codec_priv);
1521 context->codec_priv = NULL;
1522 context->codec_priv_size = 0;
1525 streamheader = gst_structure_get_value (structure, "streamheader");
1526 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1527 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1528 ("flac stream headers missing or malformed"));
1531 } else if (!strcmp (mimetype, "audio/x-speex")) {
1532 const GValue *streamheader;
1534 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1535 if (context->codec_priv != NULL) {
1536 g_free (context->codec_priv);
1537 context->codec_priv = NULL;
1538 context->codec_priv_size = 0;
1541 streamheader = gst_structure_get_value (structure, "streamheader");
1542 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1543 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1544 ("speex stream headers missing or malformed"));
1547 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1548 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1549 } else if (!strcmp (mimetype, "audio/x-tta")) {
1552 /* TTA frame duration */
1553 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1555 gst_structure_get_int (structure, "width", &width);
1556 audiocontext->bitdepth = width;
1557 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1559 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1561 const GValue *mdpr_data;
1563 gst_structure_get_int (structure, "raversion", &raversion);
1564 switch (raversion) {
1566 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1569 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1572 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1578 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1579 if (mdpr_data != NULL) {
1580 guint8 *priv_data = NULL;
1581 guint priv_data_size = 0;
1583 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1585 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1586 priv_data = g_malloc0 (priv_data_size);
1588 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1590 context->codec_priv = priv_data;
1591 context->codec_priv_size = priv_data_size;
1594 } else if (!strcmp (mimetype, "audio/x-wma")) {
1596 guint codec_priv_size;
1603 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1604 || !gst_structure_get_int (structure, "block_align", &block_align)
1605 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1606 || samplerate == 0 || channels == 0) {
1607 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1608 "channels/rate on WMA caps");
1612 switch (wmaversion) {
1614 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1617 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1620 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1623 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1627 if (gst_structure_get_int (structure, "depth", &depth))
1628 audiocontext->bitdepth = depth;
1630 codec_priv_size = WAVEFORMATEX_SIZE;
1632 codec_priv_size += GST_BUFFER_SIZE (buf);
1634 /* serialize waveformatex structure */
1635 codec_priv = g_malloc0 (codec_priv_size);
1636 GST_WRITE_UINT16_LE (codec_priv, format);
1637 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1638 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1639 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1640 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1641 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1643 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1645 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1647 /* process codec private/initialization data, if any */
1649 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1650 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1653 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1654 context->codec_priv = (gpointer) codec_priv;
1655 context->codec_priv_size = codec_priv_size;
1663 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1664 GST_PAD_NAME (pad), caps);
1671 * gst_matroska_mux_subtitle_pad_setcaps:
1672 * @pad: Pad which got the caps.
1675 * Setcaps function for subtitle sink pad.
1677 * Returns: #TRUE on success.
1680 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1683 * Consider this as boilerplate code for now. There is
1684 * no single subtitle creation element in GStreamer,
1685 * neither do I know how subtitling works at all. */
1687 /* There is now (at least) one such alement (kateenc), and I'm going
1688 to handle it here and claim it works when it can be piped back
1689 through GStreamer and VLC */
1691 GstMatroskaTrackContext *context = NULL;
1692 GstMatroskaTrackSubtitleContext *scontext;
1693 GstMatroskaMux *mux;
1694 GstMatroskaPad *collect_pad;
1695 const gchar *mimetype;
1696 GstStructure *structure;
1698 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1701 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1702 g_assert (collect_pad);
1703 context = collect_pad->track;
1705 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1706 scontext = (GstMatroskaTrackSubtitleContext *) context;
1708 structure = gst_caps_get_structure (caps, 0);
1709 mimetype = gst_structure_get_name (structure);
1712 scontext->check_utf8 = 1;
1713 scontext->invalid_utf8 = 0;
1714 context->default_duration = 0;
1716 /* TODO: - other format than Kate */
1718 if (!strcmp (mimetype, "subtitle/x-kate")) {
1719 const GValue *streamheader;
1721 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1723 if (context->codec_priv != NULL) {
1724 g_free (context->codec_priv);
1725 context->codec_priv = NULL;
1726 context->codec_priv_size = 0;
1729 streamheader = gst_structure_get_value (structure, "streamheader");
1730 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1731 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1732 ("kate stream headers missing or malformed"));
1743 * gst_matroska_mux_request_new_pad:
1744 * @element: #GstMatroskaMux.
1745 * @templ: #GstPadTemplate.
1746 * @pad_name: New pad name.
1748 * Request pad function for sink templates.
1750 * Returns: New #GstPad.
1753 gst_matroska_mux_request_new_pad (GstElement * element,
1754 GstPadTemplate * templ, const gchar * pad_name)
1756 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1757 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1758 GstMatroskaPad *collect_pad;
1759 GstPad *newpad = NULL;
1761 GstPadSetCapsFunction setcapsfunc = NULL;
1762 GstMatroskaTrackContext *context = NULL;
1764 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1765 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1766 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1767 context = (GstMatroskaTrackContext *)
1768 g_new0 (GstMatroskaTrackAudioContext, 1);
1769 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1770 context->name = g_strdup ("Audio");
1771 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1772 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1773 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1774 context = (GstMatroskaTrackContext *)
1775 g_new0 (GstMatroskaTrackVideoContext, 1);
1776 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1777 context->name = g_strdup ("Video");
1778 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1779 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1780 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1781 context = (GstMatroskaTrackContext *)
1782 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1783 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1784 context->name = g_strdup ("Subtitle");
1786 GST_WARNING_OBJECT (mux, "This is not our template!");
1790 newpad = gst_pad_new_from_template (templ, name);
1792 collect_pad = (GstMatroskaPad *)
1793 gst_collect_pads_add_pad_full (mux->collect, newpad,
1794 sizeof (GstMatroskaPad),
1795 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1797 collect_pad->track = context;
1798 gst_matroska_pad_reset (collect_pad, FALSE);
1800 /* FIXME: hacked way to override/extend the event function of
1801 * GstCollectPads; because it sets its own event function giving the
1802 * element no access to events.
1803 * TODO GstCollectPads should really give its 'users' a clean chance to
1804 * properly handle events that are not meant for collectpads itself.
1805 * Perhaps a callback or so, though rejected (?) in #340060.
1806 * This would allow (clean) transcoding of info from demuxer/streams
1807 * to another muxer */
1808 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1809 gst_pad_set_event_function (newpad,
1810 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1812 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1813 gst_pad_set_active (newpad, TRUE);
1814 gst_element_add_pad (element, newpad);
1821 * gst_matroska_mux_release_pad:
1822 * @element: #GstMatroskaMux.
1823 * @pad: Pad to release.
1825 * Release a previously requested pad.
1828 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1830 GstMatroskaMux *mux;
1833 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1835 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1836 GstCollectData *cdata = (GstCollectData *) walk->data;
1837 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1839 if (cdata->pad == pad) {
1840 GstClockTime min_dur; /* observed minimum duration */
1842 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1843 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1844 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1845 if (collect_pad->duration < min_dur)
1846 collect_pad->duration = min_dur;
1849 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1850 mux->duration < collect_pad->duration)
1851 mux->duration = collect_pad->duration;
1857 gst_collect_pads_remove_pad (mux->collect, pad);
1858 if (gst_element_remove_pad (element, pad))
1864 * gst_matroska_mux_track_header:
1865 * @mux: #GstMatroskaMux
1866 * @context: Tack context.
1868 * Write a track header.
1871 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1872 GstMatroskaTrackContext * context)
1874 GstEbmlWrite *ebml = mux->ebml_write;
1877 /* TODO: check if everything necessary is written and check default values */
1879 /* track type goes before the type-specific stuff */
1880 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1881 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1883 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1884 gst_matroska_mux_create_uid ());
1885 if (context->default_duration) {
1886 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1887 context->default_duration);
1889 if (context->language) {
1890 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1894 /* type-specific stuff */
1895 switch (context->type) {
1896 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1897 GstMatroskaTrackVideoContext *videocontext =
1898 (GstMatroskaTrackVideoContext *) context;
1900 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1902 videocontext->pixel_width);
1903 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1904 videocontext->pixel_height);
1905 if (videocontext->display_width && videocontext->display_height) {
1906 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1907 videocontext->display_width);
1908 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1909 videocontext->display_height);
1911 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1912 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1913 if (videocontext->fourcc) {
1914 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1916 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1917 (gpointer) & fcc_le, 4);
1919 gst_ebml_write_master_finish (ebml, master);
1924 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1925 GstMatroskaTrackAudioContext *audiocontext =
1926 (GstMatroskaTrackAudioContext *) context;
1928 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1929 if (audiocontext->samplerate != 8000)
1930 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1931 audiocontext->samplerate);
1932 if (audiocontext->channels != 1)
1933 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1934 audiocontext->channels);
1935 if (audiocontext->bitdepth) {
1936 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1937 audiocontext->bitdepth);
1939 gst_ebml_write_master_finish (ebml, master);
1945 /* doesn't need type-specific data */
1949 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1950 if (context->codec_priv)
1951 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1952 context->codec_priv, context->codec_priv_size);
1953 /* FIXME: until we have a nice way of getting the codecname
1954 * out of the caps, I'm not going to enable this. Too much
1955 * (useless, double, boring) work... */
1956 /* TODO: Use value from tags if any */
1957 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1958 context->codec_name); */
1959 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1964 * gst_matroska_mux_start:
1965 * @mux: #GstMatroskaMux
1967 * Start a new matroska file (write headers etc...)
1970 gst_matroska_mux_start (GstMatroskaMux * mux)
1972 GstEbmlWrite *ebml = mux->ebml_write;
1973 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1974 GST_MATROSKA_ID_TRACKS,
1975 GST_MATROSKA_ID_CUES,
1976 GST_MATROSKA_ID_TAGS,
1979 guint64 master, child;
1983 GstClockTime duration = 0;
1984 guint32 segment_uid[4];
1985 GTimeVal time = { 0, 0 };
1987 /* we start with a EBML header */
1988 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
1990 /* start a segment */
1992 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
1993 mux->segment_master = ebml->pos;
1995 /* the rest of the header is cached */
1996 gst_ebml_write_set_cache (ebml, 0x1000);
1998 /* seekhead (table of contents) - we set the positions later */
1999 mux->seekhead_pos = ebml->pos;
2000 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2001 for (i = 0; seekhead_id[i] != 0; i++) {
2002 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2003 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2004 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2005 gst_ebml_write_master_finish (ebml, child);
2007 gst_ebml_write_master_finish (ebml, master);
2010 mux->info_pos = ebml->pos;
2011 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2012 for (i = 0; i < 4; i++) {
2013 segment_uid[i] = g_random_int ();
2015 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2016 (guint8 *) segment_uid, 16);
2017 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2018 mux->duration_pos = ebml->pos;
2020 for (collected = mux->collect->data; collected;
2021 collected = g_slist_next (collected)) {
2022 GstMatroskaPad *collect_pad;
2023 GstFormat format = GST_FORMAT_TIME;
2025 gint64 trackduration;
2027 collect_pad = (GstMatroskaPad *) collected->data;
2028 thepad = collect_pad->collect.pad;
2030 /* Query the total length of the track. */
2031 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2032 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2033 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2034 GST_TIME_ARGS (trackduration));
2035 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2036 duration = (GstClockTime) trackduration;
2040 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2041 gst_guint64_to_gdouble (duration) /
2042 gst_guint64_to_gdouble (mux->time_scale));
2044 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2045 "GStreamer plugin version " PACKAGE_VERSION);
2046 if (mux->writing_app && mux->writing_app[0]) {
2047 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2049 g_get_current_time (&time);
2050 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2051 gst_ebml_write_master_finish (ebml, master);
2054 mux->tracks_pos = ebml->pos;
2055 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2057 for (collected = mux->collect->data; collected;
2058 collected = g_slist_next (collected)) {
2059 GstMatroskaPad *collect_pad;
2062 collect_pad = (GstMatroskaPad *) collected->data;
2063 thepad = collect_pad->collect.pad;
2065 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2066 collect_pad->track->codec_id != 0) {
2067 collect_pad->track->num = tracknum++;
2068 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2069 gst_matroska_mux_track_header (mux, collect_pad->track);
2070 gst_ebml_write_master_finish (ebml, child);
2073 gst_ebml_write_master_finish (ebml, master);
2075 /* lastly, flush the cache */
2076 gst_ebml_write_flush_cache (ebml);
2080 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2083 /* TODO: more sensible tag mappings */
2086 const gchar *matroska_tagname;
2087 const gchar *gstreamer_tagname;
2091 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2092 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2093 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2094 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2095 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2096 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2097 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2098 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2099 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2100 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2101 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2102 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2103 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2104 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2105 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2107 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2109 guint64 simpletag_master;
2111 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2112 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2113 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2115 if (strcmp (tagname_gst, tag) == 0) {
2116 GValue src = { 0, };
2119 if (!gst_tag_list_copy_value (&src, list, tag))
2121 if ((dest = gst_value_serialize (&src))) {
2123 simpletag_master = gst_ebml_write_master_start (ebml,
2124 GST_MATROSKA_ID_SIMPLETAG);
2125 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2126 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2127 gst_ebml_write_master_finish (ebml, simpletag_master);
2130 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2132 g_value_unset (&src);
2140 * gst_matroska_mux_finish:
2141 * @mux: #GstMatroskaMux
2143 * Finish a new matroska file (write index etc...)
2146 gst_matroska_mux_finish (GstMatroskaMux * mux)
2148 GstEbmlWrite *ebml = mux->ebml_write;
2150 guint64 duration = 0;
2152 const GstTagList *tags;
2154 /* finish last cluster */
2156 gst_ebml_write_master_finish (ebml, mux->cluster);
2160 if (mux->index != NULL) {
2162 guint64 master, pointentry_master, trackpos_master;
2164 mux->cues_pos = ebml->pos;
2165 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2166 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2168 for (n = 0; n < mux->num_indexes; n++) {
2169 GstMatroskaIndex *idx = &mux->index[n];
2171 pointentry_master = gst_ebml_write_master_start (ebml,
2172 GST_MATROSKA_ID_POINTENTRY);
2173 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2174 idx->time / mux->time_scale);
2175 trackpos_master = gst_ebml_write_master_start (ebml,
2176 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2177 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2178 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2179 idx->pos - mux->segment_master);
2180 gst_ebml_write_master_finish (ebml, trackpos_master);
2181 gst_ebml_write_master_finish (ebml, pointentry_master);
2184 gst_ebml_write_master_finish (ebml, master);
2185 gst_ebml_write_flush_cache (ebml);
2189 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2191 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2192 guint64 master_tags, master_tag;
2194 GST_DEBUG ("Writing tags");
2196 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2197 mux->tags_pos = ebml->pos;
2198 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2199 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2200 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2201 gst_ebml_write_master_finish (ebml, master_tag);
2202 gst_ebml_write_master_finish (ebml, master_tags);
2205 /* update seekhead. We know that:
2206 * - a seekhead contains 4 entries.
2207 * - order of entries is as above.
2208 * - a seekhead has a 4-byte header + 8-byte length
2209 * - each entry is 2-byte master, 2-byte ID pointer,
2210 * 2-byte length pointer, all 8/1-byte length, 4-
2211 * byte ID and 8-byte length pointer, where the
2212 * length pointer starts at 20.
2213 * - all entries are local to the segment (so pos - segment_master).
2214 * - so each entry is at 12 + 20 + num * 28. */
2215 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2216 mux->info_pos - mux->segment_master);
2217 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2218 mux->tracks_pos - mux->segment_master);
2219 if (mux->index != NULL) {
2220 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2221 mux->cues_pos - mux->segment_master);
2224 guint64 my_pos = ebml->pos;
2226 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2227 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2228 gst_ebml_write_seek (ebml, my_pos);
2231 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2232 mux->tags_pos - mux->segment_master);
2235 guint64 my_pos = ebml->pos;
2237 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2238 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2239 gst_ebml_write_seek (ebml, my_pos);
2242 /* update duration */
2243 /* first get the overall duration */
2244 /* a released track may have left a duration in here */
2245 duration = mux->duration;
2246 for (collected = mux->collect->data; collected;
2247 collected = g_slist_next (collected)) {
2248 GstMatroskaPad *collect_pad;
2249 GstClockTime min_duration; /* observed minimum duration */
2251 collect_pad = (GstMatroskaPad *) collected->data;
2253 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2254 " end ts %" GST_TIME_FORMAT, collect_pad,
2255 GST_TIME_ARGS (collect_pad->start_ts),
2256 GST_TIME_ARGS (collect_pad->end_ts));
2258 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2259 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2261 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2262 if (collect_pad->duration < min_duration)
2263 collect_pad->duration = min_duration;
2264 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2265 GST_TIME_ARGS (collect_pad->duration));
2268 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2269 duration < collect_pad->duration)
2270 duration = collect_pad->duration;
2272 if (duration != 0) {
2273 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2274 GST_TIME_ARGS (duration));
2275 pos = mux->ebml_write->pos;
2276 gst_ebml_write_seek (ebml, mux->duration_pos);
2277 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2278 gst_guint64_to_gdouble (duration) /
2279 gst_guint64_to_gdouble (mux->time_scale));
2280 gst_ebml_write_seek (ebml, pos);
2283 guint64 my_pos = ebml->pos;
2285 gst_ebml_write_seek (ebml, mux->duration_pos);
2286 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2287 gst_ebml_write_seek (ebml, my_pos);
2290 /* finish segment - this also writes element length */
2291 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2296 * gst_matroska_mux_best_pad:
2297 * @mux: #GstMatroskaMux
2298 * @popped: True if at least one buffer was popped from #GstCollectPads
2300 * Find a pad with the oldest data
2301 * (data from this pad should be written first).
2303 * Returns: Selected pad.
2305 static GstMatroskaPad *
2306 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2309 GstMatroskaPad *best = NULL;
2312 for (collected = mux->collect->data; collected;
2313 collected = g_slist_next (collected)) {
2314 GstMatroskaPad *collect_pad;
2316 collect_pad = (GstMatroskaPad *) collected->data;
2317 /* fetch a new buffer if needed */
2318 if (collect_pad->buffer == NULL) {
2319 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2320 (GstCollectData *) collect_pad);
2322 if (collect_pad->buffer != NULL)
2326 /* if we have a buffer check if it is better then the current best one */
2327 if (collect_pad->buffer != NULL) {
2328 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2329 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2330 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2331 GST_BUFFER_TIMESTAMP (best->buffer))) {
2341 * gst_matroska_mux_buffer_header:
2342 * @track: Track context.
2343 * @relative_timestamp: relative timestamp of the buffer
2344 * @flags: Buffer flags.
2346 * Create a buffer containing buffer header.
2348 * Returns: New buffer.
2351 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2352 gint16 relative_timestamp, int flags)
2356 hdr = gst_buffer_new_and_alloc (4);
2357 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2358 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2359 /* time relative to clustertime */
2360 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2363 GST_BUFFER_DATA (hdr)[3] = flags;
2368 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2369 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2370 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2373 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2374 GstMatroskaPad * collect_pad, GstBuffer * buf)
2376 GstMatroskaTrackVideoContext *ctx =
2377 (GstMatroskaTrackVideoContext *) collect_pad->track;
2378 const guint8 *data = GST_BUFFER_DATA (buf);
2379 guint size = GST_BUFFER_SIZE (buf);
2381 guint32 next_parse_offset;
2382 GstBuffer *ret = NULL;
2383 gboolean is_muxing_unit = FALSE;
2385 if (GST_BUFFER_SIZE (buf) < 13) {
2386 gst_buffer_unref (buf);
2390 /* Check if this buffer contains a picture or end-of-sequence packet */
2391 while (size >= 13) {
2392 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2393 gst_buffer_unref (buf);
2397 parse_code = GST_READ_UINT8 (data + 4);
2398 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2399 if (ctx->dirac_unit) {
2400 gst_buffer_unref (ctx->dirac_unit);
2401 ctx->dirac_unit = NULL;
2403 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2404 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2405 is_muxing_unit = TRUE;
2409 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2411 if (G_UNLIKELY (next_parse_offset == 0))
2414 data += next_parse_offset;
2415 size -= next_parse_offset;
2418 if (ctx->dirac_unit)
2419 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2421 ctx->dirac_unit = gst_buffer_ref (buf);
2423 if (is_muxing_unit) {
2424 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2425 ctx->dirac_unit = NULL;
2426 gst_buffer_copy_metadata (ret, buf,
2427 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2428 GST_BUFFER_COPY_CAPS);
2429 gst_buffer_unref (buf);
2431 gst_buffer_unref (buf);
2439 * gst_matroska_mux_write_data:
2440 * @mux: #GstMatroskaMux
2441 * @collect_pad: #GstMatroskaPad with the data
2443 * Write collected data (called from gst_matroska_mux_collected).
2445 * Returns: Result of the gst_pad_push issued to write the data.
2447 static GstFlowReturn
2448 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2450 GstEbmlWrite *ebml = mux->ebml_write;
2451 GstBuffer *buf, *hdr;
2453 gboolean write_duration;
2454 gint16 relative_timestamp;
2455 gint64 relative_timestamp64;
2456 guint64 block_duration;
2457 gboolean is_video_keyframe = FALSE;
2460 buf = collect_pad->buffer;
2461 collect_pad->buffer = NULL;
2463 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2464 if (collect_pad->track->xiph_headers_to_skip > 0) {
2465 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2466 gst_buffer_unref (buf);
2467 --collect_pad->track->xiph_headers_to_skip;
2471 /* for dirac we have to queue up everything up to a picture unit */
2472 if (collect_pad->track->codec_id != NULL &&
2473 strcmp (collect_pad->track->codec_id,
2474 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2475 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2480 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2481 * this would wreak havoc with time stored in matroska file */
2482 /* TODO: maybe calculate a timestamp by using the previous timestamp
2483 * and default duration */
2484 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2485 GST_WARNING_OBJECT (collect_pad->collect.pad,
2486 "Invalid buffer timestamp; dropping buffer");
2487 gst_buffer_unref (buf);
2491 /* set the timestamp for outgoing buffers */
2492 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2494 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2495 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2496 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2497 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2498 is_video_keyframe = TRUE;
2502 /* start a new cluster every two seconds or at keyframe */
2503 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2504 || is_video_keyframe) {
2506 gst_ebml_write_master_finish (ebml, mux->cluster);
2507 mux->cluster_pos = ebml->pos;
2509 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2510 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2511 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2512 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2517 mux->cluster_pos = ebml->pos;
2518 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2519 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2520 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2521 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2524 /* update duration of this track */
2525 if (GST_BUFFER_DURATION_IS_VALID (buf))
2526 collect_pad->duration += GST_BUFFER_DURATION (buf);
2528 /* We currently write index entries for all video tracks or for the audio
2529 * track in a single-track audio file. This could be improved by keeping the
2530 * index only for the *first* video track. */
2532 /* TODO: index is useful for every track, should contain the number of
2533 * the block in the cluster which contains the timestamp, should also work
2534 * for files with multiple audio tracks.
2536 if (is_video_keyframe ||
2537 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2538 (mux->num_streams == 1))) {
2541 if (mux->min_index_interval != 0) {
2542 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2543 if (mux->index[last_idx].track == collect_pad->track->num)
2548 if (last_idx < 0 || mux->min_index_interval == 0 ||
2549 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2550 >= mux->min_index_interval)) {
2551 GstMatroskaIndex *idx;
2553 if (mux->num_indexes % 32 == 0) {
2554 mux->index = g_renew (GstMatroskaIndex, mux->index,
2555 mux->num_indexes + 32);
2557 idx = &mux->index[mux->num_indexes++];
2559 idx->pos = mux->cluster_pos;
2560 idx->time = GST_BUFFER_TIMESTAMP (buf);
2561 idx->track = collect_pad->track->num;
2565 /* Check if the duration differs from the default duration. */
2566 write_duration = FALSE;
2567 block_duration = GST_BUFFER_DURATION (buf);
2568 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2569 if (block_duration != collect_pad->track->default_duration) {
2570 write_duration = TRUE;
2574 /* write the block, for matroska v2 use SimpleBlock if possible
2575 * one slice (*breath*).
2576 * FIXME: Need to do correct lacing! */
2577 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2578 if (relative_timestamp64 >= 0) {
2579 /* round the timestamp */
2580 relative_timestamp64 += mux->time_scale / 2;
2582 /* round the timestamp */
2583 relative_timestamp64 -= mux->time_scale / 2;
2585 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2586 if (mux->matroska_version > 1 && !write_duration) {
2588 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2591 gst_matroska_mux_create_buffer_header (collect_pad->track,
2592 relative_timestamp, flags);
2593 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2594 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2595 gst_ebml_write_buffer (ebml, hdr);
2596 gst_ebml_write_buffer (ebml, buf);
2598 return gst_ebml_last_write_result (ebml);
2600 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2602 gst_matroska_mux_create_buffer_header (collect_pad->track,
2603 relative_timestamp, 0);
2604 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2605 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2606 gst_ebml_write_buffer (ebml, hdr);
2607 gst_ebml_write_buffer (ebml, buf);
2608 if (write_duration) {
2609 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2610 block_duration / mux->time_scale);
2612 gst_ebml_write_master_finish (ebml, blockgroup);
2613 return gst_ebml_last_write_result (ebml);
2619 * gst_matroska_mux_collected:
2620 * @pads: #GstCollectPads
2621 * @uuser_data: #GstMatroskaMux
2623 * Collectpads callback.
2625 * Returns: #GstFlowReturn
2627 static GstFlowReturn
2628 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2630 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2631 GstMatroskaPad *best;
2635 GST_DEBUG_OBJECT (mux, "Collected pads");
2637 /* start with a header */
2638 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2639 if (mux->collect->data == NULL) {
2640 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2641 ("No input streams configured"));
2642 return GST_FLOW_ERROR;
2644 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2645 gst_matroska_mux_start (mux);
2646 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2650 /* which stream to write from? */
2651 best = gst_matroska_mux_best_pad (mux, &popped);
2653 /* if there is no best pad, we have reached EOS */
2655 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2656 gst_matroska_mux_finish (mux);
2657 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2658 ret = GST_FLOW_UNEXPECTED;
2661 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2662 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2663 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2664 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2666 /* make note of first and last encountered timestamps, so we can calculate
2667 * the actual duration later when we send an updated header on eos */
2668 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2669 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2670 GstClockTime end_ts = start_ts;
2672 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2673 end_ts += GST_BUFFER_DURATION (best->buffer);
2674 else if (best->track->default_duration)
2675 end_ts += best->track->default_duration;
2677 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2678 best->end_ts = end_ts;
2680 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2681 start_ts < best->start_ts))
2682 best->start_ts = start_ts;
2685 /* write one buffer */
2686 ret = gst_matroska_mux_write_data (mux, best);
2687 } while (ret == GST_FLOW_OK && !popped);
2694 * gst_matroska_mux_change_state:
2695 * @element: #GstMatroskaMux
2696 * @transition: State change transition.
2698 * Change the muxer state.
2700 * Returns: #GstStateChangeReturn
2702 static GstStateChangeReturn
2703 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2705 GstStateChangeReturn ret;
2706 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2708 switch (transition) {
2709 case GST_STATE_CHANGE_NULL_TO_READY:
2711 case GST_STATE_CHANGE_READY_TO_PAUSED:
2712 gst_collect_pads_start (mux->collect);
2714 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2716 case GST_STATE_CHANGE_PAUSED_TO_READY:
2717 gst_collect_pads_stop (mux->collect);
2723 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2725 switch (transition) {
2726 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2728 case GST_STATE_CHANGE_PAUSED_TO_READY:
2729 gst_matroska_mux_reset (GST_ELEMENT (mux));
2731 case GST_STATE_CHANGE_READY_TO_NULL:
2741 gst_matroska_mux_set_property (GObject * object,
2742 guint prop_id, const GValue * value, GParamSpec * pspec)
2744 GstMatroskaMux *mux;
2746 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2747 mux = GST_MATROSKA_MUX (object);
2750 case ARG_WRITING_APP:
2751 if (!g_value_get_string (value)) {
2752 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2755 g_free (mux->writing_app);
2756 mux->writing_app = g_value_dup_string (value);
2758 case ARG_MATROSKA_VERSION:
2759 mux->matroska_version = g_value_get_int (value);
2761 case ARG_MIN_INDEX_INTERVAL:
2762 mux->min_index_interval = g_value_get_int64 (value);
2765 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2771 gst_matroska_mux_get_property (GObject * object,
2772 guint prop_id, GValue * value, GParamSpec * pspec)
2774 GstMatroskaMux *mux;
2776 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2777 mux = GST_MATROSKA_MUX (object);
2780 case ARG_WRITING_APP:
2781 g_value_set_string (value, mux->writing_app);
2783 case ARG_MATROSKA_VERSION:
2784 g_value_set_int (value, mux->matroska_version);
2786 case ARG_MIN_INDEX_INTERVAL:
2787 g_value_set_int64 (value, mux->min_index_interval);
2790 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2796 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2798 return gst_element_register (plugin, "matroskamux",
2799 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);