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_DOCTYPE_VERSION 2
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 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
208 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
210 /* Matroska muxer destructor */
211 static void gst_matroska_mux_finalize (GObject * object);
213 /* Pads collected callback */
215 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
218 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
220 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
221 GstPadTemplate * templ, const gchar * name);
222 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
224 /* gst internal change state handler */
225 static GstStateChangeReturn
226 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
228 /* gobject bla bla */
229 static void gst_matroska_mux_set_property (GObject * object,
230 guint prop_id, const GValue * value, GParamSpec * pspec);
231 static void gst_matroska_mux_get_property (GObject * object,
232 guint prop_id, GValue * value, GParamSpec * pspec);
235 static void gst_matroska_mux_reset (GstElement * element);
238 static guint64 gst_matroska_mux_create_uid ();
240 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
241 GstMatroskaTrackContext * context);
242 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
243 GstMatroskaTrackContext * context);
244 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
245 GstMatroskaTrackContext * context);
246 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
247 GstMatroskaTrackContext * context);
248 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
249 GstMatroskaTrackContext * context);
252 gst_matroska_mux_add_interfaces (GType type)
254 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
256 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
260 gst_matroska_mux_base_init (gpointer g_class)
265 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
267 GObjectClass *gobject_class;
268 GstElementClass *gstelement_class;
270 gobject_class = (GObjectClass *) klass;
271 gstelement_class = (GstElementClass *) klass;
273 gst_element_class_add_pad_template (gstelement_class,
274 gst_static_pad_template_get (&videosink_templ));
275 gst_element_class_add_pad_template (gstelement_class,
276 gst_static_pad_template_get (&audiosink_templ));
277 gst_element_class_add_pad_template (gstelement_class,
278 gst_static_pad_template_get (&subtitlesink_templ));
279 gst_element_class_add_pad_template (gstelement_class,
280 gst_static_pad_template_get (&src_templ));
281 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
283 "Muxes video/audio/subtitle streams into a matroska stream",
284 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
286 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
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_DOCTYPE_VERSION,
299 g_param_spec_int ("version", "DocType version",
300 "This parameter determines what Matroska features can be used.",
301 1, 2, DEFAULT_DOCTYPE_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 GstPadTemplate *templ;
329 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
330 mux->srcpad = gst_pad_new_from_template (templ, "src");
332 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
333 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
335 mux->collect = gst_collect_pads_new ();
336 gst_collect_pads_set_function (mux->collect,
337 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
340 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
341 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
343 /* property defaults */
344 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
345 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
346 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
348 /* initialize internal variables */
350 mux->num_streams = 0;
351 mux->num_a_streams = 0;
352 mux->num_t_streams = 0;
353 mux->num_v_streams = 0;
355 /* initialize remaining variables */
356 gst_matroska_mux_reset (GST_ELEMENT (mux));
361 * gst_matroska_mux_finalize:
362 * @object: #GstMatroskaMux that should be finalized.
364 * Finalize matroska muxer.
367 gst_matroska_mux_finalize (GObject * object)
369 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
371 gst_object_unref (mux->collect);
372 gst_object_unref (mux->ebml_write);
373 if (mux->writing_app)
374 g_free (mux->writing_app);
376 G_OBJECT_CLASS (parent_class)->finalize (object);
381 * gst_matroska_mux_create_uid:
383 * Generate new unused track UID.
385 * Returns: New track UID.
388 gst_matroska_mux_create_uid (void)
395 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
400 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
401 for (i = 0; i < used_uids->len; i++) {
402 if (g_array_index (used_uids, guint64, i) == uid) {
407 g_array_append_val (used_uids, uid);
410 G_UNLOCK (used_uids);
416 * gst_matroska_pad_reset:
417 * @collect_pad: the #GstMatroskaPad
419 * Reset and/or release resources of a matroska collect pad.
422 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
425 GstMatroskaTrackType type = 0;
427 /* free track information */
428 if (collect_pad->track != NULL) {
429 /* retrieve for optional later use */
430 name = collect_pad->track->name;
431 type = collect_pad->track->type;
432 /* extra for video */
433 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
434 GstMatroskaTrackVideoContext *ctx =
435 (GstMatroskaTrackVideoContext *) collect_pad->track;
437 if (ctx->dirac_unit) {
438 gst_buffer_unref (ctx->dirac_unit);
439 ctx->dirac_unit = NULL;
442 g_free (collect_pad->track->codec_id);
443 g_free (collect_pad->track->codec_name);
445 g_free (collect_pad->track->name);
446 g_free (collect_pad->track->language);
447 g_free (collect_pad->track->codec_priv);
448 g_free (collect_pad->track);
449 collect_pad->track = NULL;
452 /* free cached buffer */
453 if (collect_pad->buffer != NULL) {
454 gst_buffer_unref (collect_pad->buffer);
455 collect_pad->buffer = NULL;
458 if (!full && type != 0) {
459 GstMatroskaTrackContext *context;
461 /* create a fresh context */
463 case GST_MATROSKA_TRACK_TYPE_VIDEO:
464 context = (GstMatroskaTrackContext *)
465 g_new0 (GstMatroskaTrackVideoContext, 1);
467 case GST_MATROSKA_TRACK_TYPE_AUDIO:
468 context = (GstMatroskaTrackContext *)
469 g_new0 (GstMatroskaTrackAudioContext, 1);
471 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
472 context = (GstMatroskaTrackContext *)
473 g_new0 (GstMatroskaTrackSubtitleContext, 1);
476 g_assert_not_reached ();
480 context->type = type;
481 context->name = name;
482 /* TODO: check default values for the context */
483 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
484 collect_pad->track = context;
485 collect_pad->buffer = NULL;
486 collect_pad->duration = 0;
487 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
488 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
493 * gst_matroska_pad_free:
494 * @collect_pad: the #GstMatroskaPad
496 * Release resources of a matroska collect pad.
499 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
501 gst_matroska_pad_reset (collect_pad, TRUE);
506 * gst_matroska_mux_reset:
507 * @element: #GstMatroskaMux that should be reseted.
509 * Reset matroska muxer back to initial state.
512 gst_matroska_mux_reset (GstElement * element)
514 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
517 /* reset EBML write */
518 gst_ebml_write_reset (mux->ebml_write);
521 mux->state = GST_MATROSKA_MUX_STATE_START;
523 /* clean up existing streams */
525 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
526 GstMatroskaPad *collect_pad;
528 collect_pad = (GstMatroskaPad *) walk->data;
530 /* reset collect pad to pristine state */
531 gst_matroska_pad_reset (collect_pad, FALSE);
535 mux->num_indexes = 0;
540 mux->time_scale = GST_MSECOND;
545 mux->cluster_time = 0;
546 mux->cluster_pos = 0;
547 mux->prev_cluster_size = 0;
550 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
554 * gst_matroska_mux_handle_src_event:
555 * @pad: Pad which received the event.
556 * @event: Received event.
558 * handle events - copied from oggmux without understanding
560 * Returns: #TRUE on success.
563 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
567 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
571 /* disable seeking for now */
577 return gst_pad_event_default (pad, event);
581 * gst_matroska_mux_handle_sink_event:
582 * @pad: Pad which received the event.
583 * @event: Received event.
585 * handle events - informational ones like tags
587 * Returns: #TRUE on success.
590 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
592 GstMatroskaTrackContext *context;
593 GstMatroskaPad *collect_pad;
598 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
600 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
601 switch (GST_EVENT_TYPE (event)) {
605 GST_DEBUG_OBJECT (mux, "received tag event");
606 gst_event_parse_tag (event, &list);
608 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
609 g_assert (collect_pad);
610 context = collect_pad->track;
613 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
614 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
615 const gchar *lang_code;
617 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
619 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
620 context->language = g_strdup (lang_code);
622 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
627 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
628 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
631 case GST_EVENT_NEWSEGMENT:
632 /* We don't support NEWSEGMENT events */
634 gst_event_unref (event);
640 /* now GstCollectPads can take care of the rest, e.g. EOS */
642 ret = mux->collect_event (pad, event);
643 gst_object_unref (mux);
650 * gst_matroska_mux_video_pad_setcaps:
651 * @pad: Pad which got the caps.
654 * Setcaps function for video sink pad.
656 * Returns: #TRUE on success.
659 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
661 GstMatroskaTrackContext *context = NULL;
662 GstMatroskaTrackVideoContext *videocontext;
664 GstMatroskaPad *collect_pad;
665 GstStructure *structure;
666 const gchar *mimetype;
667 const GValue *value = NULL;
668 const GstBuffer *codec_buf = NULL;
669 gint width, height, pixel_width, pixel_height;
671 gboolean interlaced = FALSE;
673 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
676 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
677 g_assert (collect_pad);
678 context = collect_pad->track;
680 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
681 videocontext = (GstMatroskaTrackVideoContext *) context;
683 /* gst -> matroska ID'ing */
684 structure = gst_caps_get_structure (caps, 0);
686 mimetype = gst_structure_get_name (structure);
688 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
690 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
692 if (!strcmp (mimetype, "video/x-theora")) {
693 /* we'll extract the details later from the theora identification header */
697 /* get general properties */
698 /* spec says it is mandatory */
699 if (!gst_structure_get_int (structure, "width", &width) ||
700 !gst_structure_get_int (structure, "height", &height))
703 videocontext->pixel_width = width;
704 videocontext->pixel_height = height;
705 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
707 context->default_duration =
708 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
709 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
710 GST_TIME_ARGS (context->default_duration));
712 context->default_duration = 0;
714 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
715 &pixel_width, &pixel_height)) {
716 if (pixel_width > pixel_height) {
717 videocontext->display_width = width * pixel_width / pixel_height;
718 videocontext->display_height = height;
719 } else if (pixel_width < pixel_height) {
720 videocontext->display_width = width;
721 videocontext->display_height = height * pixel_height / pixel_width;
723 videocontext->display_width = 0;
724 videocontext->display_height = 0;
727 videocontext->display_width = 0;
728 videocontext->display_height = 0;
733 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
734 videocontext->fourcc = 0;
736 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
737 * data and other settings
741 /* extract codec_data, may turn out needed */
742 value = gst_structure_get_value (structure, "codec_data");
744 codec_buf = gst_value_get_buffer (value);
747 if (!strcmp (mimetype, "video/x-raw-yuv")) {
748 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
749 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
750 } else if (!strcmp (mimetype, "image/jpeg")) {
751 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
752 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
753 ||!strcmp (mimetype, "video/x-huffyuv")
754 || !strcmp (mimetype, "video/x-divx")
755 || !strcmp (mimetype, "video/x-dv")
756 || !strcmp (mimetype, "video/x-h263")
757 || !strcmp (mimetype, "video/x-msmpeg")
758 || !strcmp (mimetype, "video/x-wmv")) {
759 gst_riff_strf_vids *bih;
760 gint size = sizeof (gst_riff_strf_vids);
763 if (!strcmp (mimetype, "video/x-xvid"))
764 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
765 else if (!strcmp (mimetype, "video/x-huffyuv"))
766 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
767 else if (!strcmp (mimetype, "video/x-dv"))
768 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
769 else if (!strcmp (mimetype, "video/x-h263"))
770 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
771 else if (!strcmp (mimetype, "video/x-divx")) {
774 gst_structure_get_int (structure, "divxversion", &divxversion);
775 switch (divxversion) {
777 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
780 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
783 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
786 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
789 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
790 switch (msmpegversion) {
792 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
795 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
801 } else if (!strcmp (mimetype, "video/x-wmv")) {
804 if (gst_structure_get_fourcc (structure, "format", &format)) {
806 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
807 if (wmvversion == 2) {
808 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
809 } else if (wmvversion == 1) {
810 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
811 } else if (wmvversion == 3) {
812 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
820 bih = g_new0 (gst_riff_strf_vids, 1);
821 GST_WRITE_UINT32_LE (&bih->size, size);
822 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
823 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
824 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
825 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
826 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
827 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
828 videocontext->pixel_height * 3);
830 /* process codec private/initialization data, if any */
832 size += GST_BUFFER_SIZE (codec_buf);
833 bih = g_realloc (bih, size);
834 GST_WRITE_UINT32_LE (&bih->size, size);
835 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
836 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
839 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
840 context->codec_priv = (gpointer) bih;
841 context->codec_priv_size = size;
842 } else if (!strcmp (mimetype, "video/x-h264")) {
843 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
845 if (context->codec_priv != NULL) {
846 g_free (context->codec_priv);
847 context->codec_priv = NULL;
848 context->codec_priv_size = 0;
851 /* Create avcC header */
852 if (codec_buf != NULL) {
853 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
854 context->codec_priv = g_malloc0 (context->codec_priv_size);
855 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
856 context->codec_priv_size);
858 } else if (!strcmp (mimetype, "video/x-theora")) {
859 const GValue *streamheader;
861 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
863 if (context->codec_priv != NULL) {
864 g_free (context->codec_priv);
865 context->codec_priv = NULL;
866 context->codec_priv_size = 0;
869 streamheader = gst_structure_get_value (structure, "streamheader");
870 if (!theora_streamheader_to_codecdata (streamheader, context)) {
871 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
872 ("theora stream headers missing or malformed"));
875 } else if (!strcmp (mimetype, "video/x-dirac")) {
876 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
877 } else if (!strcmp (mimetype, "video/x-vp8")) {
878 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
879 } else if (!strcmp (mimetype, "video/mpeg")) {
882 gst_structure_get_int (structure, "mpegversion", &mpegversion);
883 switch (mpegversion) {
885 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
888 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
891 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
897 /* global headers may be in codec data */
898 if (codec_buf != NULL) {
899 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
900 context->codec_priv = g_malloc0 (context->codec_priv_size);
901 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
902 context->codec_priv_size);
904 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
906 /* can only make it here if preceding case verified it was version 3 */
907 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
908 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
910 const GValue *mdpr_data;
912 gst_structure_get_int (structure, "rmversion", &rmversion);
915 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
918 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
921 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
924 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
930 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
931 if (mdpr_data != NULL) {
932 guint8 *priv_data = NULL;
933 guint priv_data_size = 0;
935 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
937 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
938 priv_data = g_malloc0 (priv_data_size);
940 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
942 context->codec_priv = priv_data;
943 context->codec_priv_size = priv_data_size;
952 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
953 GST_PAD_NAME (pad), caps);
958 /* N > 0 to expect a particular number of headers, negative if the
959 number of headers is variable */
961 xiphN_streamheader_to_codecdata (const GValue * streamheader,
962 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
964 GstBuffer **buf = NULL;
967 guint bufi, i, offset, priv_data_size;
969 if (streamheader == NULL)
970 goto no_stream_headers;
972 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
975 bufarr = g_value_peek_pointer (streamheader);
976 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
978 if (N > 0 && bufarr->len != N)
981 context->xiph_headers_to_skip = bufarr->len;
983 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
984 for (i = 0; i < bufarr->len; i++) {
985 GValue *bufval = &g_array_index (bufarr, GValue, i);
987 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
989 goto wrong_content_type;
992 buf[i] = g_value_peek_pointer (bufval);
996 if (bufarr->len > 0) {
997 for (i = 0; i < bufarr->len - 1; i++) {
998 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1002 for (i = 0; i < bufarr->len; ++i) {
1003 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1006 priv_data = g_malloc0 (priv_data_size);
1008 priv_data[0] = bufarr->len - 1;
1011 if (bufarr->len > 0) {
1012 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1013 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1014 priv_data[offset++] = 0xff;
1016 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1020 for (i = 0; i < bufarr->len; ++i) {
1021 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1022 GST_BUFFER_SIZE (buf[i]));
1023 offset += GST_BUFFER_SIZE (buf[i]);
1026 context->codec_priv = priv_data;
1027 context->codec_priv_size = priv_data_size;
1030 *p_buf0 = gst_buffer_ref (buf[0]);
1039 GST_WARNING ("required streamheaders missing in sink caps!");
1044 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1045 G_VALUE_TYPE_NAME (streamheader));
1050 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1055 GST_WARNING ("streamheaders array does not contain GstBuffers");
1061 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1062 GstMatroskaTrackContext * context)
1064 GstBuffer *buf0 = NULL;
1066 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1069 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1070 GST_WARNING ("First vorbis header too small, ignoring");
1072 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1073 GstMatroskaTrackAudioContext *audiocontext;
1076 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1077 audiocontext = (GstMatroskaTrackAudioContext *) context;
1078 audiocontext->channels = GST_READ_UINT8 (hdr);
1079 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1084 gst_buffer_unref (buf0);
1090 theora_streamheader_to_codecdata (const GValue * streamheader,
1091 GstMatroskaTrackContext * context)
1093 GstBuffer *buf0 = NULL;
1095 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1098 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1099 GST_WARNING ("First theora header too small, ignoring");
1100 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1101 GST_WARNING ("First header not a theora identification header, ignoring");
1103 GstMatroskaTrackVideoContext *videocontext;
1104 guint fps_num, fps_denom, par_num, par_denom;
1107 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1109 videocontext = (GstMatroskaTrackVideoContext *) context;
1110 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1111 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1112 hdr += 3 + 3 + 1 + 1;
1113 fps_num = GST_READ_UINT32_BE (hdr);
1114 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1115 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1116 fps_denom, fps_num);
1118 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1119 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1120 if (par_num > 0 && par_num > 0) {
1121 if (par_num > par_denom) {
1122 videocontext->display_width =
1123 videocontext->pixel_width * par_num / par_denom;
1124 videocontext->display_height = videocontext->pixel_height;
1125 } else if (par_num < par_denom) {
1126 videocontext->display_width = videocontext->pixel_width;
1127 videocontext->display_height =
1128 videocontext->pixel_height * par_denom / par_num;
1130 videocontext->display_width = 0;
1131 videocontext->display_height = 0;
1134 videocontext->display_width = 0;
1135 videocontext->display_height = 0;
1141 gst_buffer_unref (buf0);
1147 kate_streamheader_to_codecdata (const GValue * streamheader,
1148 GstMatroskaTrackContext * context)
1150 GstBuffer *buf0 = NULL;
1152 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1155 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1156 GST_WARNING ("First kate header too small, ignoring");
1157 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1158 GST_WARNING ("First header not a kate identification header, ignoring");
1162 gst_buffer_unref (buf0);
1168 flac_streamheader_to_codecdata (const GValue * streamheader,
1169 GstMatroskaTrackContext * context)
1176 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1177 GST_WARNING ("No or invalid streamheader field in the caps");
1181 bufarr = g_value_peek_pointer (streamheader);
1182 if (bufarr->len < 2) {
1183 GST_WARNING ("Too few headers in streamheader field");
1187 context->xiph_headers_to_skip = bufarr->len + 1;
1189 bufval = &g_array_index (bufarr, GValue, 0);
1190 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1191 GST_WARNING ("streamheaders array does not contain GstBuffers");
1195 buffer = g_value_peek_pointer (bufval);
1197 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1198 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1199 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1200 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1201 GST_WARNING ("Invalid streamheader for FLAC");
1205 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1206 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1207 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1208 GST_BUFFER_SIZE (buffer) - 9);
1210 for (i = 1; i < bufarr->len; i++) {
1211 bufval = &g_array_index (bufarr, GValue, i);
1213 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1214 g_free (context->codec_priv);
1215 context->codec_priv = NULL;
1216 context->codec_priv_size = 0;
1217 GST_WARNING ("streamheaders array does not contain GstBuffers");
1221 buffer = g_value_peek_pointer (bufval);
1223 context->codec_priv =
1224 g_realloc (context->codec_priv,
1225 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1226 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1227 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1228 context->codec_priv_size =
1229 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1236 speex_streamheader_to_codecdata (const GValue * streamheader,
1237 GstMatroskaTrackContext * context)
1243 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1244 GST_WARNING ("No or invalid streamheader field in the caps");
1248 bufarr = g_value_peek_pointer (streamheader);
1249 if (bufarr->len != 2) {
1250 GST_WARNING ("Too few headers in streamheader field");
1254 context->xiph_headers_to_skip = bufarr->len + 1;
1256 bufval = &g_array_index (bufarr, GValue, 0);
1257 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1258 GST_WARNING ("streamheaders array does not contain GstBuffers");
1262 buffer = g_value_peek_pointer (bufval);
1264 if (GST_BUFFER_SIZE (buffer) < 80
1265 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1266 GST_WARNING ("Invalid streamheader for Speex");
1270 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1271 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1272 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1273 GST_BUFFER_SIZE (buffer));
1275 bufval = &g_array_index (bufarr, GValue, 1);
1277 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1278 g_free (context->codec_priv);
1279 context->codec_priv = NULL;
1280 context->codec_priv_size = 0;
1281 GST_WARNING ("streamheaders array does not contain GstBuffers");
1285 buffer = g_value_peek_pointer (bufval);
1287 context->codec_priv =
1288 g_realloc (context->codec_priv,
1289 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1290 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1291 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1292 context->codec_priv_size =
1293 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1298 static const gchar *
1299 aac_codec_data_to_codec_id (const GstBuffer * buf)
1301 const gchar *result;
1304 /* default to MAIN */
1307 if (GST_BUFFER_SIZE (buf) >= 2) {
1308 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1326 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1335 * gst_matroska_mux_audio_pad_setcaps:
1336 * @pad: Pad which got the caps.
1339 * Setcaps function for audio sink pad.
1341 * Returns: #TRUE on success.
1344 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1346 GstMatroskaTrackContext *context = NULL;
1347 GstMatroskaTrackAudioContext *audiocontext;
1348 GstMatroskaMux *mux;
1349 GstMatroskaPad *collect_pad;
1350 const gchar *mimetype;
1351 gint samplerate = 0, channels = 0;
1352 GstStructure *structure;
1353 const GValue *codec_data = NULL;
1354 const GstBuffer *buf = NULL;
1355 const gchar *stream_format = NULL;
1357 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1360 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1361 g_assert (collect_pad);
1362 context = collect_pad->track;
1364 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1365 audiocontext = (GstMatroskaTrackAudioContext *) context;
1367 structure = gst_caps_get_structure (caps, 0);
1368 mimetype = gst_structure_get_name (structure);
1371 gst_structure_get_int (structure, "rate", &samplerate);
1372 gst_structure_get_int (structure, "channels", &channels);
1374 audiocontext->samplerate = samplerate;
1375 audiocontext->channels = channels;
1376 audiocontext->bitdepth = 0;
1377 context->default_duration = 0;
1379 codec_data = gst_structure_get_value (structure, "codec_data");
1381 buf = gst_value_get_buffer (codec_data);
1383 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1384 * data and other settings
1388 if (!strcmp (mimetype, "audio/mpeg")) {
1389 gint mpegversion = 0;
1391 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1392 switch (mpegversion) {
1398 gst_structure_get_int (structure, "layer", &layer);
1400 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1401 GST_WARNING_OBJECT (mux,
1402 "Unable to determine MPEG audio version, assuming 1");
1408 else if (layer == 2)
1410 else if (version == 2)
1415 context->default_duration =
1416 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1420 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1423 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1426 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1435 stream_format = gst_structure_get_string (structure, "stream-format");
1436 /* check this is raw aac */
1437 if (stream_format) {
1438 if (strcmp (stream_format, "raw") != 0) {
1439 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1443 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1448 if (mpegversion == 2)
1450 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1451 aac_codec_data_to_codec_id (buf));
1452 else if (mpegversion == 4)
1454 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1455 aac_codec_data_to_codec_id (buf));
1457 g_assert_not_reached ();
1459 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1466 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1468 gint endianness = G_LITTLE_ENDIAN;
1469 gboolean signedness = TRUE;
1471 if (!gst_structure_get_int (structure, "width", &width) ||
1472 !gst_structure_get_int (structure, "depth", &depth) ||
1473 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1474 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1479 !gst_structure_get_int (structure, "endianness", &endianness)) {
1480 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1484 if (width != depth) {
1485 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1489 /* FIXME: where is this spec'ed out? (tpm) */
1490 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1491 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1495 audiocontext->bitdepth = depth;
1496 if (endianness == G_BIG_ENDIAN)
1497 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1499 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1501 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1504 if (!gst_structure_get_int (structure, "width", &width)) {
1505 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1509 audiocontext->bitdepth = width;
1510 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1512 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1513 const GValue *streamheader;
1515 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1517 if (context->codec_priv != NULL) {
1518 g_free (context->codec_priv);
1519 context->codec_priv = NULL;
1520 context->codec_priv_size = 0;
1523 streamheader = gst_structure_get_value (structure, "streamheader");
1524 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1525 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1526 ("vorbis stream headers missing or malformed"));
1529 } else if (!strcmp (mimetype, "audio/x-flac")) {
1530 const GValue *streamheader;
1532 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1533 if (context->codec_priv != NULL) {
1534 g_free (context->codec_priv);
1535 context->codec_priv = NULL;
1536 context->codec_priv_size = 0;
1539 streamheader = gst_structure_get_value (structure, "streamheader");
1540 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1541 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1542 ("flac stream headers missing or malformed"));
1545 } else if (!strcmp (mimetype, "audio/x-speex")) {
1546 const GValue *streamheader;
1548 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1549 if (context->codec_priv != NULL) {
1550 g_free (context->codec_priv);
1551 context->codec_priv = NULL;
1552 context->codec_priv_size = 0;
1555 streamheader = gst_structure_get_value (structure, "streamheader");
1556 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1557 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1558 ("speex stream headers missing or malformed"));
1561 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1562 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1563 } else if (!strcmp (mimetype, "audio/x-tta")) {
1566 /* TTA frame duration */
1567 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1569 gst_structure_get_int (structure, "width", &width);
1570 audiocontext->bitdepth = width;
1571 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1573 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1575 const GValue *mdpr_data;
1577 gst_structure_get_int (structure, "raversion", &raversion);
1578 switch (raversion) {
1580 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1583 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1586 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1592 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1593 if (mdpr_data != NULL) {
1594 guint8 *priv_data = NULL;
1595 guint priv_data_size = 0;
1597 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1599 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1600 priv_data = g_malloc0 (priv_data_size);
1602 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1604 context->codec_priv = priv_data;
1605 context->codec_priv_size = priv_data_size;
1608 } else if (!strcmp (mimetype, "audio/x-wma")) {
1610 guint codec_priv_size;
1617 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1618 || !gst_structure_get_int (structure, "block_align", &block_align)
1619 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1620 || samplerate == 0 || channels == 0) {
1621 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1622 "channels/rate on WMA caps");
1626 switch (wmaversion) {
1628 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1631 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1634 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1637 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1641 if (gst_structure_get_int (structure, "depth", &depth))
1642 audiocontext->bitdepth = depth;
1644 codec_priv_size = WAVEFORMATEX_SIZE;
1646 codec_priv_size += GST_BUFFER_SIZE (buf);
1648 /* serialize waveformatex structure */
1649 codec_priv = g_malloc0 (codec_priv_size);
1650 GST_WRITE_UINT16_LE (codec_priv, format);
1651 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1652 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1653 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1654 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1655 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1657 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1659 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1661 /* process codec private/initialization data, if any */
1663 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1664 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1667 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1668 context->codec_priv = (gpointer) codec_priv;
1669 context->codec_priv_size = codec_priv_size;
1677 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1678 GST_PAD_NAME (pad), caps);
1685 * gst_matroska_mux_subtitle_pad_setcaps:
1686 * @pad: Pad which got the caps.
1689 * Setcaps function for subtitle sink pad.
1691 * Returns: #TRUE on success.
1694 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1697 * Consider this as boilerplate code for now. There is
1698 * no single subtitle creation element in GStreamer,
1699 * neither do I know how subtitling works at all. */
1701 /* There is now (at least) one such alement (kateenc), and I'm going
1702 to handle it here and claim it works when it can be piped back
1703 through GStreamer and VLC */
1705 GstMatroskaTrackContext *context = NULL;
1706 GstMatroskaTrackSubtitleContext *scontext;
1707 GstMatroskaMux *mux;
1708 GstMatroskaPad *collect_pad;
1709 const gchar *mimetype;
1710 GstStructure *structure;
1712 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1715 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1716 g_assert (collect_pad);
1717 context = collect_pad->track;
1719 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1720 scontext = (GstMatroskaTrackSubtitleContext *) context;
1722 structure = gst_caps_get_structure (caps, 0);
1723 mimetype = gst_structure_get_name (structure);
1726 scontext->check_utf8 = 1;
1727 scontext->invalid_utf8 = 0;
1728 context->default_duration = 0;
1730 /* TODO: - other format than Kate */
1732 if (!strcmp (mimetype, "subtitle/x-kate")) {
1733 const GValue *streamheader;
1735 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1737 if (context->codec_priv != NULL) {
1738 g_free (context->codec_priv);
1739 context->codec_priv = NULL;
1740 context->codec_priv_size = 0;
1743 streamheader = gst_structure_get_value (structure, "streamheader");
1744 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1745 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1746 ("kate stream headers missing or malformed"));
1757 * gst_matroska_mux_request_new_pad:
1758 * @element: #GstMatroskaMux.
1759 * @templ: #GstPadTemplate.
1760 * @pad_name: New pad name.
1762 * Request pad function for sink templates.
1764 * Returns: New #GstPad.
1767 gst_matroska_mux_request_new_pad (GstElement * element,
1768 GstPadTemplate * templ, const gchar * pad_name)
1770 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1771 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1772 GstMatroskaPad *collect_pad;
1773 GstPad *newpad = NULL;
1775 GstPadSetCapsFunction setcapsfunc = NULL;
1776 GstMatroskaTrackContext *context = NULL;
1778 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1779 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1780 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1781 context = (GstMatroskaTrackContext *)
1782 g_new0 (GstMatroskaTrackAudioContext, 1);
1783 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1784 context->name = g_strdup ("Audio");
1785 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1786 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1787 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1788 context = (GstMatroskaTrackContext *)
1789 g_new0 (GstMatroskaTrackVideoContext, 1);
1790 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1791 context->name = g_strdup ("Video");
1792 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1793 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1794 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1795 context = (GstMatroskaTrackContext *)
1796 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1797 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1798 context->name = g_strdup ("Subtitle");
1800 GST_WARNING_OBJECT (mux, "This is not our template!");
1804 newpad = gst_pad_new_from_template (templ, name);
1806 collect_pad = (GstMatroskaPad *)
1807 gst_collect_pads_add_pad_full (mux->collect, newpad,
1808 sizeof (GstMatroskaPad),
1809 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1811 collect_pad->track = context;
1812 gst_matroska_pad_reset (collect_pad, FALSE);
1814 /* FIXME: hacked way to override/extend the event function of
1815 * GstCollectPads; because it sets its own event function giving the
1816 * element no access to events.
1817 * TODO GstCollectPads should really give its 'users' a clean chance to
1818 * properly handle events that are not meant for collectpads itself.
1819 * Perhaps a callback or so, though rejected (?) in #340060.
1820 * This would allow (clean) transcoding of info from demuxer/streams
1821 * to another muxer */
1822 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1823 gst_pad_set_event_function (newpad,
1824 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1826 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1827 gst_pad_set_active (newpad, TRUE);
1828 gst_element_add_pad (element, newpad);
1835 * gst_matroska_mux_release_pad:
1836 * @element: #GstMatroskaMux.
1837 * @pad: Pad to release.
1839 * Release a previously requested pad.
1842 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1844 GstMatroskaMux *mux;
1847 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1849 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1850 GstCollectData *cdata = (GstCollectData *) walk->data;
1851 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1853 if (cdata->pad == pad) {
1854 GstClockTime min_dur; /* observed minimum duration */
1856 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1857 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1858 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1859 if (collect_pad->duration < min_dur)
1860 collect_pad->duration = min_dur;
1863 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1864 mux->duration < collect_pad->duration)
1865 mux->duration = collect_pad->duration;
1871 gst_collect_pads_remove_pad (mux->collect, pad);
1872 if (gst_element_remove_pad (element, pad))
1878 * gst_matroska_mux_track_header:
1879 * @mux: #GstMatroskaMux
1880 * @context: Tack context.
1882 * Write a track header.
1885 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1886 GstMatroskaTrackContext * context)
1888 GstEbmlWrite *ebml = mux->ebml_write;
1891 /* TODO: check if everything necessary is written and check default values */
1893 /* track type goes before the type-specific stuff */
1894 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1895 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1897 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1898 gst_matroska_mux_create_uid ());
1899 if (context->default_duration) {
1900 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1901 context->default_duration);
1903 if (context->language) {
1904 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1908 /* type-specific stuff */
1909 switch (context->type) {
1910 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1911 GstMatroskaTrackVideoContext *videocontext =
1912 (GstMatroskaTrackVideoContext *) context;
1914 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1915 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1916 videocontext->pixel_width);
1917 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1918 videocontext->pixel_height);
1919 if (videocontext->display_width && videocontext->display_height) {
1920 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1921 videocontext->display_width);
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1923 videocontext->display_height);
1925 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1926 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1927 if (videocontext->fourcc) {
1928 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1930 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1931 (gpointer) & fcc_le, 4);
1933 gst_ebml_write_master_finish (ebml, master);
1938 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1939 GstMatroskaTrackAudioContext *audiocontext =
1940 (GstMatroskaTrackAudioContext *) context;
1942 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1943 if (audiocontext->samplerate != 8000)
1944 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1945 audiocontext->samplerate);
1946 if (audiocontext->channels != 1)
1947 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1948 audiocontext->channels);
1949 if (audiocontext->bitdepth) {
1950 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1951 audiocontext->bitdepth);
1953 gst_ebml_write_master_finish (ebml, master);
1959 /* doesn't need type-specific data */
1963 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1964 if (context->codec_priv)
1965 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1966 context->codec_priv, context->codec_priv_size);
1967 /* FIXME: until we have a nice way of getting the codecname
1968 * out of the caps, I'm not going to enable this. Too much
1969 * (useless, double, boring) work... */
1970 /* TODO: Use value from tags if any */
1971 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1972 context->codec_name); */
1973 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1978 * gst_matroska_mux_start:
1979 * @mux: #GstMatroskaMux
1981 * Start a new matroska file (write headers etc...)
1984 gst_matroska_mux_start (GstMatroskaMux * mux)
1986 GstEbmlWrite *ebml = mux->ebml_write;
1987 const gchar *doctype;
1988 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1989 GST_MATROSKA_ID_TRACKS,
1990 GST_MATROSKA_ID_CUES,
1991 GST_MATROSKA_ID_TAGS,
1994 guint64 master, child;
1998 GstClockTime duration = 0;
1999 guint32 segment_uid[4];
2000 GTimeVal time = { 0, 0 };
2002 /* we start with a EBML header */
2003 doctype = mux->doctype;
2004 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2005 doctype, mux->doctype_version);
2006 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2008 /* start a segment */
2010 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2011 mux->segment_master = ebml->pos;
2013 /* the rest of the header is cached */
2014 gst_ebml_write_set_cache (ebml, 0x1000);
2016 /* seekhead (table of contents) - we set the positions later */
2017 mux->seekhead_pos = ebml->pos;
2018 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2019 for (i = 0; seekhead_id[i] != 0; i++) {
2020 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2021 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2022 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2023 gst_ebml_write_master_finish (ebml, child);
2025 gst_ebml_write_master_finish (ebml, master);
2028 mux->info_pos = ebml->pos;
2029 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2030 for (i = 0; i < 4; i++) {
2031 segment_uid[i] = g_random_int ();
2033 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2034 (guint8 *) segment_uid, 16);
2035 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2036 mux->duration_pos = ebml->pos;
2038 for (collected = mux->collect->data; collected;
2039 collected = g_slist_next (collected)) {
2040 GstMatroskaPad *collect_pad;
2041 GstFormat format = GST_FORMAT_TIME;
2043 gint64 trackduration;
2045 collect_pad = (GstMatroskaPad *) collected->data;
2046 thepad = collect_pad->collect.pad;
2048 /* Query the total length of the track. */
2049 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2050 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2051 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2052 GST_TIME_ARGS (trackduration));
2053 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2054 duration = (GstClockTime) trackduration;
2058 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2059 gst_guint64_to_gdouble (duration) /
2060 gst_guint64_to_gdouble (mux->time_scale));
2062 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2063 "GStreamer plugin version " PACKAGE_VERSION);
2064 if (mux->writing_app && mux->writing_app[0]) {
2065 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2067 g_get_current_time (&time);
2068 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2069 gst_ebml_write_master_finish (ebml, master);
2072 mux->tracks_pos = ebml->pos;
2073 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2075 for (collected = mux->collect->data; collected;
2076 collected = g_slist_next (collected)) {
2077 GstMatroskaPad *collect_pad;
2080 collect_pad = (GstMatroskaPad *) collected->data;
2081 thepad = collect_pad->collect.pad;
2083 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2084 collect_pad->track->codec_id != 0) {
2085 collect_pad->track->num = tracknum++;
2086 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2087 gst_matroska_mux_track_header (mux, collect_pad->track);
2088 gst_ebml_write_master_finish (ebml, child);
2091 gst_ebml_write_master_finish (ebml, master);
2093 /* lastly, flush the cache */
2094 gst_ebml_write_flush_cache (ebml);
2098 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2101 /* TODO: more sensible tag mappings */
2104 const gchar *matroska_tagname;
2105 const gchar *gstreamer_tagname;
2109 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2110 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2111 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2112 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2113 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2114 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2115 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2116 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2117 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2118 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2119 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2120 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2121 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2122 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2123 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2125 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2127 guint64 simpletag_master;
2129 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2130 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2131 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2133 if (strcmp (tagname_gst, tag) == 0) {
2134 GValue src = { 0, };
2137 if (!gst_tag_list_copy_value (&src, list, tag))
2139 if ((dest = gst_value_serialize (&src))) {
2141 simpletag_master = gst_ebml_write_master_start (ebml,
2142 GST_MATROSKA_ID_SIMPLETAG);
2143 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2144 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2145 gst_ebml_write_master_finish (ebml, simpletag_master);
2148 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2150 g_value_unset (&src);
2158 * gst_matroska_mux_finish:
2159 * @mux: #GstMatroskaMux
2161 * Finish a new matroska file (write index etc...)
2164 gst_matroska_mux_finish (GstMatroskaMux * mux)
2166 GstEbmlWrite *ebml = mux->ebml_write;
2168 guint64 duration = 0;
2170 const GstTagList *tags;
2172 /* finish last cluster */
2174 gst_ebml_write_master_finish (ebml, mux->cluster);
2178 if (mux->index != NULL) {
2180 guint64 master, pointentry_master, trackpos_master;
2182 mux->cues_pos = ebml->pos;
2183 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2184 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2186 for (n = 0; n < mux->num_indexes; n++) {
2187 GstMatroskaIndex *idx = &mux->index[n];
2189 pointentry_master = gst_ebml_write_master_start (ebml,
2190 GST_MATROSKA_ID_POINTENTRY);
2191 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2192 idx->time / mux->time_scale);
2193 trackpos_master = gst_ebml_write_master_start (ebml,
2194 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2195 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2196 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2197 idx->pos - mux->segment_master);
2198 gst_ebml_write_master_finish (ebml, trackpos_master);
2199 gst_ebml_write_master_finish (ebml, pointentry_master);
2202 gst_ebml_write_master_finish (ebml, master);
2203 gst_ebml_write_flush_cache (ebml);
2207 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2209 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2210 guint64 master_tags, master_tag;
2212 GST_DEBUG ("Writing tags");
2214 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2215 mux->tags_pos = ebml->pos;
2216 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2217 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2218 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2219 gst_ebml_write_master_finish (ebml, master_tag);
2220 gst_ebml_write_master_finish (ebml, master_tags);
2223 /* update seekhead. We know that:
2224 * - a seekhead contains 4 entries.
2225 * - order of entries is as above.
2226 * - a seekhead has a 4-byte header + 8-byte length
2227 * - each entry is 2-byte master, 2-byte ID pointer,
2228 * 2-byte length pointer, all 8/1-byte length, 4-
2229 * byte ID and 8-byte length pointer, where the
2230 * length pointer starts at 20.
2231 * - all entries are local to the segment (so pos - segment_master).
2232 * - so each entry is at 12 + 20 + num * 28. */
2233 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2234 mux->info_pos - mux->segment_master);
2235 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2236 mux->tracks_pos - mux->segment_master);
2237 if (mux->index != NULL) {
2238 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2239 mux->cues_pos - mux->segment_master);
2242 guint64 my_pos = ebml->pos;
2244 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2245 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2246 gst_ebml_write_seek (ebml, my_pos);
2249 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2250 mux->tags_pos - mux->segment_master);
2253 guint64 my_pos = ebml->pos;
2255 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2256 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2257 gst_ebml_write_seek (ebml, my_pos);
2260 /* update duration */
2261 /* first get the overall duration */
2262 /* a released track may have left a duration in here */
2263 duration = mux->duration;
2264 for (collected = mux->collect->data; collected;
2265 collected = g_slist_next (collected)) {
2266 GstMatroskaPad *collect_pad;
2267 GstClockTime min_duration; /* observed minimum duration */
2269 collect_pad = (GstMatroskaPad *) collected->data;
2271 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2272 " end ts %" GST_TIME_FORMAT, collect_pad,
2273 GST_TIME_ARGS (collect_pad->start_ts),
2274 GST_TIME_ARGS (collect_pad->end_ts));
2276 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2277 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2279 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2280 if (collect_pad->duration < min_duration)
2281 collect_pad->duration = min_duration;
2282 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2283 GST_TIME_ARGS (collect_pad->duration));
2286 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2287 duration < collect_pad->duration)
2288 duration = collect_pad->duration;
2290 if (duration != 0) {
2291 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2292 GST_TIME_ARGS (duration));
2293 pos = mux->ebml_write->pos;
2294 gst_ebml_write_seek (ebml, mux->duration_pos);
2295 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2296 gst_guint64_to_gdouble (duration) /
2297 gst_guint64_to_gdouble (mux->time_scale));
2298 gst_ebml_write_seek (ebml, pos);
2301 guint64 my_pos = ebml->pos;
2303 gst_ebml_write_seek (ebml, mux->duration_pos);
2304 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2305 gst_ebml_write_seek (ebml, my_pos);
2308 /* finish segment - this also writes element length */
2309 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2314 * gst_matroska_mux_best_pad:
2315 * @mux: #GstMatroskaMux
2316 * @popped: True if at least one buffer was popped from #GstCollectPads
2318 * Find a pad with the oldest data
2319 * (data from this pad should be written first).
2321 * Returns: Selected pad.
2323 static GstMatroskaPad *
2324 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2327 GstMatroskaPad *best = NULL;
2330 for (collected = mux->collect->data; collected;
2331 collected = g_slist_next (collected)) {
2332 GstMatroskaPad *collect_pad;
2334 collect_pad = (GstMatroskaPad *) collected->data;
2335 /* fetch a new buffer if needed */
2336 if (collect_pad->buffer == NULL) {
2337 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2338 (GstCollectData *) collect_pad);
2340 if (collect_pad->buffer != NULL)
2344 /* if we have a buffer check if it is better then the current best one */
2345 if (collect_pad->buffer != NULL) {
2346 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2347 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2348 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2349 GST_BUFFER_TIMESTAMP (best->buffer))) {
2359 * gst_matroska_mux_buffer_header:
2360 * @track: Track context.
2361 * @relative_timestamp: relative timestamp of the buffer
2362 * @flags: Buffer flags.
2364 * Create a buffer containing buffer header.
2366 * Returns: New buffer.
2369 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2370 gint16 relative_timestamp, int flags)
2374 hdr = gst_buffer_new_and_alloc (4);
2375 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2376 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2377 /* time relative to clustertime */
2378 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2381 GST_BUFFER_DATA (hdr)[3] = flags;
2386 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2387 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2388 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2391 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2392 GstMatroskaPad * collect_pad, GstBuffer * buf)
2394 GstMatroskaTrackVideoContext *ctx =
2395 (GstMatroskaTrackVideoContext *) collect_pad->track;
2396 const guint8 *data = GST_BUFFER_DATA (buf);
2397 guint size = GST_BUFFER_SIZE (buf);
2399 guint32 next_parse_offset;
2400 GstBuffer *ret = NULL;
2401 gboolean is_muxing_unit = FALSE;
2403 if (GST_BUFFER_SIZE (buf) < 13) {
2404 gst_buffer_unref (buf);
2408 /* Check if this buffer contains a picture or end-of-sequence packet */
2409 while (size >= 13) {
2410 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2411 gst_buffer_unref (buf);
2415 parse_code = GST_READ_UINT8 (data + 4);
2416 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2417 if (ctx->dirac_unit) {
2418 gst_buffer_unref (ctx->dirac_unit);
2419 ctx->dirac_unit = NULL;
2421 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2422 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2423 is_muxing_unit = TRUE;
2427 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2429 if (G_UNLIKELY (next_parse_offset == 0))
2432 data += next_parse_offset;
2433 size -= next_parse_offset;
2436 if (ctx->dirac_unit)
2437 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2439 ctx->dirac_unit = gst_buffer_ref (buf);
2441 if (is_muxing_unit) {
2442 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2443 ctx->dirac_unit = NULL;
2444 gst_buffer_copy_metadata (ret, buf,
2445 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2446 GST_BUFFER_COPY_CAPS);
2447 gst_buffer_unref (buf);
2449 gst_buffer_unref (buf);
2457 * gst_matroska_mux_write_data:
2458 * @mux: #GstMatroskaMux
2459 * @collect_pad: #GstMatroskaPad with the data
2461 * Write collected data (called from gst_matroska_mux_collected).
2463 * Returns: Result of the gst_pad_push issued to write the data.
2465 static GstFlowReturn
2466 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2468 GstEbmlWrite *ebml = mux->ebml_write;
2469 GstBuffer *buf, *hdr;
2471 gboolean write_duration;
2472 gint16 relative_timestamp;
2473 gint64 relative_timestamp64;
2474 guint64 block_duration;
2475 gboolean is_video_keyframe = FALSE;
2478 buf = collect_pad->buffer;
2479 collect_pad->buffer = NULL;
2481 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2482 if (collect_pad->track->xiph_headers_to_skip > 0) {
2483 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2484 gst_buffer_unref (buf);
2485 --collect_pad->track->xiph_headers_to_skip;
2489 /* for dirac we have to queue up everything up to a picture unit */
2490 if (collect_pad->track->codec_id != NULL &&
2491 strcmp (collect_pad->track->codec_id,
2492 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2493 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2498 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2499 * this would wreak havoc with time stored in matroska file */
2500 /* TODO: maybe calculate a timestamp by using the previous timestamp
2501 * and default duration */
2502 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2503 GST_WARNING_OBJECT (collect_pad->collect.pad,
2504 "Invalid buffer timestamp; dropping buffer");
2505 gst_buffer_unref (buf);
2509 /* set the timestamp for outgoing buffers */
2510 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2512 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2513 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2514 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2515 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2516 is_video_keyframe = TRUE;
2520 /* start a new cluster every two seconds or at keyframe */
2521 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2522 || is_video_keyframe) {
2524 gst_ebml_write_master_finish (ebml, mux->cluster);
2525 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2526 mux->cluster_pos = ebml->pos;
2528 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2529 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2530 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2531 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2532 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2533 mux->prev_cluster_size);
2538 mux->cluster_pos = ebml->pos;
2539 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2540 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2541 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2542 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2545 /* update duration of this track */
2546 if (GST_BUFFER_DURATION_IS_VALID (buf))
2547 collect_pad->duration += GST_BUFFER_DURATION (buf);
2549 /* We currently write index entries for all video tracks or for the audio
2550 * track in a single-track audio file. This could be improved by keeping the
2551 * index only for the *first* video track. */
2553 /* TODO: index is useful for every track, should contain the number of
2554 * the block in the cluster which contains the timestamp, should also work
2555 * for files with multiple audio tracks.
2557 if (is_video_keyframe ||
2558 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2559 (mux->num_streams == 1))) {
2562 if (mux->min_index_interval != 0) {
2563 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2564 if (mux->index[last_idx].track == collect_pad->track->num)
2569 if (last_idx < 0 || mux->min_index_interval == 0 ||
2570 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2571 >= mux->min_index_interval)) {
2572 GstMatroskaIndex *idx;
2574 if (mux->num_indexes % 32 == 0) {
2575 mux->index = g_renew (GstMatroskaIndex, mux->index,
2576 mux->num_indexes + 32);
2578 idx = &mux->index[mux->num_indexes++];
2580 idx->pos = mux->cluster_pos;
2581 idx->time = GST_BUFFER_TIMESTAMP (buf);
2582 idx->track = collect_pad->track->num;
2586 /* Check if the duration differs from the default duration. */
2587 write_duration = FALSE;
2588 block_duration = GST_BUFFER_DURATION (buf);
2589 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2590 if (block_duration != collect_pad->track->default_duration) {
2591 write_duration = TRUE;
2595 /* write the block, for doctype v2 use SimpleBlock if possible
2596 * one slice (*breath*).
2597 * FIXME: Need to do correct lacing! */
2598 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2599 if (relative_timestamp64 >= 0) {
2600 /* round the timestamp */
2601 relative_timestamp64 += mux->time_scale / 2;
2603 /* round the timestamp */
2604 relative_timestamp64 -= mux->time_scale / 2;
2606 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2607 if (mux->doctype_version > 1 && !write_duration) {
2609 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2612 gst_matroska_mux_create_buffer_header (collect_pad->track,
2613 relative_timestamp, flags);
2614 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2615 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2616 gst_ebml_write_buffer (ebml, hdr);
2617 gst_ebml_write_buffer (ebml, buf);
2619 return gst_ebml_last_write_result (ebml);
2621 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2623 gst_matroska_mux_create_buffer_header (collect_pad->track,
2624 relative_timestamp, 0);
2625 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2626 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2627 gst_ebml_write_buffer (ebml, hdr);
2628 gst_ebml_write_buffer (ebml, buf);
2629 if (write_duration) {
2630 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2631 block_duration / mux->time_scale);
2633 gst_ebml_write_master_finish (ebml, blockgroup);
2634 return gst_ebml_last_write_result (ebml);
2640 * gst_matroska_mux_collected:
2641 * @pads: #GstCollectPads
2642 * @uuser_data: #GstMatroskaMux
2644 * Collectpads callback.
2646 * Returns: #GstFlowReturn
2648 static GstFlowReturn
2649 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2651 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2652 GstMatroskaPad *best;
2656 GST_DEBUG_OBJECT (mux, "Collected pads");
2658 /* start with a header */
2659 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2660 if (mux->collect->data == NULL) {
2661 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2662 ("No input streams configured"));
2663 return GST_FLOW_ERROR;
2665 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2666 gst_matroska_mux_start (mux);
2667 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2671 /* which stream to write from? */
2672 best = gst_matroska_mux_best_pad (mux, &popped);
2674 /* if there is no best pad, we have reached EOS */
2676 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2677 gst_matroska_mux_finish (mux);
2678 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2679 ret = GST_FLOW_UNEXPECTED;
2682 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2683 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2684 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2685 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2687 /* make note of first and last encountered timestamps, so we can calculate
2688 * the actual duration later when we send an updated header on eos */
2689 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2690 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2691 GstClockTime end_ts = start_ts;
2693 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2694 end_ts += GST_BUFFER_DURATION (best->buffer);
2695 else if (best->track->default_duration)
2696 end_ts += best->track->default_duration;
2698 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2699 best->end_ts = end_ts;
2701 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2702 start_ts < best->start_ts))
2703 best->start_ts = start_ts;
2706 /* write one buffer */
2707 ret = gst_matroska_mux_write_data (mux, best);
2708 } while (ret == GST_FLOW_OK && !popped);
2715 * gst_matroska_mux_change_state:
2716 * @element: #GstMatroskaMux
2717 * @transition: State change transition.
2719 * Change the muxer state.
2721 * Returns: #GstStateChangeReturn
2723 static GstStateChangeReturn
2724 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2726 GstStateChangeReturn ret;
2727 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2729 switch (transition) {
2730 case GST_STATE_CHANGE_NULL_TO_READY:
2732 case GST_STATE_CHANGE_READY_TO_PAUSED:
2733 gst_collect_pads_start (mux->collect);
2735 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2737 case GST_STATE_CHANGE_PAUSED_TO_READY:
2738 gst_collect_pads_stop (mux->collect);
2744 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2746 switch (transition) {
2747 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2749 case GST_STATE_CHANGE_PAUSED_TO_READY:
2750 gst_matroska_mux_reset (GST_ELEMENT (mux));
2752 case GST_STATE_CHANGE_READY_TO_NULL:
2762 gst_matroska_mux_set_property (GObject * object,
2763 guint prop_id, const GValue * value, GParamSpec * pspec)
2765 GstMatroskaMux *mux;
2767 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2768 mux = GST_MATROSKA_MUX (object);
2771 case ARG_WRITING_APP:
2772 if (!g_value_get_string (value)) {
2773 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2776 g_free (mux->writing_app);
2777 mux->writing_app = g_value_dup_string (value);
2779 case ARG_DOCTYPE_VERSION:
2780 mux->doctype_version = g_value_get_int (value);
2782 case ARG_MIN_INDEX_INTERVAL:
2783 mux->min_index_interval = g_value_get_int64 (value);
2786 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2792 gst_matroska_mux_get_property (GObject * object,
2793 guint prop_id, GValue * value, GParamSpec * pspec)
2795 GstMatroskaMux *mux;
2797 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2798 mux = GST_MATROSKA_MUX (object);
2801 case ARG_WRITING_APP:
2802 g_value_set_string (value, mux->writing_app);
2804 case ARG_DOCTYPE_VERSION:
2805 g_value_set_int (value, mux->doctype_version);
2807 case ARG_MIN_INDEX_INTERVAL:
2808 g_value_set_int64 (value, mux->min_index_interval);
2811 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);