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 COMMON_VIDEO_CAPS "; "
127 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
128 COMMON_VIDEO_CAPS "; "
129 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
132 #define COMMON_AUDIO_CAPS \
133 "channels = (int) [ 1, MAX ], " \
134 "rate = (int) [ 1, MAX ]"
137 * * require codec data, etc as needed
139 static GstStaticPadTemplate audiosink_templ =
140 GST_STATIC_PAD_TEMPLATE ("audio_%d",
143 GST_STATIC_CAPS ("audio/mpeg, "
144 "mpegversion = (int) 1, "
145 "layer = (int) [ 1, 3 ], "
146 "stream-format = (string) { raw }, "
147 COMMON_AUDIO_CAPS "; "
149 "mpegversion = (int) { 2, 4 }, "
150 COMMON_AUDIO_CAPS "; "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
162 "signed = (boolean) false, "
163 COMMON_AUDIO_CAPS ";"
167 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
168 "signed = (boolean) true, "
169 COMMON_AUDIO_CAPS ";"
173 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
174 "signed = (boolean) true, "
175 COMMON_AUDIO_CAPS ";"
179 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
180 "signed = (boolean) true, "
181 COMMON_AUDIO_CAPS ";"
182 "audio/x-raw-float, "
183 "width = (int) [ 32, 64 ], "
184 "endianness = (int) LITTLE_ENDIAN, "
185 COMMON_AUDIO_CAPS ";"
187 "width = (int) { 8, 16, 24 }, "
188 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
189 "audio/x-pn-realaudio, "
190 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
191 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
192 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
196 static GstStaticPadTemplate subtitlesink_templ =
197 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
200 GST_STATIC_CAPS_ANY);
202 static GArray *used_uids;
203 G_LOCK_DEFINE_STATIC (used_uids);
205 static void gst_matroska_mux_add_interfaces (GType type);
207 GType gst_matroska_mux_get_type (void);
208 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
209 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
211 /* Matroska muxer destructor */
212 static void gst_matroska_mux_finalize (GObject * object);
214 /* Pads collected callback */
216 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
219 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
221 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
222 GstPadTemplate * templ, const gchar * name);
223 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
225 /* gst internal change state handler */
226 static GstStateChangeReturn
227 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
229 /* gobject bla bla */
230 static void gst_matroska_mux_set_property (GObject * object,
231 guint prop_id, const GValue * value, GParamSpec * pspec);
232 static void gst_matroska_mux_get_property (GObject * object,
233 guint prop_id, GValue * value, GParamSpec * pspec);
236 static void gst_matroska_mux_reset (GstElement * element);
239 static guint64 gst_matroska_mux_create_uid ();
241 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
253 gst_matroska_mux_add_interfaces (GType type)
255 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
257 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
261 gst_matroska_mux_base_init (gpointer g_class)
263 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
265 gst_element_class_add_pad_template (element_class,
266 gst_static_pad_template_get (&videosink_templ));
267 gst_element_class_add_pad_template (element_class,
268 gst_static_pad_template_get (&audiosink_templ));
269 gst_element_class_add_pad_template (element_class,
270 gst_static_pad_template_get (&subtitlesink_templ));
271 gst_element_class_add_pad_template (element_class,
272 gst_static_pad_template_get (&src_templ));
273 gst_element_class_set_details_simple (element_class, "Matroska muxer",
275 "Muxes video/audio/subtitle streams into a matroska stream",
276 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
278 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
283 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
285 GObjectClass *gobject_class;
286 GstElementClass *gstelement_class;
288 gobject_class = (GObjectClass *) klass;
289 gstelement_class = (GstElementClass *) klass;
291 gobject_class->finalize = gst_matroska_mux_finalize;
293 gobject_class->get_property = gst_matroska_mux_get_property;
294 gobject_class->set_property = gst_matroska_mux_set_property;
296 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
297 g_param_spec_string ("writing-app", "Writing application.",
298 "The name the application that creates the matroska file.",
299 NULL, G_PARAM_READWRITE));
300 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
301 g_param_spec_int ("version", "Matroska version",
302 "This parameter determines what matroska features can be used.",
303 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
304 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
305 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
306 "entries", "An index entry is created every so many nanoseconds.",
307 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
309 gstelement_class->change_state =
310 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
311 gstelement_class->request_new_pad =
312 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
313 gstelement_class->release_pad =
314 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
319 * gst_matroska_mux_init:
320 * @mux: #GstMatroskaMux that should be initialized.
321 * @g_class: Class of the muxer.
323 * Matroska muxer constructor.
326 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
328 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
329 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
330 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
332 mux->collect = gst_collect_pads_new ();
333 gst_collect_pads_set_function (mux->collect,
334 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
337 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
339 /* property defaults */
340 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
341 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
342 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
344 /* initialize internal variables */
346 mux->num_streams = 0;
347 mux->num_a_streams = 0;
348 mux->num_t_streams = 0;
349 mux->num_v_streams = 0;
351 /* initialize remaining variables */
352 gst_matroska_mux_reset (GST_ELEMENT (mux));
357 * gst_matroska_mux_finalize:
358 * @object: #GstMatroskaMux that should be finalized.
360 * Finalize matroska muxer.
363 gst_matroska_mux_finalize (GObject * object)
365 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
367 gst_object_unref (mux->collect);
368 gst_object_unref (mux->ebml_write);
369 if (mux->writing_app)
370 g_free (mux->writing_app);
372 G_OBJECT_CLASS (parent_class)->finalize (object);
377 * gst_matroska_mux_create_uid:
379 * Generate new unused track UID.
381 * Returns: New track UID.
384 gst_matroska_mux_create_uid (void)
391 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
396 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
397 for (i = 0; i < used_uids->len; i++) {
398 if (g_array_index (used_uids, guint64, i) == uid) {
403 g_array_append_val (used_uids, uid);
406 G_UNLOCK (used_uids);
412 * gst_matroska_pad_reset:
413 * @collect_pad: the #GstMatroskaPad
415 * Reset and/or release resources of a matroska collect pad.
418 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
421 GstMatroskaTrackType type = 0;
423 /* free track information */
424 if (collect_pad->track != NULL) {
425 /* retrieve for optional later use */
426 name = collect_pad->track->name;
427 type = collect_pad->track->type;
428 /* extra for video */
429 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
430 GstMatroskaTrackVideoContext *ctx =
431 (GstMatroskaTrackVideoContext *) collect_pad->track;
433 if (ctx->dirac_unit) {
434 gst_buffer_unref (ctx->dirac_unit);
435 ctx->dirac_unit = NULL;
438 g_free (collect_pad->track->codec_id);
439 g_free (collect_pad->track->codec_name);
441 g_free (collect_pad->track->name);
442 g_free (collect_pad->track->language);
443 g_free (collect_pad->track->codec_priv);
444 g_free (collect_pad->track);
445 collect_pad->track = NULL;
448 /* free cached buffer */
449 if (collect_pad->buffer != NULL) {
450 gst_buffer_unref (collect_pad->buffer);
451 collect_pad->buffer = NULL;
454 if (!full && type != 0) {
455 GstMatroskaTrackContext *context;
457 /* create a fresh context */
459 case GST_MATROSKA_TRACK_TYPE_VIDEO:
460 context = (GstMatroskaTrackContext *)
461 g_new0 (GstMatroskaTrackVideoContext, 1);
463 case GST_MATROSKA_TRACK_TYPE_AUDIO:
464 context = (GstMatroskaTrackContext *)
465 g_new0 (GstMatroskaTrackAudioContext, 1);
467 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
468 context = (GstMatroskaTrackContext *)
469 g_new0 (GstMatroskaTrackSubtitleContext, 1);
472 g_assert_not_reached ();
476 context->type = type;
477 context->name = name;
478 /* TODO: check default values for the context */
479 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
480 collect_pad->track = context;
481 collect_pad->buffer = NULL;
482 collect_pad->duration = 0;
483 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
484 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
489 * gst_matroska_pad_free:
490 * @collect_pad: the #GstMatroskaPad
492 * Release resources of a matroska collect pad.
495 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
497 gst_matroska_pad_reset (collect_pad, TRUE);
502 * gst_matroska_mux_reset:
503 * @element: #GstMatroskaMux that should be reseted.
505 * Reset matroska muxer back to initial state.
508 gst_matroska_mux_reset (GstElement * element)
510 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
513 /* reset EBML write */
514 gst_ebml_write_reset (mux->ebml_write);
517 mux->state = GST_MATROSKA_MUX_STATE_START;
519 /* clean up existing streams */
521 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
522 GstMatroskaPad *collect_pad;
524 collect_pad = (GstMatroskaPad *) walk->data;
526 /* reset collect pad to pristine state */
527 gst_matroska_pad_reset (collect_pad, FALSE);
531 mux->num_indexes = 0;
536 mux->time_scale = GST_MSECOND;
541 mux->cluster_time = 0;
542 mux->cluster_pos = 0;
543 mux->prev_cluster_size = 0;
546 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
550 * gst_matroska_mux_handle_src_event:
551 * @pad: Pad which received the event.
552 * @event: Received event.
554 * handle events - copied from oggmux without understanding
556 * Returns: #TRUE on success.
559 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
563 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
567 /* disable seeking for now */
573 return gst_pad_event_default (pad, event);
577 * gst_matroska_mux_handle_sink_event:
578 * @pad: Pad which received the event.
579 * @event: Received event.
581 * handle events - informational ones like tags
583 * Returns: #TRUE on success.
586 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
588 GstMatroskaTrackContext *context;
589 GstMatroskaPad *collect_pad;
594 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
596 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
597 switch (GST_EVENT_TYPE (event)) {
601 GST_DEBUG_OBJECT (mux, "received tag event");
602 gst_event_parse_tag (event, &list);
604 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
605 g_assert (collect_pad);
606 context = collect_pad->track;
609 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
610 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
611 const gchar *lang_code;
613 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
615 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
616 context->language = g_strdup (lang_code);
618 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
623 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
624 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
627 case GST_EVENT_NEWSEGMENT:
628 /* We don't support NEWSEGMENT events */
630 gst_event_unref (event);
636 /* now GstCollectPads can take care of the rest, e.g. EOS */
638 ret = mux->collect_event (pad, event);
639 gst_object_unref (mux);
646 * gst_matroska_mux_video_pad_setcaps:
647 * @pad: Pad which got the caps.
650 * Setcaps function for video sink pad.
652 * Returns: #TRUE on success.
655 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
657 GstMatroskaTrackContext *context = NULL;
658 GstMatroskaTrackVideoContext *videocontext;
660 GstMatroskaPad *collect_pad;
661 GstStructure *structure;
662 const gchar *mimetype;
663 const GValue *value = NULL;
664 const GstBuffer *codec_buf = NULL;
665 gint width, height, pixel_width, pixel_height;
667 gboolean interlaced = FALSE;
669 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
672 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
673 g_assert (collect_pad);
674 context = collect_pad->track;
676 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
677 videocontext = (GstMatroskaTrackVideoContext *) context;
679 /* gst -> matroska ID'ing */
680 structure = gst_caps_get_structure (caps, 0);
682 mimetype = gst_structure_get_name (structure);
684 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
686 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
688 if (!strcmp (mimetype, "video/x-theora")) {
689 /* we'll extract the details later from the theora identification header */
693 /* get general properties */
694 /* spec says it is mandatory */
695 if (!gst_structure_get_int (structure, "width", &width) ||
696 !gst_structure_get_int (structure, "height", &height))
699 videocontext->pixel_width = width;
700 videocontext->pixel_height = height;
701 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
703 context->default_duration =
704 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
705 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
706 GST_TIME_ARGS (context->default_duration));
708 context->default_duration = 0;
710 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
711 &pixel_width, &pixel_height)) {
712 if (pixel_width > pixel_height) {
713 videocontext->display_width = width * pixel_width / pixel_height;
714 videocontext->display_height = height;
715 } else if (pixel_width < pixel_height) {
716 videocontext->display_width = width;
717 videocontext->display_height = height * pixel_height / pixel_width;
719 videocontext->display_width = 0;
720 videocontext->display_height = 0;
723 videocontext->display_width = 0;
724 videocontext->display_height = 0;
729 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
730 videocontext->fourcc = 0;
732 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
733 * data and other settings
737 /* extract codec_data, may turn out needed */
738 value = gst_structure_get_value (structure, "codec_data");
740 codec_buf = gst_value_get_buffer (value);
743 if (!strcmp (mimetype, "video/x-raw-yuv")) {
744 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
745 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
746 } else if (!strcmp (mimetype, "image/jpeg")) {
747 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
748 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
749 ||!strcmp (mimetype, "video/x-huffyuv")
750 || !strcmp (mimetype, "video/x-divx")
751 || !strcmp (mimetype, "video/x-dv")
752 || !strcmp (mimetype, "video/x-h263")
753 || !strcmp (mimetype, "video/x-msmpeg")
754 || !strcmp (mimetype, "video/x-wmv")) {
755 gst_riff_strf_vids *bih;
756 gint size = sizeof (gst_riff_strf_vids);
759 if (!strcmp (mimetype, "video/x-xvid"))
760 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
761 else if (!strcmp (mimetype, "video/x-huffyuv"))
762 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
763 else if (!strcmp (mimetype, "video/x-dv"))
764 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
765 else if (!strcmp (mimetype, "video/x-h263"))
766 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
767 else if (!strcmp (mimetype, "video/x-divx")) {
770 gst_structure_get_int (structure, "divxversion", &divxversion);
771 switch (divxversion) {
773 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
776 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
779 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
782 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
785 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
786 switch (msmpegversion) {
788 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
791 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
797 } else if (!strcmp (mimetype, "video/x-wmv")) {
800 if (gst_structure_get_fourcc (structure, "format", &format)) {
802 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
803 if (wmvversion == 2) {
804 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
805 } else if (wmvversion == 1) {
806 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
807 } else if (wmvversion == 3) {
808 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
816 bih = g_new0 (gst_riff_strf_vids, 1);
817 GST_WRITE_UINT32_LE (&bih->size, size);
818 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
819 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
820 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
821 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
822 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
823 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
824 videocontext->pixel_height * 3);
826 /* process codec private/initialization data, if any */
828 size += GST_BUFFER_SIZE (codec_buf);
829 bih = g_realloc (bih, size);
830 GST_WRITE_UINT32_LE (&bih->size, size);
831 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
832 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
835 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
836 context->codec_priv = (gpointer) bih;
837 context->codec_priv_size = size;
838 } else if (!strcmp (mimetype, "video/x-h264")) {
839 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
841 if (context->codec_priv != NULL) {
842 g_free (context->codec_priv);
843 context->codec_priv = NULL;
844 context->codec_priv_size = 0;
847 /* Create avcC header */
848 if (codec_buf != NULL) {
849 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
850 context->codec_priv = g_malloc0 (context->codec_priv_size);
851 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
852 context->codec_priv_size);
854 } else if (!strcmp (mimetype, "video/x-theora")) {
855 const GValue *streamheader;
857 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
859 if (context->codec_priv != NULL) {
860 g_free (context->codec_priv);
861 context->codec_priv = NULL;
862 context->codec_priv_size = 0;
865 streamheader = gst_structure_get_value (structure, "streamheader");
866 if (!theora_streamheader_to_codecdata (streamheader, context)) {
867 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
868 ("theora stream headers missing or malformed"));
871 } else if (!strcmp (mimetype, "video/x-dirac")) {
872 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
873 } else if (!strcmp (mimetype, "video/x-vp8")) {
874 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
875 } else if (!strcmp (mimetype, "video/mpeg")) {
878 gst_structure_get_int (structure, "mpegversion", &mpegversion);
879 switch (mpegversion) {
881 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
884 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
887 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
893 /* global headers may be in codec data */
894 if (codec_buf != NULL) {
895 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
896 context->codec_priv = g_malloc0 (context->codec_priv_size);
897 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
898 context->codec_priv_size);
900 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
902 /* can only make it here if preceding case verified it was version 3 */
903 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
904 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
906 const GValue *mdpr_data;
908 gst_structure_get_int (structure, "rmversion", &rmversion);
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
914 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
917 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
920 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
926 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
927 if (mdpr_data != NULL) {
928 guint8 *priv_data = NULL;
929 guint priv_data_size = 0;
931 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
933 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
934 priv_data = g_malloc0 (priv_data_size);
936 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
938 context->codec_priv = priv_data;
939 context->codec_priv_size = priv_data_size;
948 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
949 GST_PAD_NAME (pad), caps);
954 /* N > 0 to expect a particular number of headers, negative if the
955 number of headers is variable */
957 xiphN_streamheader_to_codecdata (const GValue * streamheader,
958 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
960 GstBuffer **buf = NULL;
963 guint bufi, i, offset, priv_data_size;
965 if (streamheader == NULL)
966 goto no_stream_headers;
968 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
971 bufarr = g_value_peek_pointer (streamheader);
972 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
974 if (N > 0 && bufarr->len != N)
977 context->xiph_headers_to_skip = bufarr->len;
979 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
980 for (i = 0; i < bufarr->len; i++) {
981 GValue *bufval = &g_array_index (bufarr, GValue, i);
983 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
985 goto wrong_content_type;
988 buf[i] = g_value_peek_pointer (bufval);
992 if (bufarr->len > 0) {
993 for (i = 0; i < bufarr->len - 1; i++) {
994 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
998 for (i = 0; i < bufarr->len; ++i) {
999 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1002 priv_data = g_malloc0 (priv_data_size);
1004 priv_data[0] = bufarr->len - 1;
1007 if (bufarr->len > 0) {
1008 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1009 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1010 priv_data[offset++] = 0xff;
1012 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1016 for (i = 0; i < bufarr->len; ++i) {
1017 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1018 GST_BUFFER_SIZE (buf[i]));
1019 offset += GST_BUFFER_SIZE (buf[i]);
1022 context->codec_priv = priv_data;
1023 context->codec_priv_size = priv_data_size;
1026 *p_buf0 = gst_buffer_ref (buf[0]);
1035 GST_WARNING ("required streamheaders missing in sink caps!");
1040 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1041 G_VALUE_TYPE_NAME (streamheader));
1046 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1051 GST_WARNING ("streamheaders array does not contain GstBuffers");
1057 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1058 GstMatroskaTrackContext * context)
1060 GstBuffer *buf0 = NULL;
1062 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1065 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1066 GST_WARNING ("First vorbis header too small, ignoring");
1068 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1069 GstMatroskaTrackAudioContext *audiocontext;
1072 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1073 audiocontext = (GstMatroskaTrackAudioContext *) context;
1074 audiocontext->channels = GST_READ_UINT8 (hdr);
1075 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1080 gst_buffer_unref (buf0);
1086 theora_streamheader_to_codecdata (const GValue * streamheader,
1087 GstMatroskaTrackContext * context)
1089 GstBuffer *buf0 = NULL;
1091 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1094 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1095 GST_WARNING ("First theora header too small, ignoring");
1096 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1097 GST_WARNING ("First header not a theora identification header, ignoring");
1099 GstMatroskaTrackVideoContext *videocontext;
1100 guint fps_num, fps_denom, par_num, par_denom;
1103 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1105 videocontext = (GstMatroskaTrackVideoContext *) context;
1106 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1107 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1108 hdr += 3 + 3 + 1 + 1;
1109 fps_num = GST_READ_UINT32_BE (hdr);
1110 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1111 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1112 fps_denom, fps_num);
1114 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1115 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1116 if (par_num > 0 && par_num > 0) {
1117 if (par_num > par_denom) {
1118 videocontext->display_width =
1119 videocontext->pixel_width * par_num / par_denom;
1120 videocontext->display_height = videocontext->pixel_height;
1121 } else if (par_num < par_denom) {
1122 videocontext->display_width = videocontext->pixel_width;
1123 videocontext->display_height =
1124 videocontext->pixel_height * par_denom / par_num;
1126 videocontext->display_width = 0;
1127 videocontext->display_height = 0;
1130 videocontext->display_width = 0;
1131 videocontext->display_height = 0;
1137 gst_buffer_unref (buf0);
1143 kate_streamheader_to_codecdata (const GValue * streamheader,
1144 GstMatroskaTrackContext * context)
1146 GstBuffer *buf0 = NULL;
1148 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1151 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1152 GST_WARNING ("First kate header too small, ignoring");
1153 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1154 GST_WARNING ("First header not a kate identification header, ignoring");
1158 gst_buffer_unref (buf0);
1164 flac_streamheader_to_codecdata (const GValue * streamheader,
1165 GstMatroskaTrackContext * context)
1172 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1173 GST_WARNING ("No or invalid streamheader field in the caps");
1177 bufarr = g_value_peek_pointer (streamheader);
1178 if (bufarr->len < 2) {
1179 GST_WARNING ("Too few headers in streamheader field");
1183 context->xiph_headers_to_skip = bufarr->len + 1;
1185 bufval = &g_array_index (bufarr, GValue, 0);
1186 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1187 GST_WARNING ("streamheaders array does not contain GstBuffers");
1191 buffer = g_value_peek_pointer (bufval);
1193 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1194 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1195 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1196 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1197 GST_WARNING ("Invalid streamheader for FLAC");
1201 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1202 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1203 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1204 GST_BUFFER_SIZE (buffer) - 9);
1206 for (i = 1; i < bufarr->len; i++) {
1207 bufval = &g_array_index (bufarr, GValue, i);
1209 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1210 g_free (context->codec_priv);
1211 context->codec_priv = NULL;
1212 context->codec_priv_size = 0;
1213 GST_WARNING ("streamheaders array does not contain GstBuffers");
1217 buffer = g_value_peek_pointer (bufval);
1219 context->codec_priv =
1220 g_realloc (context->codec_priv,
1221 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1222 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1223 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1224 context->codec_priv_size =
1225 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1232 speex_streamheader_to_codecdata (const GValue * streamheader,
1233 GstMatroskaTrackContext * context)
1239 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1240 GST_WARNING ("No or invalid streamheader field in the caps");
1244 bufarr = g_value_peek_pointer (streamheader);
1245 if (bufarr->len != 2) {
1246 GST_WARNING ("Too few headers in streamheader field");
1250 context->xiph_headers_to_skip = bufarr->len + 1;
1252 bufval = &g_array_index (bufarr, GValue, 0);
1253 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1254 GST_WARNING ("streamheaders array does not contain GstBuffers");
1258 buffer = g_value_peek_pointer (bufval);
1260 if (GST_BUFFER_SIZE (buffer) < 80
1261 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1262 GST_WARNING ("Invalid streamheader for Speex");
1266 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1267 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1268 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1269 GST_BUFFER_SIZE (buffer));
1271 bufval = &g_array_index (bufarr, GValue, 1);
1273 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1274 g_free (context->codec_priv);
1275 context->codec_priv = NULL;
1276 context->codec_priv_size = 0;
1277 GST_WARNING ("streamheaders array does not contain GstBuffers");
1281 buffer = g_value_peek_pointer (bufval);
1283 context->codec_priv =
1284 g_realloc (context->codec_priv,
1285 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1286 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1287 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1288 context->codec_priv_size =
1289 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1294 static const gchar *
1295 aac_codec_data_to_codec_id (const GstBuffer * buf)
1297 const gchar *result;
1300 /* default to MAIN */
1303 if (GST_BUFFER_SIZE (buf) >= 2) {
1304 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1322 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1331 * gst_matroska_mux_audio_pad_setcaps:
1332 * @pad: Pad which got the caps.
1335 * Setcaps function for audio sink pad.
1337 * Returns: #TRUE on success.
1340 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1342 GstMatroskaTrackContext *context = NULL;
1343 GstMatroskaTrackAudioContext *audiocontext;
1344 GstMatroskaMux *mux;
1345 GstMatroskaPad *collect_pad;
1346 const gchar *mimetype;
1347 gint samplerate = 0, channels = 0;
1348 GstStructure *structure;
1349 const GValue *codec_data = NULL;
1350 const GstBuffer *buf = NULL;
1351 const gchar *stream_format = NULL;
1353 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1356 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1357 g_assert (collect_pad);
1358 context = collect_pad->track;
1360 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1361 audiocontext = (GstMatroskaTrackAudioContext *) context;
1363 structure = gst_caps_get_structure (caps, 0);
1364 mimetype = gst_structure_get_name (structure);
1367 gst_structure_get_int (structure, "rate", &samplerate);
1368 gst_structure_get_int (structure, "channels", &channels);
1370 audiocontext->samplerate = samplerate;
1371 audiocontext->channels = channels;
1372 audiocontext->bitdepth = 0;
1373 context->default_duration = 0;
1375 codec_data = gst_structure_get_value (structure, "codec_data");
1377 buf = gst_value_get_buffer (codec_data);
1379 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1380 * data and other settings
1384 if (!strcmp (mimetype, "audio/mpeg")) {
1385 gint mpegversion = 0;
1387 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1388 switch (mpegversion) {
1394 gst_structure_get_int (structure, "layer", &layer);
1396 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1397 GST_WARNING_OBJECT (mux,
1398 "Unable to determine MPEG audio version, assuming 1");
1404 else if (layer == 2)
1406 else if (version == 2)
1411 context->default_duration =
1412 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1416 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1419 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1422 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1431 stream_format = gst_structure_get_string (structure, "stream-format");
1432 /* check this is raw aac */
1433 if (stream_format) {
1434 if (strcmp (stream_format, "raw") != 0) {
1435 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1439 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1444 if (mpegversion == 2)
1446 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1447 aac_codec_data_to_codec_id (buf));
1448 else if (mpegversion == 4)
1450 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1451 aac_codec_data_to_codec_id (buf));
1453 g_assert_not_reached ();
1455 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1462 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1464 gint endianness = G_LITTLE_ENDIAN;
1465 gboolean signedness = TRUE;
1467 if (!gst_structure_get_int (structure, "width", &width) ||
1468 !gst_structure_get_int (structure, "depth", &depth) ||
1469 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1470 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1475 !gst_structure_get_int (structure, "endianness", &endianness)) {
1476 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1480 if (width != depth) {
1481 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1485 /* FIXME: where is this spec'ed out? (tpm) */
1486 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1487 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1491 audiocontext->bitdepth = depth;
1492 if (endianness == G_BIG_ENDIAN)
1493 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1495 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1497 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1500 if (!gst_structure_get_int (structure, "width", &width)) {
1501 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1505 audiocontext->bitdepth = width;
1506 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1508 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1509 const GValue *streamheader;
1511 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1513 if (context->codec_priv != NULL) {
1514 g_free (context->codec_priv);
1515 context->codec_priv = NULL;
1516 context->codec_priv_size = 0;
1519 streamheader = gst_structure_get_value (structure, "streamheader");
1520 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1521 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1522 ("vorbis stream headers missing or malformed"));
1525 } else if (!strcmp (mimetype, "audio/x-flac")) {
1526 const GValue *streamheader;
1528 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1529 if (context->codec_priv != NULL) {
1530 g_free (context->codec_priv);
1531 context->codec_priv = NULL;
1532 context->codec_priv_size = 0;
1535 streamheader = gst_structure_get_value (structure, "streamheader");
1536 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1537 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1538 ("flac stream headers missing or malformed"));
1541 } else if (!strcmp (mimetype, "audio/x-speex")) {
1542 const GValue *streamheader;
1544 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1545 if (context->codec_priv != NULL) {
1546 g_free (context->codec_priv);
1547 context->codec_priv = NULL;
1548 context->codec_priv_size = 0;
1551 streamheader = gst_structure_get_value (structure, "streamheader");
1552 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1553 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1554 ("speex stream headers missing or malformed"));
1557 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1558 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1559 } else if (!strcmp (mimetype, "audio/x-tta")) {
1562 /* TTA frame duration */
1563 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1565 gst_structure_get_int (structure, "width", &width);
1566 audiocontext->bitdepth = width;
1567 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1569 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1571 const GValue *mdpr_data;
1573 gst_structure_get_int (structure, "raversion", &raversion);
1574 switch (raversion) {
1576 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1579 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1582 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1588 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1589 if (mdpr_data != NULL) {
1590 guint8 *priv_data = NULL;
1591 guint priv_data_size = 0;
1593 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1595 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1596 priv_data = g_malloc0 (priv_data_size);
1598 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1600 context->codec_priv = priv_data;
1601 context->codec_priv_size = priv_data_size;
1604 } else if (!strcmp (mimetype, "audio/x-wma")) {
1606 guint codec_priv_size;
1613 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1614 || !gst_structure_get_int (structure, "block_align", &block_align)
1615 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1616 || samplerate == 0 || channels == 0) {
1617 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1618 "channels/rate on WMA caps");
1622 switch (wmaversion) {
1624 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1627 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1630 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1633 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1637 if (gst_structure_get_int (structure, "depth", &depth))
1638 audiocontext->bitdepth = depth;
1640 codec_priv_size = WAVEFORMATEX_SIZE;
1642 codec_priv_size += GST_BUFFER_SIZE (buf);
1644 /* serialize waveformatex structure */
1645 codec_priv = g_malloc0 (codec_priv_size);
1646 GST_WRITE_UINT16_LE (codec_priv, format);
1647 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1648 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1649 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1650 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1651 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1653 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1655 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1657 /* process codec private/initialization data, if any */
1659 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1660 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1663 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1664 context->codec_priv = (gpointer) codec_priv;
1665 context->codec_priv_size = codec_priv_size;
1673 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1674 GST_PAD_NAME (pad), caps);
1681 * gst_matroska_mux_subtitle_pad_setcaps:
1682 * @pad: Pad which got the caps.
1685 * Setcaps function for subtitle sink pad.
1687 * Returns: #TRUE on success.
1690 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1693 * Consider this as boilerplate code for now. There is
1694 * no single subtitle creation element in GStreamer,
1695 * neither do I know how subtitling works at all. */
1697 /* There is now (at least) one such alement (kateenc), and I'm going
1698 to handle it here and claim it works when it can be piped back
1699 through GStreamer and VLC */
1701 GstMatroskaTrackContext *context = NULL;
1702 GstMatroskaTrackSubtitleContext *scontext;
1703 GstMatroskaMux *mux;
1704 GstMatroskaPad *collect_pad;
1705 const gchar *mimetype;
1706 GstStructure *structure;
1708 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1711 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1712 g_assert (collect_pad);
1713 context = collect_pad->track;
1715 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1716 scontext = (GstMatroskaTrackSubtitleContext *) context;
1718 structure = gst_caps_get_structure (caps, 0);
1719 mimetype = gst_structure_get_name (structure);
1722 scontext->check_utf8 = 1;
1723 scontext->invalid_utf8 = 0;
1724 context->default_duration = 0;
1726 /* TODO: - other format than Kate */
1728 if (!strcmp (mimetype, "subtitle/x-kate")) {
1729 const GValue *streamheader;
1731 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1733 if (context->codec_priv != NULL) {
1734 g_free (context->codec_priv);
1735 context->codec_priv = NULL;
1736 context->codec_priv_size = 0;
1739 streamheader = gst_structure_get_value (structure, "streamheader");
1740 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1741 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1742 ("kate stream headers missing or malformed"));
1753 * gst_matroska_mux_request_new_pad:
1754 * @element: #GstMatroskaMux.
1755 * @templ: #GstPadTemplate.
1756 * @pad_name: New pad name.
1758 * Request pad function for sink templates.
1760 * Returns: New #GstPad.
1763 gst_matroska_mux_request_new_pad (GstElement * element,
1764 GstPadTemplate * templ, const gchar * pad_name)
1766 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1767 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1768 GstMatroskaPad *collect_pad;
1769 GstPad *newpad = NULL;
1771 GstPadSetCapsFunction setcapsfunc = NULL;
1772 GstMatroskaTrackContext *context = NULL;
1774 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1775 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1776 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1777 context = (GstMatroskaTrackContext *)
1778 g_new0 (GstMatroskaTrackAudioContext, 1);
1779 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1780 context->name = g_strdup ("Audio");
1781 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1782 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1783 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1784 context = (GstMatroskaTrackContext *)
1785 g_new0 (GstMatroskaTrackVideoContext, 1);
1786 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1787 context->name = g_strdup ("Video");
1788 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1789 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1790 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1791 context = (GstMatroskaTrackContext *)
1792 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1793 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1794 context->name = g_strdup ("Subtitle");
1796 GST_WARNING_OBJECT (mux, "This is not our template!");
1800 newpad = gst_pad_new_from_template (templ, name);
1802 collect_pad = (GstMatroskaPad *)
1803 gst_collect_pads_add_pad_full (mux->collect, newpad,
1804 sizeof (GstMatroskaPad),
1805 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1807 collect_pad->track = context;
1808 gst_matroska_pad_reset (collect_pad, FALSE);
1810 /* FIXME: hacked way to override/extend the event function of
1811 * GstCollectPads; because it sets its own event function giving the
1812 * element no access to events.
1813 * TODO GstCollectPads should really give its 'users' a clean chance to
1814 * properly handle events that are not meant for collectpads itself.
1815 * Perhaps a callback or so, though rejected (?) in #340060.
1816 * This would allow (clean) transcoding of info from demuxer/streams
1817 * to another muxer */
1818 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1819 gst_pad_set_event_function (newpad,
1820 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1822 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1823 gst_pad_set_active (newpad, TRUE);
1824 gst_element_add_pad (element, newpad);
1831 * gst_matroska_mux_release_pad:
1832 * @element: #GstMatroskaMux.
1833 * @pad: Pad to release.
1835 * Release a previously requested pad.
1838 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1840 GstMatroskaMux *mux;
1843 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1845 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1846 GstCollectData *cdata = (GstCollectData *) walk->data;
1847 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1849 if (cdata->pad == pad) {
1850 GstClockTime min_dur; /* observed minimum duration */
1852 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1853 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1854 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1855 if (collect_pad->duration < min_dur)
1856 collect_pad->duration = min_dur;
1859 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1860 mux->duration < collect_pad->duration)
1861 mux->duration = collect_pad->duration;
1867 gst_collect_pads_remove_pad (mux->collect, pad);
1868 if (gst_element_remove_pad (element, pad))
1874 * gst_matroska_mux_track_header:
1875 * @mux: #GstMatroskaMux
1876 * @context: Tack context.
1878 * Write a track header.
1881 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1882 GstMatroskaTrackContext * context)
1884 GstEbmlWrite *ebml = mux->ebml_write;
1887 /* TODO: check if everything necessary is written and check default values */
1889 /* track type goes before the type-specific stuff */
1890 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1891 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1893 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1894 gst_matroska_mux_create_uid ());
1895 if (context->default_duration) {
1896 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1897 context->default_duration);
1899 if (context->language) {
1900 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1904 /* type-specific stuff */
1905 switch (context->type) {
1906 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1907 GstMatroskaTrackVideoContext *videocontext =
1908 (GstMatroskaTrackVideoContext *) context;
1910 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1911 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1912 videocontext->pixel_width);
1913 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1914 videocontext->pixel_height);
1915 if (videocontext->display_width && videocontext->display_height) {
1916 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1917 videocontext->display_width);
1918 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1919 videocontext->display_height);
1921 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1923 if (videocontext->fourcc) {
1924 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1926 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1927 (gpointer) & fcc_le, 4);
1929 gst_ebml_write_master_finish (ebml, master);
1934 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1935 GstMatroskaTrackAudioContext *audiocontext =
1936 (GstMatroskaTrackAudioContext *) context;
1938 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1939 if (audiocontext->samplerate != 8000)
1940 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1941 audiocontext->samplerate);
1942 if (audiocontext->channels != 1)
1943 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1944 audiocontext->channels);
1945 if (audiocontext->bitdepth) {
1946 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1947 audiocontext->bitdepth);
1949 gst_ebml_write_master_finish (ebml, master);
1955 /* doesn't need type-specific data */
1959 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1960 if (context->codec_priv)
1961 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1962 context->codec_priv, context->codec_priv_size);
1963 /* FIXME: until we have a nice way of getting the codecname
1964 * out of the caps, I'm not going to enable this. Too much
1965 * (useless, double, boring) work... */
1966 /* TODO: Use value from tags if any */
1967 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1968 context->codec_name); */
1969 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1974 * gst_matroska_mux_start:
1975 * @mux: #GstMatroskaMux
1977 * Start a new matroska file (write headers etc...)
1980 gst_matroska_mux_start (GstMatroskaMux * mux)
1982 GstEbmlWrite *ebml = mux->ebml_write;
1983 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1984 GST_MATROSKA_ID_TRACKS,
1985 GST_MATROSKA_ID_CUES,
1986 GST_MATROSKA_ID_TAGS,
1989 guint64 master, child;
1993 GstClockTime duration = 0;
1994 guint32 segment_uid[4];
1995 GTimeVal time = { 0, 0 };
1997 /* we start with a EBML header */
1998 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
2000 /* start a segment */
2002 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2003 mux->segment_master = ebml->pos;
2005 /* the rest of the header is cached */
2006 gst_ebml_write_set_cache (ebml, 0x1000);
2008 /* seekhead (table of contents) - we set the positions later */
2009 mux->seekhead_pos = ebml->pos;
2010 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2011 for (i = 0; seekhead_id[i] != 0; i++) {
2012 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2013 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2014 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2015 gst_ebml_write_master_finish (ebml, child);
2017 gst_ebml_write_master_finish (ebml, master);
2020 mux->info_pos = ebml->pos;
2021 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2022 for (i = 0; i < 4; i++) {
2023 segment_uid[i] = g_random_int ();
2025 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2026 (guint8 *) segment_uid, 16);
2027 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2028 mux->duration_pos = ebml->pos;
2030 for (collected = mux->collect->data; collected;
2031 collected = g_slist_next (collected)) {
2032 GstMatroskaPad *collect_pad;
2033 GstFormat format = GST_FORMAT_TIME;
2035 gint64 trackduration;
2037 collect_pad = (GstMatroskaPad *) collected->data;
2038 thepad = collect_pad->collect.pad;
2040 /* Query the total length of the track. */
2041 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2042 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2043 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2044 GST_TIME_ARGS (trackduration));
2045 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2046 duration = (GstClockTime) trackduration;
2050 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2051 gst_guint64_to_gdouble (duration) /
2052 gst_guint64_to_gdouble (mux->time_scale));
2054 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2055 "GStreamer plugin version " PACKAGE_VERSION);
2056 if (mux->writing_app && mux->writing_app[0]) {
2057 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2059 g_get_current_time (&time);
2060 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2061 gst_ebml_write_master_finish (ebml, master);
2064 mux->tracks_pos = ebml->pos;
2065 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2067 for (collected = mux->collect->data; collected;
2068 collected = g_slist_next (collected)) {
2069 GstMatroskaPad *collect_pad;
2072 collect_pad = (GstMatroskaPad *) collected->data;
2073 thepad = collect_pad->collect.pad;
2075 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2076 collect_pad->track->codec_id != 0) {
2077 collect_pad->track->num = tracknum++;
2078 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2079 gst_matroska_mux_track_header (mux, collect_pad->track);
2080 gst_ebml_write_master_finish (ebml, child);
2083 gst_ebml_write_master_finish (ebml, master);
2085 /* lastly, flush the cache */
2086 gst_ebml_write_flush_cache (ebml);
2090 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2093 /* TODO: more sensible tag mappings */
2096 const gchar *matroska_tagname;
2097 const gchar *gstreamer_tagname;
2101 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2102 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2103 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2104 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2105 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2106 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2107 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2108 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2109 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2110 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2111 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2112 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2113 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2114 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2115 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2117 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2119 guint64 simpletag_master;
2121 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2122 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2123 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2125 if (strcmp (tagname_gst, tag) == 0) {
2126 GValue src = { 0, };
2129 if (!gst_tag_list_copy_value (&src, list, tag))
2131 if ((dest = gst_value_serialize (&src))) {
2133 simpletag_master = gst_ebml_write_master_start (ebml,
2134 GST_MATROSKA_ID_SIMPLETAG);
2135 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2136 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2137 gst_ebml_write_master_finish (ebml, simpletag_master);
2140 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2142 g_value_unset (&src);
2150 * gst_matroska_mux_finish:
2151 * @mux: #GstMatroskaMux
2153 * Finish a new matroska file (write index etc...)
2156 gst_matroska_mux_finish (GstMatroskaMux * mux)
2158 GstEbmlWrite *ebml = mux->ebml_write;
2160 guint64 duration = 0;
2162 const GstTagList *tags;
2164 /* finish last cluster */
2166 gst_ebml_write_master_finish (ebml, mux->cluster);
2170 if (mux->index != NULL) {
2172 guint64 master, pointentry_master, trackpos_master;
2174 mux->cues_pos = ebml->pos;
2175 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2176 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2178 for (n = 0; n < mux->num_indexes; n++) {
2179 GstMatroskaIndex *idx = &mux->index[n];
2181 pointentry_master = gst_ebml_write_master_start (ebml,
2182 GST_MATROSKA_ID_POINTENTRY);
2183 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2184 idx->time / mux->time_scale);
2185 trackpos_master = gst_ebml_write_master_start (ebml,
2186 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2187 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2188 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2189 idx->pos - mux->segment_master);
2190 gst_ebml_write_master_finish (ebml, trackpos_master);
2191 gst_ebml_write_master_finish (ebml, pointentry_master);
2194 gst_ebml_write_master_finish (ebml, master);
2195 gst_ebml_write_flush_cache (ebml);
2199 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2201 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2202 guint64 master_tags, master_tag;
2204 GST_DEBUG ("Writing tags");
2206 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2207 mux->tags_pos = ebml->pos;
2208 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2209 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2210 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2211 gst_ebml_write_master_finish (ebml, master_tag);
2212 gst_ebml_write_master_finish (ebml, master_tags);
2215 /* update seekhead. We know that:
2216 * - a seekhead contains 4 entries.
2217 * - order of entries is as above.
2218 * - a seekhead has a 4-byte header + 8-byte length
2219 * - each entry is 2-byte master, 2-byte ID pointer,
2220 * 2-byte length pointer, all 8/1-byte length, 4-
2221 * byte ID and 8-byte length pointer, where the
2222 * length pointer starts at 20.
2223 * - all entries are local to the segment (so pos - segment_master).
2224 * - so each entry is at 12 + 20 + num * 28. */
2225 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2226 mux->info_pos - mux->segment_master);
2227 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2228 mux->tracks_pos - mux->segment_master);
2229 if (mux->index != NULL) {
2230 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2231 mux->cues_pos - mux->segment_master);
2234 guint64 my_pos = ebml->pos;
2236 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2237 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2238 gst_ebml_write_seek (ebml, my_pos);
2241 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2242 mux->tags_pos - mux->segment_master);
2245 guint64 my_pos = ebml->pos;
2247 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2248 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2249 gst_ebml_write_seek (ebml, my_pos);
2252 /* update duration */
2253 /* first get the overall duration */
2254 /* a released track may have left a duration in here */
2255 duration = mux->duration;
2256 for (collected = mux->collect->data; collected;
2257 collected = g_slist_next (collected)) {
2258 GstMatroskaPad *collect_pad;
2259 GstClockTime min_duration; /* observed minimum duration */
2261 collect_pad = (GstMatroskaPad *) collected->data;
2263 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2264 " end ts %" GST_TIME_FORMAT, collect_pad,
2265 GST_TIME_ARGS (collect_pad->start_ts),
2266 GST_TIME_ARGS (collect_pad->end_ts));
2268 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2269 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2271 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2272 if (collect_pad->duration < min_duration)
2273 collect_pad->duration = min_duration;
2274 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2275 GST_TIME_ARGS (collect_pad->duration));
2278 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2279 duration < collect_pad->duration)
2280 duration = collect_pad->duration;
2282 if (duration != 0) {
2283 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2284 GST_TIME_ARGS (duration));
2285 pos = mux->ebml_write->pos;
2286 gst_ebml_write_seek (ebml, mux->duration_pos);
2287 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2288 gst_guint64_to_gdouble (duration) /
2289 gst_guint64_to_gdouble (mux->time_scale));
2290 gst_ebml_write_seek (ebml, pos);
2293 guint64 my_pos = ebml->pos;
2295 gst_ebml_write_seek (ebml, mux->duration_pos);
2296 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2297 gst_ebml_write_seek (ebml, my_pos);
2300 /* finish segment - this also writes element length */
2301 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2306 * gst_matroska_mux_best_pad:
2307 * @mux: #GstMatroskaMux
2308 * @popped: True if at least one buffer was popped from #GstCollectPads
2310 * Find a pad with the oldest data
2311 * (data from this pad should be written first).
2313 * Returns: Selected pad.
2315 static GstMatroskaPad *
2316 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2319 GstMatroskaPad *best = NULL;
2322 for (collected = mux->collect->data; collected;
2323 collected = g_slist_next (collected)) {
2324 GstMatroskaPad *collect_pad;
2326 collect_pad = (GstMatroskaPad *) collected->data;
2327 /* fetch a new buffer if needed */
2328 if (collect_pad->buffer == NULL) {
2329 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2330 (GstCollectData *) collect_pad);
2332 if (collect_pad->buffer != NULL)
2336 /* if we have a buffer check if it is better then the current best one */
2337 if (collect_pad->buffer != NULL) {
2338 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2339 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2340 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2341 GST_BUFFER_TIMESTAMP (best->buffer))) {
2351 * gst_matroska_mux_buffer_header:
2352 * @track: Track context.
2353 * @relative_timestamp: relative timestamp of the buffer
2354 * @flags: Buffer flags.
2356 * Create a buffer containing buffer header.
2358 * Returns: New buffer.
2361 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2362 gint16 relative_timestamp, int flags)
2366 hdr = gst_buffer_new_and_alloc (4);
2367 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2368 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2369 /* time relative to clustertime */
2370 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2373 GST_BUFFER_DATA (hdr)[3] = flags;
2378 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2379 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2380 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2383 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2384 GstMatroskaPad * collect_pad, GstBuffer * buf)
2386 GstMatroskaTrackVideoContext *ctx =
2387 (GstMatroskaTrackVideoContext *) collect_pad->track;
2388 const guint8 *data = GST_BUFFER_DATA (buf);
2389 guint size = GST_BUFFER_SIZE (buf);
2391 guint32 next_parse_offset;
2392 GstBuffer *ret = NULL;
2393 gboolean is_muxing_unit = FALSE;
2395 if (GST_BUFFER_SIZE (buf) < 13) {
2396 gst_buffer_unref (buf);
2400 /* Check if this buffer contains a picture or end-of-sequence packet */
2401 while (size >= 13) {
2402 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2403 gst_buffer_unref (buf);
2407 parse_code = GST_READ_UINT8 (data + 4);
2408 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2409 if (ctx->dirac_unit) {
2410 gst_buffer_unref (ctx->dirac_unit);
2411 ctx->dirac_unit = NULL;
2413 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2414 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2415 is_muxing_unit = TRUE;
2419 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2421 if (G_UNLIKELY (next_parse_offset == 0))
2424 data += next_parse_offset;
2425 size -= next_parse_offset;
2428 if (ctx->dirac_unit)
2429 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2431 ctx->dirac_unit = gst_buffer_ref (buf);
2433 if (is_muxing_unit) {
2434 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2435 ctx->dirac_unit = NULL;
2436 gst_buffer_copy_metadata (ret, buf,
2437 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2438 GST_BUFFER_COPY_CAPS);
2439 gst_buffer_unref (buf);
2441 gst_buffer_unref (buf);
2449 * gst_matroska_mux_write_data:
2450 * @mux: #GstMatroskaMux
2451 * @collect_pad: #GstMatroskaPad with the data
2453 * Write collected data (called from gst_matroska_mux_collected).
2455 * Returns: Result of the gst_pad_push issued to write the data.
2457 static GstFlowReturn
2458 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2460 GstEbmlWrite *ebml = mux->ebml_write;
2461 GstBuffer *buf, *hdr;
2463 gboolean write_duration;
2464 gint16 relative_timestamp;
2465 gint64 relative_timestamp64;
2466 guint64 block_duration;
2467 gboolean is_video_keyframe = FALSE;
2470 buf = collect_pad->buffer;
2471 collect_pad->buffer = NULL;
2473 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2474 if (collect_pad->track->xiph_headers_to_skip > 0) {
2475 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2476 gst_buffer_unref (buf);
2477 --collect_pad->track->xiph_headers_to_skip;
2481 /* for dirac we have to queue up everything up to a picture unit */
2482 if (collect_pad->track->codec_id != NULL &&
2483 strcmp (collect_pad->track->codec_id,
2484 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2485 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2490 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2491 * this would wreak havoc with time stored in matroska file */
2492 /* TODO: maybe calculate a timestamp by using the previous timestamp
2493 * and default duration */
2494 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2495 GST_WARNING_OBJECT (collect_pad->collect.pad,
2496 "Invalid buffer timestamp; dropping buffer");
2497 gst_buffer_unref (buf);
2501 /* set the timestamp for outgoing buffers */
2502 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2504 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2505 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2506 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2507 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2508 is_video_keyframe = TRUE;
2512 /* start a new cluster every two seconds or at keyframe */
2513 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2514 || is_video_keyframe) {
2516 gst_ebml_write_master_finish (ebml, mux->cluster);
2517 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2518 mux->cluster_pos = ebml->pos;
2520 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2521 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2522 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2523 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2524 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2525 mux->prev_cluster_size);
2530 mux->cluster_pos = ebml->pos;
2531 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2532 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2533 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2534 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2537 /* update duration of this track */
2538 if (GST_BUFFER_DURATION_IS_VALID (buf))
2539 collect_pad->duration += GST_BUFFER_DURATION (buf);
2541 /* We currently write index entries for all video tracks or for the audio
2542 * track in a single-track audio file. This could be improved by keeping the
2543 * index only for the *first* video track. */
2545 /* TODO: index is useful for every track, should contain the number of
2546 * the block in the cluster which contains the timestamp, should also work
2547 * for files with multiple audio tracks.
2549 if (is_video_keyframe ||
2550 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2551 (mux->num_streams == 1))) {
2554 if (mux->min_index_interval != 0) {
2555 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2556 if (mux->index[last_idx].track == collect_pad->track->num)
2561 if (last_idx < 0 || mux->min_index_interval == 0 ||
2562 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2563 >= mux->min_index_interval)) {
2564 GstMatroskaIndex *idx;
2566 if (mux->num_indexes % 32 == 0) {
2567 mux->index = g_renew (GstMatroskaIndex, mux->index,
2568 mux->num_indexes + 32);
2570 idx = &mux->index[mux->num_indexes++];
2572 idx->pos = mux->cluster_pos;
2573 idx->time = GST_BUFFER_TIMESTAMP (buf);
2574 idx->track = collect_pad->track->num;
2578 /* Check if the duration differs from the default duration. */
2579 write_duration = FALSE;
2580 block_duration = GST_BUFFER_DURATION (buf);
2581 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2582 if (block_duration != collect_pad->track->default_duration) {
2583 write_duration = TRUE;
2587 /* write the block, for matroska v2 use SimpleBlock if possible
2588 * one slice (*breath*).
2589 * FIXME: Need to do correct lacing! */
2590 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2591 if (relative_timestamp64 >= 0) {
2592 /* round the timestamp */
2593 relative_timestamp64 += mux->time_scale / 2;
2595 /* round the timestamp */
2596 relative_timestamp64 -= mux->time_scale / 2;
2598 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2599 if (mux->matroska_version > 1 && !write_duration) {
2601 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2604 gst_matroska_mux_create_buffer_header (collect_pad->track,
2605 relative_timestamp, flags);
2606 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2607 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2608 gst_ebml_write_buffer (ebml, hdr);
2609 gst_ebml_write_buffer (ebml, buf);
2611 return gst_ebml_last_write_result (ebml);
2613 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2615 gst_matroska_mux_create_buffer_header (collect_pad->track,
2616 relative_timestamp, 0);
2617 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2618 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2619 gst_ebml_write_buffer (ebml, hdr);
2620 gst_ebml_write_buffer (ebml, buf);
2621 if (write_duration) {
2622 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2623 block_duration / mux->time_scale);
2625 gst_ebml_write_master_finish (ebml, blockgroup);
2626 return gst_ebml_last_write_result (ebml);
2632 * gst_matroska_mux_collected:
2633 * @pads: #GstCollectPads
2634 * @uuser_data: #GstMatroskaMux
2636 * Collectpads callback.
2638 * Returns: #GstFlowReturn
2640 static GstFlowReturn
2641 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2643 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2644 GstMatroskaPad *best;
2648 GST_DEBUG_OBJECT (mux, "Collected pads");
2650 /* start with a header */
2651 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2652 if (mux->collect->data == NULL) {
2653 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2654 ("No input streams configured"));
2655 return GST_FLOW_ERROR;
2657 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2658 gst_matroska_mux_start (mux);
2659 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2663 /* which stream to write from? */
2664 best = gst_matroska_mux_best_pad (mux, &popped);
2666 /* if there is no best pad, we have reached EOS */
2668 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2669 gst_matroska_mux_finish (mux);
2670 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2671 ret = GST_FLOW_UNEXPECTED;
2674 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2675 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2676 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2677 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2679 /* make note of first and last encountered timestamps, so we can calculate
2680 * the actual duration later when we send an updated header on eos */
2681 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2682 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2683 GstClockTime end_ts = start_ts;
2685 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2686 end_ts += GST_BUFFER_DURATION (best->buffer);
2687 else if (best->track->default_duration)
2688 end_ts += best->track->default_duration;
2690 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2691 best->end_ts = end_ts;
2693 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2694 start_ts < best->start_ts))
2695 best->start_ts = start_ts;
2698 /* write one buffer */
2699 ret = gst_matroska_mux_write_data (mux, best);
2700 } while (ret == GST_FLOW_OK && !popped);
2707 * gst_matroska_mux_change_state:
2708 * @element: #GstMatroskaMux
2709 * @transition: State change transition.
2711 * Change the muxer state.
2713 * Returns: #GstStateChangeReturn
2715 static GstStateChangeReturn
2716 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2718 GstStateChangeReturn ret;
2719 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2721 switch (transition) {
2722 case GST_STATE_CHANGE_NULL_TO_READY:
2724 case GST_STATE_CHANGE_READY_TO_PAUSED:
2725 gst_collect_pads_start (mux->collect);
2727 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2729 case GST_STATE_CHANGE_PAUSED_TO_READY:
2730 gst_collect_pads_stop (mux->collect);
2736 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2738 switch (transition) {
2739 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2741 case GST_STATE_CHANGE_PAUSED_TO_READY:
2742 gst_matroska_mux_reset (GST_ELEMENT (mux));
2744 case GST_STATE_CHANGE_READY_TO_NULL:
2754 gst_matroska_mux_set_property (GObject * object,
2755 guint prop_id, const GValue * value, GParamSpec * pspec)
2757 GstMatroskaMux *mux;
2759 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2760 mux = GST_MATROSKA_MUX (object);
2763 case ARG_WRITING_APP:
2764 if (!g_value_get_string (value)) {
2765 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2768 g_free (mux->writing_app);
2769 mux->writing_app = g_value_dup_string (value);
2771 case ARG_MATROSKA_VERSION:
2772 mux->matroska_version = g_value_get_int (value);
2774 case ARG_MIN_INDEX_INTERVAL:
2775 mux->min_index_interval = g_value_get_int64 (value);
2778 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2784 gst_matroska_mux_get_property (GObject * object,
2785 guint prop_id, GValue * value, GParamSpec * pspec)
2787 GstMatroskaMux *mux;
2789 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2790 mux = GST_MATROSKA_MUX (object);
2793 case ARG_WRITING_APP:
2794 g_value_set_string (value, mux->writing_app);
2796 case ARG_MATROSKA_VERSION:
2797 g_value_set_int (value, mux->matroska_version);
2799 case ARG_MIN_INDEX_INTERVAL:
2800 g_value_set_int64 (value, mux->min_index_interval);
2803 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2809 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2811 return gst_element_register (plugin, "matroskamux",
2812 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);