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");
331 g_object_unref (templ);
333 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
334 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
336 mux->collect = gst_collect_pads_new ();
337 gst_collect_pads_set_function (mux->collect,
338 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
341 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
342 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
344 /* property defaults */
345 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
346 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
347 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
349 /* initialize internal variables */
351 mux->num_streams = 0;
352 mux->num_a_streams = 0;
353 mux->num_t_streams = 0;
354 mux->num_v_streams = 0;
356 /* initialize remaining variables */
357 gst_matroska_mux_reset (GST_ELEMENT (mux));
362 * gst_matroska_mux_finalize:
363 * @object: #GstMatroskaMux that should be finalized.
365 * Finalize matroska muxer.
368 gst_matroska_mux_finalize (GObject * object)
370 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
372 gst_object_unref (mux->collect);
373 gst_object_unref (mux->ebml_write);
374 if (mux->writing_app)
375 g_free (mux->writing_app);
377 G_OBJECT_CLASS (parent_class)->finalize (object);
382 * gst_matroska_mux_create_uid:
384 * Generate new unused track UID.
386 * Returns: New track UID.
389 gst_matroska_mux_create_uid (void)
396 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
401 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
402 for (i = 0; i < used_uids->len; i++) {
403 if (g_array_index (used_uids, guint64, i) == uid) {
408 g_array_append_val (used_uids, uid);
411 G_UNLOCK (used_uids);
417 * gst_matroska_pad_reset:
418 * @collect_pad: the #GstMatroskaPad
420 * Reset and/or release resources of a matroska collect pad.
423 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
426 GstMatroskaTrackType type = 0;
428 /* free track information */
429 if (collect_pad->track != NULL) {
430 /* retrieve for optional later use */
431 name = collect_pad->track->name;
432 type = collect_pad->track->type;
433 /* extra for video */
434 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
435 GstMatroskaTrackVideoContext *ctx =
436 (GstMatroskaTrackVideoContext *) collect_pad->track;
438 if (ctx->dirac_unit) {
439 gst_buffer_unref (ctx->dirac_unit);
440 ctx->dirac_unit = NULL;
443 g_free (collect_pad->track->codec_id);
444 g_free (collect_pad->track->codec_name);
446 g_free (collect_pad->track->name);
447 g_free (collect_pad->track->language);
448 g_free (collect_pad->track->codec_priv);
449 g_free (collect_pad->track);
450 collect_pad->track = NULL;
453 /* free cached buffer */
454 if (collect_pad->buffer != NULL) {
455 gst_buffer_unref (collect_pad->buffer);
456 collect_pad->buffer = NULL;
459 if (!full && type != 0) {
460 GstMatroskaTrackContext *context;
462 /* create a fresh context */
464 case GST_MATROSKA_TRACK_TYPE_VIDEO:
465 context = (GstMatroskaTrackContext *)
466 g_new0 (GstMatroskaTrackVideoContext, 1);
468 case GST_MATROSKA_TRACK_TYPE_AUDIO:
469 context = (GstMatroskaTrackContext *)
470 g_new0 (GstMatroskaTrackAudioContext, 1);
472 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
473 context = (GstMatroskaTrackContext *)
474 g_new0 (GstMatroskaTrackSubtitleContext, 1);
477 g_assert_not_reached ();
481 context->type = type;
482 context->name = name;
483 /* TODO: check default values for the context */
484 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
485 collect_pad->track = context;
486 collect_pad->buffer = NULL;
487 collect_pad->duration = 0;
488 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
489 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
494 * gst_matroska_pad_free:
495 * @collect_pad: the #GstMatroskaPad
497 * Release resources of a matroska collect pad.
500 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
502 gst_matroska_pad_reset (collect_pad, TRUE);
507 * gst_matroska_mux_reset:
508 * @element: #GstMatroskaMux that should be reseted.
510 * Reset matroska muxer back to initial state.
513 gst_matroska_mux_reset (GstElement * element)
515 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
518 /* reset EBML write */
519 gst_ebml_write_reset (mux->ebml_write);
522 mux->state = GST_MATROSKA_MUX_STATE_START;
524 /* clean up existing streams */
526 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
527 GstMatroskaPad *collect_pad;
529 collect_pad = (GstMatroskaPad *) walk->data;
531 /* reset collect pad to pristine state */
532 gst_matroska_pad_reset (collect_pad, FALSE);
536 mux->num_indexes = 0;
541 mux->time_scale = GST_MSECOND;
546 mux->cluster_time = 0;
547 mux->cluster_pos = 0;
548 mux->prev_cluster_size = 0;
551 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
555 * gst_matroska_mux_handle_src_event:
556 * @pad: Pad which received the event.
557 * @event: Received event.
559 * handle events - copied from oggmux without understanding
561 * Returns: #TRUE on success.
564 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
568 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
572 /* disable seeking for now */
578 return gst_pad_event_default (pad, event);
582 * gst_matroska_mux_handle_sink_event:
583 * @pad: Pad which received the event.
584 * @event: Received event.
586 * handle events - informational ones like tags
588 * Returns: #TRUE on success.
591 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
593 GstMatroskaTrackContext *context;
594 GstMatroskaPad *collect_pad;
599 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
601 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
602 switch (GST_EVENT_TYPE (event)) {
606 GST_DEBUG_OBJECT (mux, "received tag event");
607 gst_event_parse_tag (event, &list);
609 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
610 g_assert (collect_pad);
611 context = collect_pad->track;
614 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
615 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
616 const gchar *lang_code;
618 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
620 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
621 context->language = g_strdup (lang_code);
623 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
628 /* FIXME: remove locking again after GstTagSetter has been fixed */
629 GST_OBJECT_LOCK (mux);
630 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
631 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
632 GST_OBJECT_UNLOCK (mux);
635 case GST_EVENT_NEWSEGMENT:
636 /* We don't support NEWSEGMENT events */
638 gst_event_unref (event);
644 /* now GstCollectPads can take care of the rest, e.g. EOS */
646 ret = mux->collect_event (pad, event);
647 gst_object_unref (mux);
654 * gst_matroska_mux_video_pad_setcaps:
655 * @pad: Pad which got the caps.
658 * Setcaps function for video sink pad.
660 * Returns: #TRUE on success.
663 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
665 GstMatroskaTrackContext *context = NULL;
666 GstMatroskaTrackVideoContext *videocontext;
668 GstMatroskaPad *collect_pad;
669 GstStructure *structure;
670 const gchar *mimetype;
671 const GValue *value = NULL;
672 const GstBuffer *codec_buf = NULL;
673 gint width, height, pixel_width, pixel_height;
675 gboolean interlaced = FALSE;
677 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
680 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
681 g_assert (collect_pad);
682 context = collect_pad->track;
684 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
685 videocontext = (GstMatroskaTrackVideoContext *) context;
687 /* gst -> matroska ID'ing */
688 structure = gst_caps_get_structure (caps, 0);
690 mimetype = gst_structure_get_name (structure);
692 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
694 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
696 if (!strcmp (mimetype, "video/x-theora")) {
697 /* we'll extract the details later from the theora identification header */
701 /* get general properties */
702 /* spec says it is mandatory */
703 if (!gst_structure_get_int (structure, "width", &width) ||
704 !gst_structure_get_int (structure, "height", &height))
707 videocontext->pixel_width = width;
708 videocontext->pixel_height = height;
709 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
711 context->default_duration =
712 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
713 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
714 GST_TIME_ARGS (context->default_duration));
716 context->default_duration = 0;
718 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
719 &pixel_width, &pixel_height)) {
720 if (pixel_width > pixel_height) {
721 videocontext->display_width = width * pixel_width / pixel_height;
722 videocontext->display_height = height;
723 } else if (pixel_width < pixel_height) {
724 videocontext->display_width = width;
725 videocontext->display_height = height * pixel_height / pixel_width;
727 videocontext->display_width = 0;
728 videocontext->display_height = 0;
731 videocontext->display_width = 0;
732 videocontext->display_height = 0;
737 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
738 videocontext->fourcc = 0;
740 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
741 * data and other settings
745 /* extract codec_data, may turn out needed */
746 value = gst_structure_get_value (structure, "codec_data");
748 codec_buf = gst_value_get_buffer (value);
751 if (!strcmp (mimetype, "video/x-raw-yuv")) {
752 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
753 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
754 } else if (!strcmp (mimetype, "image/jpeg")) {
755 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
756 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
757 ||!strcmp (mimetype, "video/x-huffyuv")
758 || !strcmp (mimetype, "video/x-divx")
759 || !strcmp (mimetype, "video/x-dv")
760 || !strcmp (mimetype, "video/x-h263")
761 || !strcmp (mimetype, "video/x-msmpeg")
762 || !strcmp (mimetype, "video/x-wmv")) {
763 gst_riff_strf_vids *bih;
764 gint size = sizeof (gst_riff_strf_vids);
767 if (!strcmp (mimetype, "video/x-xvid"))
768 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
769 else if (!strcmp (mimetype, "video/x-huffyuv"))
770 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
771 else if (!strcmp (mimetype, "video/x-dv"))
772 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
773 else if (!strcmp (mimetype, "video/x-h263"))
774 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
775 else if (!strcmp (mimetype, "video/x-divx")) {
778 gst_structure_get_int (structure, "divxversion", &divxversion);
779 switch (divxversion) {
781 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
784 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
787 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
790 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
793 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
794 switch (msmpegversion) {
796 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
799 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
805 } else if (!strcmp (mimetype, "video/x-wmv")) {
808 if (gst_structure_get_fourcc (structure, "format", &format)) {
810 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
811 if (wmvversion == 2) {
812 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
813 } else if (wmvversion == 1) {
814 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
815 } else if (wmvversion == 3) {
816 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
824 bih = g_new0 (gst_riff_strf_vids, 1);
825 GST_WRITE_UINT32_LE (&bih->size, size);
826 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
827 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
828 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
829 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
830 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
831 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
832 videocontext->pixel_height * 3);
834 /* process codec private/initialization data, if any */
836 size += GST_BUFFER_SIZE (codec_buf);
837 bih = g_realloc (bih, size);
838 GST_WRITE_UINT32_LE (&bih->size, size);
839 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
840 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
843 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
844 context->codec_priv = (gpointer) bih;
845 context->codec_priv_size = size;
846 } else if (!strcmp (mimetype, "video/x-h264")) {
847 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
849 if (context->codec_priv != NULL) {
850 g_free (context->codec_priv);
851 context->codec_priv = NULL;
852 context->codec_priv_size = 0;
855 /* Create avcC header */
856 if (codec_buf != NULL) {
857 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
858 context->codec_priv = g_malloc0 (context->codec_priv_size);
859 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
860 context->codec_priv_size);
862 } else if (!strcmp (mimetype, "video/x-theora")) {
863 const GValue *streamheader;
865 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
867 if (context->codec_priv != NULL) {
868 g_free (context->codec_priv);
869 context->codec_priv = NULL;
870 context->codec_priv_size = 0;
873 streamheader = gst_structure_get_value (structure, "streamheader");
874 if (!theora_streamheader_to_codecdata (streamheader, context)) {
875 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
876 ("theora stream headers missing or malformed"));
879 } else if (!strcmp (mimetype, "video/x-dirac")) {
880 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
881 } else if (!strcmp (mimetype, "video/x-vp8")) {
882 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
883 } else if (!strcmp (mimetype, "video/mpeg")) {
886 gst_structure_get_int (structure, "mpegversion", &mpegversion);
887 switch (mpegversion) {
889 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
895 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
901 /* global headers may be in codec data */
902 if (codec_buf != NULL) {
903 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
904 context->codec_priv = g_malloc0 (context->codec_priv_size);
905 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
906 context->codec_priv_size);
908 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
910 /* can only make it here if preceding case verified it was version 3 */
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
912 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
914 const GValue *mdpr_data;
916 gst_structure_get_int (structure, "rmversion", &rmversion);
919 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
922 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
925 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
928 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
934 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
935 if (mdpr_data != NULL) {
936 guint8 *priv_data = NULL;
937 guint priv_data_size = 0;
939 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
941 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
942 priv_data = g_malloc0 (priv_data_size);
944 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
946 context->codec_priv = priv_data;
947 context->codec_priv_size = priv_data_size;
956 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
957 GST_PAD_NAME (pad), caps);
962 /* N > 0 to expect a particular number of headers, negative if the
963 number of headers is variable */
965 xiphN_streamheader_to_codecdata (const GValue * streamheader,
966 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
968 GstBuffer **buf = NULL;
971 guint bufi, i, offset, priv_data_size;
973 if (streamheader == NULL)
974 goto no_stream_headers;
976 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
979 bufarr = g_value_peek_pointer (streamheader);
980 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
982 if (N > 0 && bufarr->len != N)
985 context->xiph_headers_to_skip = bufarr->len;
987 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
988 for (i = 0; i < bufarr->len; i++) {
989 GValue *bufval = &g_array_index (bufarr, GValue, i);
991 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
993 goto wrong_content_type;
996 buf[i] = g_value_peek_pointer (bufval);
1000 if (bufarr->len > 0) {
1001 for (i = 0; i < bufarr->len - 1; i++) {
1002 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1006 for (i = 0; i < bufarr->len; ++i) {
1007 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1010 priv_data = g_malloc0 (priv_data_size);
1012 priv_data[0] = bufarr->len - 1;
1015 if (bufarr->len > 0) {
1016 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1017 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1018 priv_data[offset++] = 0xff;
1020 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1024 for (i = 0; i < bufarr->len; ++i) {
1025 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1026 GST_BUFFER_SIZE (buf[i]));
1027 offset += GST_BUFFER_SIZE (buf[i]);
1030 context->codec_priv = priv_data;
1031 context->codec_priv_size = priv_data_size;
1034 *p_buf0 = gst_buffer_ref (buf[0]);
1043 GST_WARNING ("required streamheaders missing in sink caps!");
1048 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1049 G_VALUE_TYPE_NAME (streamheader));
1054 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1059 GST_WARNING ("streamheaders array does not contain GstBuffers");
1065 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1066 GstMatroskaTrackContext * context)
1068 GstBuffer *buf0 = NULL;
1070 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1073 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1074 GST_WARNING ("First vorbis header too small, ignoring");
1076 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1077 GstMatroskaTrackAudioContext *audiocontext;
1080 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1081 audiocontext = (GstMatroskaTrackAudioContext *) context;
1082 audiocontext->channels = GST_READ_UINT8 (hdr);
1083 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1088 gst_buffer_unref (buf0);
1094 theora_streamheader_to_codecdata (const GValue * streamheader,
1095 GstMatroskaTrackContext * context)
1097 GstBuffer *buf0 = NULL;
1099 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1102 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1103 GST_WARNING ("First theora header too small, ignoring");
1104 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1105 GST_WARNING ("First header not a theora identification header, ignoring");
1107 GstMatroskaTrackVideoContext *videocontext;
1108 guint fps_num, fps_denom, par_num, par_denom;
1111 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1113 videocontext = (GstMatroskaTrackVideoContext *) context;
1114 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1115 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1116 hdr += 3 + 3 + 1 + 1;
1117 fps_num = GST_READ_UINT32_BE (hdr);
1118 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1119 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1120 fps_denom, fps_num);
1122 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1123 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1124 if (par_num > 0 && par_num > 0) {
1125 if (par_num > par_denom) {
1126 videocontext->display_width =
1127 videocontext->pixel_width * par_num / par_denom;
1128 videocontext->display_height = videocontext->pixel_height;
1129 } else if (par_num < par_denom) {
1130 videocontext->display_width = videocontext->pixel_width;
1131 videocontext->display_height =
1132 videocontext->pixel_height * par_denom / par_num;
1134 videocontext->display_width = 0;
1135 videocontext->display_height = 0;
1138 videocontext->display_width = 0;
1139 videocontext->display_height = 0;
1145 gst_buffer_unref (buf0);
1151 kate_streamheader_to_codecdata (const GValue * streamheader,
1152 GstMatroskaTrackContext * context)
1154 GstBuffer *buf0 = NULL;
1156 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1159 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1160 GST_WARNING ("First kate header too small, ignoring");
1161 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1162 GST_WARNING ("First header not a kate identification header, ignoring");
1166 gst_buffer_unref (buf0);
1172 flac_streamheader_to_codecdata (const GValue * streamheader,
1173 GstMatroskaTrackContext * context)
1180 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1181 GST_WARNING ("No or invalid streamheader field in the caps");
1185 bufarr = g_value_peek_pointer (streamheader);
1186 if (bufarr->len < 2) {
1187 GST_WARNING ("Too few headers in streamheader field");
1191 context->xiph_headers_to_skip = bufarr->len + 1;
1193 bufval = &g_array_index (bufarr, GValue, 0);
1194 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1195 GST_WARNING ("streamheaders array does not contain GstBuffers");
1199 buffer = g_value_peek_pointer (bufval);
1201 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1202 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1203 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1204 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1205 GST_WARNING ("Invalid streamheader for FLAC");
1209 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1210 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1211 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1212 GST_BUFFER_SIZE (buffer) - 9);
1214 for (i = 1; i < bufarr->len; i++) {
1215 bufval = &g_array_index (bufarr, GValue, i);
1217 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1218 g_free (context->codec_priv);
1219 context->codec_priv = NULL;
1220 context->codec_priv_size = 0;
1221 GST_WARNING ("streamheaders array does not contain GstBuffers");
1225 buffer = g_value_peek_pointer (bufval);
1227 context->codec_priv =
1228 g_realloc (context->codec_priv,
1229 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1230 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1231 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1232 context->codec_priv_size =
1233 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1240 speex_streamheader_to_codecdata (const GValue * streamheader,
1241 GstMatroskaTrackContext * context)
1247 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1248 GST_WARNING ("No or invalid streamheader field in the caps");
1252 bufarr = g_value_peek_pointer (streamheader);
1253 if (bufarr->len != 2) {
1254 GST_WARNING ("Too few headers in streamheader field");
1258 context->xiph_headers_to_skip = bufarr->len + 1;
1260 bufval = &g_array_index (bufarr, GValue, 0);
1261 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1262 GST_WARNING ("streamheaders array does not contain GstBuffers");
1266 buffer = g_value_peek_pointer (bufval);
1268 if (GST_BUFFER_SIZE (buffer) < 80
1269 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1270 GST_WARNING ("Invalid streamheader for Speex");
1274 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1275 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1276 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1277 GST_BUFFER_SIZE (buffer));
1279 bufval = &g_array_index (bufarr, GValue, 1);
1281 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1282 g_free (context->codec_priv);
1283 context->codec_priv = NULL;
1284 context->codec_priv_size = 0;
1285 GST_WARNING ("streamheaders array does not contain GstBuffers");
1289 buffer = g_value_peek_pointer (bufval);
1291 context->codec_priv =
1292 g_realloc (context->codec_priv,
1293 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1294 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1295 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1296 context->codec_priv_size =
1297 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1302 static const gchar *
1303 aac_codec_data_to_codec_id (const GstBuffer * buf)
1305 const gchar *result;
1308 /* default to MAIN */
1311 if (GST_BUFFER_SIZE (buf) >= 2) {
1312 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1330 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1339 * gst_matroska_mux_audio_pad_setcaps:
1340 * @pad: Pad which got the caps.
1343 * Setcaps function for audio sink pad.
1345 * Returns: #TRUE on success.
1348 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1350 GstMatroskaTrackContext *context = NULL;
1351 GstMatroskaTrackAudioContext *audiocontext;
1352 GstMatroskaMux *mux;
1353 GstMatroskaPad *collect_pad;
1354 const gchar *mimetype;
1355 gint samplerate = 0, channels = 0;
1356 GstStructure *structure;
1357 const GValue *codec_data = NULL;
1358 const GstBuffer *buf = NULL;
1359 const gchar *stream_format = NULL;
1361 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1364 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1365 g_assert (collect_pad);
1366 context = collect_pad->track;
1368 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1369 audiocontext = (GstMatroskaTrackAudioContext *) context;
1371 structure = gst_caps_get_structure (caps, 0);
1372 mimetype = gst_structure_get_name (structure);
1375 gst_structure_get_int (structure, "rate", &samplerate);
1376 gst_structure_get_int (structure, "channels", &channels);
1378 audiocontext->samplerate = samplerate;
1379 audiocontext->channels = channels;
1380 audiocontext->bitdepth = 0;
1381 context->default_duration = 0;
1383 codec_data = gst_structure_get_value (structure, "codec_data");
1385 buf = gst_value_get_buffer (codec_data);
1387 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1388 * data and other settings
1392 if (!strcmp (mimetype, "audio/mpeg")) {
1393 gint mpegversion = 0;
1395 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1396 switch (mpegversion) {
1402 gst_structure_get_int (structure, "layer", &layer);
1404 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1405 GST_WARNING_OBJECT (mux,
1406 "Unable to determine MPEG audio version, assuming 1");
1412 else if (layer == 2)
1414 else if (version == 2)
1419 context->default_duration =
1420 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1424 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1427 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1430 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1439 stream_format = gst_structure_get_string (structure, "stream-format");
1440 /* check this is raw aac */
1441 if (stream_format) {
1442 if (strcmp (stream_format, "raw") != 0) {
1443 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1447 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1452 if (mpegversion == 2)
1454 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1455 aac_codec_data_to_codec_id (buf));
1456 else if (mpegversion == 4)
1458 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1459 aac_codec_data_to_codec_id (buf));
1461 g_assert_not_reached ();
1463 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1470 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1472 gint endianness = G_LITTLE_ENDIAN;
1473 gboolean signedness = TRUE;
1475 if (!gst_structure_get_int (structure, "width", &width) ||
1476 !gst_structure_get_int (structure, "depth", &depth) ||
1477 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1478 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1483 !gst_structure_get_int (structure, "endianness", &endianness)) {
1484 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1488 if (width != depth) {
1489 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1493 /* FIXME: where is this spec'ed out? (tpm) */
1494 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1495 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1499 audiocontext->bitdepth = depth;
1500 if (endianness == G_BIG_ENDIAN)
1501 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1503 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1505 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1508 if (!gst_structure_get_int (structure, "width", &width)) {
1509 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1513 audiocontext->bitdepth = width;
1514 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1516 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1517 const GValue *streamheader;
1519 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1521 if (context->codec_priv != NULL) {
1522 g_free (context->codec_priv);
1523 context->codec_priv = NULL;
1524 context->codec_priv_size = 0;
1527 streamheader = gst_structure_get_value (structure, "streamheader");
1528 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1529 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1530 ("vorbis stream headers missing or malformed"));
1533 } else if (!strcmp (mimetype, "audio/x-flac")) {
1534 const GValue *streamheader;
1536 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1537 if (context->codec_priv != NULL) {
1538 g_free (context->codec_priv);
1539 context->codec_priv = NULL;
1540 context->codec_priv_size = 0;
1543 streamheader = gst_structure_get_value (structure, "streamheader");
1544 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1545 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1546 ("flac stream headers missing or malformed"));
1549 } else if (!strcmp (mimetype, "audio/x-speex")) {
1550 const GValue *streamheader;
1552 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1553 if (context->codec_priv != NULL) {
1554 g_free (context->codec_priv);
1555 context->codec_priv = NULL;
1556 context->codec_priv_size = 0;
1559 streamheader = gst_structure_get_value (structure, "streamheader");
1560 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1561 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1562 ("speex stream headers missing or malformed"));
1565 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1566 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1567 } else if (!strcmp (mimetype, "audio/x-tta")) {
1570 /* TTA frame duration */
1571 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1573 gst_structure_get_int (structure, "width", &width);
1574 audiocontext->bitdepth = width;
1575 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1577 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1579 const GValue *mdpr_data;
1581 gst_structure_get_int (structure, "raversion", &raversion);
1582 switch (raversion) {
1584 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1590 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1596 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1597 if (mdpr_data != NULL) {
1598 guint8 *priv_data = NULL;
1599 guint priv_data_size = 0;
1601 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1603 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1604 priv_data = g_malloc0 (priv_data_size);
1606 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1608 context->codec_priv = priv_data;
1609 context->codec_priv_size = priv_data_size;
1612 } else if (!strcmp (mimetype, "audio/x-wma")) {
1614 guint codec_priv_size;
1621 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1622 || !gst_structure_get_int (structure, "block_align", &block_align)
1623 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1624 || samplerate == 0 || channels == 0) {
1625 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1626 "channels/rate on WMA caps");
1630 switch (wmaversion) {
1632 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1635 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1638 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1641 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1645 if (gst_structure_get_int (structure, "depth", &depth))
1646 audiocontext->bitdepth = depth;
1648 codec_priv_size = WAVEFORMATEX_SIZE;
1650 codec_priv_size += GST_BUFFER_SIZE (buf);
1652 /* serialize waveformatex structure */
1653 codec_priv = g_malloc0 (codec_priv_size);
1654 GST_WRITE_UINT16_LE (codec_priv, format);
1655 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1656 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1657 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1658 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1659 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1661 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1663 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1665 /* process codec private/initialization data, if any */
1667 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1668 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1671 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1672 context->codec_priv = (gpointer) codec_priv;
1673 context->codec_priv_size = codec_priv_size;
1681 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1682 GST_PAD_NAME (pad), caps);
1689 * gst_matroska_mux_subtitle_pad_setcaps:
1690 * @pad: Pad which got the caps.
1693 * Setcaps function for subtitle sink pad.
1695 * Returns: #TRUE on success.
1698 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1701 * Consider this as boilerplate code for now. There is
1702 * no single subtitle creation element in GStreamer,
1703 * neither do I know how subtitling works at all. */
1705 /* There is now (at least) one such alement (kateenc), and I'm going
1706 to handle it here and claim it works when it can be piped back
1707 through GStreamer and VLC */
1709 GstMatroskaTrackContext *context = NULL;
1710 GstMatroskaTrackSubtitleContext *scontext;
1711 GstMatroskaMux *mux;
1712 GstMatroskaPad *collect_pad;
1713 const gchar *mimetype;
1714 GstStructure *structure;
1716 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1719 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1720 g_assert (collect_pad);
1721 context = collect_pad->track;
1723 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1724 scontext = (GstMatroskaTrackSubtitleContext *) context;
1726 structure = gst_caps_get_structure (caps, 0);
1727 mimetype = gst_structure_get_name (structure);
1730 scontext->check_utf8 = 1;
1731 scontext->invalid_utf8 = 0;
1732 context->default_duration = 0;
1734 /* TODO: - other format than Kate */
1736 if (!strcmp (mimetype, "subtitle/x-kate")) {
1737 const GValue *streamheader;
1739 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1741 if (context->codec_priv != NULL) {
1742 g_free (context->codec_priv);
1743 context->codec_priv = NULL;
1744 context->codec_priv_size = 0;
1747 streamheader = gst_structure_get_value (structure, "streamheader");
1748 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1749 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1750 ("kate stream headers missing or malformed"));
1761 * gst_matroska_mux_request_new_pad:
1762 * @element: #GstMatroskaMux.
1763 * @templ: #GstPadTemplate.
1764 * @pad_name: New pad name.
1766 * Request pad function for sink templates.
1768 * Returns: New #GstPad.
1771 gst_matroska_mux_request_new_pad (GstElement * element,
1772 GstPadTemplate * templ, const gchar * pad_name)
1774 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1775 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1776 GstMatroskaPad *collect_pad;
1777 GstPad *newpad = NULL;
1779 GstPadSetCapsFunction setcapsfunc = NULL;
1780 GstMatroskaTrackContext *context = NULL;
1782 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1783 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1784 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1785 context = (GstMatroskaTrackContext *)
1786 g_new0 (GstMatroskaTrackAudioContext, 1);
1787 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1788 context->name = g_strdup ("Audio");
1789 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1790 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1791 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1792 context = (GstMatroskaTrackContext *)
1793 g_new0 (GstMatroskaTrackVideoContext, 1);
1794 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1795 context->name = g_strdup ("Video");
1796 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1797 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1798 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1799 context = (GstMatroskaTrackContext *)
1800 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1801 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1802 context->name = g_strdup ("Subtitle");
1804 GST_WARNING_OBJECT (mux, "This is not our template!");
1808 newpad = gst_pad_new_from_template (templ, name);
1810 collect_pad = (GstMatroskaPad *)
1811 gst_collect_pads_add_pad_full (mux->collect, newpad,
1812 sizeof (GstMatroskaPad),
1813 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1815 collect_pad->track = context;
1816 gst_matroska_pad_reset (collect_pad, FALSE);
1818 /* FIXME: hacked way to override/extend the event function of
1819 * GstCollectPads; because it sets its own event function giving the
1820 * element no access to events.
1821 * TODO GstCollectPads should really give its 'users' a clean chance to
1822 * properly handle events that are not meant for collectpads itself.
1823 * Perhaps a callback or so, though rejected (?) in #340060.
1824 * This would allow (clean) transcoding of info from demuxer/streams
1825 * to another muxer */
1826 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1827 gst_pad_set_event_function (newpad,
1828 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1830 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1831 gst_pad_set_active (newpad, TRUE);
1832 gst_element_add_pad (element, newpad);
1839 * gst_matroska_mux_release_pad:
1840 * @element: #GstMatroskaMux.
1841 * @pad: Pad to release.
1843 * Release a previously requested pad.
1846 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1848 GstMatroskaMux *mux;
1851 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1853 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1854 GstCollectData *cdata = (GstCollectData *) walk->data;
1855 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1857 if (cdata->pad == pad) {
1858 GstClockTime min_dur; /* observed minimum duration */
1860 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1861 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1862 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1863 if (collect_pad->duration < min_dur)
1864 collect_pad->duration = min_dur;
1867 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1868 mux->duration < collect_pad->duration)
1869 mux->duration = collect_pad->duration;
1875 gst_collect_pads_remove_pad (mux->collect, pad);
1876 if (gst_element_remove_pad (element, pad))
1882 * gst_matroska_mux_track_header:
1883 * @mux: #GstMatroskaMux
1884 * @context: Tack context.
1886 * Write a track header.
1889 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1890 GstMatroskaTrackContext * context)
1892 GstEbmlWrite *ebml = mux->ebml_write;
1895 /* TODO: check if everything necessary is written and check default values */
1897 /* track type goes before the type-specific stuff */
1898 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1899 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1902 gst_matroska_mux_create_uid ());
1903 if (context->default_duration) {
1904 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1905 context->default_duration);
1907 if (context->language) {
1908 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1912 /* type-specific stuff */
1913 switch (context->type) {
1914 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1915 GstMatroskaTrackVideoContext *videocontext =
1916 (GstMatroskaTrackVideoContext *) context;
1918 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1919 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1920 videocontext->pixel_width);
1921 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1922 videocontext->pixel_height);
1923 if (videocontext->display_width && videocontext->display_height) {
1924 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1925 videocontext->display_width);
1926 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1927 videocontext->display_height);
1929 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1930 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1931 if (videocontext->fourcc) {
1932 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1934 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1935 (gpointer) & fcc_le, 4);
1937 gst_ebml_write_master_finish (ebml, master);
1942 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1943 GstMatroskaTrackAudioContext *audiocontext =
1944 (GstMatroskaTrackAudioContext *) context;
1946 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1947 if (audiocontext->samplerate != 8000)
1948 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1949 audiocontext->samplerate);
1950 if (audiocontext->channels != 1)
1951 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1952 audiocontext->channels);
1953 if (audiocontext->bitdepth) {
1954 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1955 audiocontext->bitdepth);
1957 gst_ebml_write_master_finish (ebml, master);
1963 /* doesn't need type-specific data */
1967 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1968 if (context->codec_priv)
1969 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1970 context->codec_priv, context->codec_priv_size);
1971 /* FIXME: until we have a nice way of getting the codecname
1972 * out of the caps, I'm not going to enable this. Too much
1973 * (useless, double, boring) work... */
1974 /* TODO: Use value from tags if any */
1975 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1976 context->codec_name); */
1977 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1982 * gst_matroska_mux_start:
1983 * @mux: #GstMatroskaMux
1985 * Start a new matroska file (write headers etc...)
1988 gst_matroska_mux_start (GstMatroskaMux * mux)
1990 GstEbmlWrite *ebml = mux->ebml_write;
1991 const gchar *doctype;
1992 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1993 GST_MATROSKA_ID_TRACKS,
1994 GST_MATROSKA_ID_CUES,
1995 GST_MATROSKA_ID_TAGS,
1998 guint64 master, child;
2002 GstClockTime duration = 0;
2003 guint32 segment_uid[4];
2004 GTimeVal time = { 0, 0 };
2006 /* we start with a EBML header */
2007 doctype = mux->doctype;
2008 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2009 doctype, mux->doctype_version);
2010 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2012 /* start a segment */
2014 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2015 mux->segment_master = ebml->pos;
2017 /* the rest of the header is cached */
2018 gst_ebml_write_set_cache (ebml, 0x1000);
2020 /* seekhead (table of contents) - we set the positions later */
2021 mux->seekhead_pos = ebml->pos;
2022 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2023 for (i = 0; seekhead_id[i] != 0; i++) {
2024 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2025 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2026 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2027 gst_ebml_write_master_finish (ebml, child);
2029 gst_ebml_write_master_finish (ebml, master);
2032 mux->info_pos = ebml->pos;
2033 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2034 for (i = 0; i < 4; i++) {
2035 segment_uid[i] = g_random_int ();
2037 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2038 (guint8 *) segment_uid, 16);
2039 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2040 mux->duration_pos = ebml->pos;
2042 for (collected = mux->collect->data; collected;
2043 collected = g_slist_next (collected)) {
2044 GstMatroskaPad *collect_pad;
2045 GstFormat format = GST_FORMAT_TIME;
2047 gint64 trackduration;
2049 collect_pad = (GstMatroskaPad *) collected->data;
2050 thepad = collect_pad->collect.pad;
2052 /* Query the total length of the track. */
2053 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2054 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2055 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2056 GST_TIME_ARGS (trackduration));
2057 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2058 duration = (GstClockTime) trackduration;
2062 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2063 gst_guint64_to_gdouble (duration) /
2064 gst_guint64_to_gdouble (mux->time_scale));
2066 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2067 "GStreamer plugin version " PACKAGE_VERSION);
2068 if (mux->writing_app && mux->writing_app[0]) {
2069 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2071 g_get_current_time (&time);
2072 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2073 gst_ebml_write_master_finish (ebml, master);
2076 mux->tracks_pos = ebml->pos;
2077 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2079 for (collected = mux->collect->data; collected;
2080 collected = g_slist_next (collected)) {
2081 GstMatroskaPad *collect_pad;
2084 collect_pad = (GstMatroskaPad *) collected->data;
2085 thepad = collect_pad->collect.pad;
2087 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2088 collect_pad->track->codec_id != 0) {
2089 collect_pad->track->num = tracknum++;
2090 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2091 gst_matroska_mux_track_header (mux, collect_pad->track);
2092 gst_ebml_write_master_finish (ebml, child);
2095 gst_ebml_write_master_finish (ebml, master);
2097 /* lastly, flush the cache */
2098 gst_ebml_write_flush_cache (ebml);
2102 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2105 /* TODO: more sensible tag mappings */
2108 const gchar *matroska_tagname;
2109 const gchar *gstreamer_tagname;
2113 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2114 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2115 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2116 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2117 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2118 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2119 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2120 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2121 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2122 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2123 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2124 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2125 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2126 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2127 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2129 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2131 guint64 simpletag_master;
2133 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2134 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2135 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2137 if (strcmp (tagname_gst, tag) == 0) {
2138 GValue src = { 0, };
2141 if (!gst_tag_list_copy_value (&src, list, tag))
2143 if ((dest = gst_value_serialize (&src))) {
2145 simpletag_master = gst_ebml_write_master_start (ebml,
2146 GST_MATROSKA_ID_SIMPLETAG);
2147 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2148 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2149 gst_ebml_write_master_finish (ebml, simpletag_master);
2152 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2154 g_value_unset (&src);
2162 * gst_matroska_mux_finish:
2163 * @mux: #GstMatroskaMux
2165 * Finish a new matroska file (write index etc...)
2168 gst_matroska_mux_finish (GstMatroskaMux * mux)
2170 GstEbmlWrite *ebml = mux->ebml_write;
2172 guint64 duration = 0;
2174 const GstTagList *tags;
2176 /* finish last cluster */
2178 gst_ebml_write_master_finish (ebml, mux->cluster);
2182 if (mux->index != NULL) {
2184 guint64 master, pointentry_master, trackpos_master;
2186 mux->cues_pos = ebml->pos;
2187 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2188 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2190 for (n = 0; n < mux->num_indexes; n++) {
2191 GstMatroskaIndex *idx = &mux->index[n];
2193 pointentry_master = gst_ebml_write_master_start (ebml,
2194 GST_MATROSKA_ID_POINTENTRY);
2195 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2196 idx->time / mux->time_scale);
2197 trackpos_master = gst_ebml_write_master_start (ebml,
2198 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2199 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2200 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2201 idx->pos - mux->segment_master);
2202 gst_ebml_write_master_finish (ebml, trackpos_master);
2203 gst_ebml_write_master_finish (ebml, pointentry_master);
2206 gst_ebml_write_master_finish (ebml, master);
2207 gst_ebml_write_flush_cache (ebml);
2211 /* FIXME: remove locking again after GstTagSetter has been fixed */
2212 GST_OBJECT_LOCK (mux);
2213 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2214 GST_OBJECT_UNLOCK (mux);
2216 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2217 guint64 master_tags, master_tag;
2219 GST_DEBUG ("Writing tags");
2221 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2222 mux->tags_pos = ebml->pos;
2223 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2224 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2225 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2226 gst_ebml_write_master_finish (ebml, master_tag);
2227 gst_ebml_write_master_finish (ebml, master_tags);
2230 /* update seekhead. We know that:
2231 * - a seekhead contains 4 entries.
2232 * - order of entries is as above.
2233 * - a seekhead has a 4-byte header + 8-byte length
2234 * - each entry is 2-byte master, 2-byte ID pointer,
2235 * 2-byte length pointer, all 8/1-byte length, 4-
2236 * byte ID and 8-byte length pointer, where the
2237 * length pointer starts at 20.
2238 * - all entries are local to the segment (so pos - segment_master).
2239 * - so each entry is at 12 + 20 + num * 28. */
2240 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2241 mux->info_pos - mux->segment_master);
2242 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2243 mux->tracks_pos - mux->segment_master);
2244 if (mux->index != NULL) {
2245 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2246 mux->cues_pos - mux->segment_master);
2249 guint64 my_pos = ebml->pos;
2251 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2252 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2253 gst_ebml_write_seek (ebml, my_pos);
2256 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2257 mux->tags_pos - mux->segment_master);
2260 guint64 my_pos = ebml->pos;
2262 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2263 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2264 gst_ebml_write_seek (ebml, my_pos);
2267 /* update duration */
2268 /* first get the overall duration */
2269 /* a released track may have left a duration in here */
2270 duration = mux->duration;
2271 for (collected = mux->collect->data; collected;
2272 collected = g_slist_next (collected)) {
2273 GstMatroskaPad *collect_pad;
2274 GstClockTime min_duration; /* observed minimum duration */
2276 collect_pad = (GstMatroskaPad *) collected->data;
2278 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2279 " end ts %" GST_TIME_FORMAT, collect_pad,
2280 GST_TIME_ARGS (collect_pad->start_ts),
2281 GST_TIME_ARGS (collect_pad->end_ts));
2283 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2284 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2286 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2287 if (collect_pad->duration < min_duration)
2288 collect_pad->duration = min_duration;
2289 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2290 GST_TIME_ARGS (collect_pad->duration));
2293 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2294 duration < collect_pad->duration)
2295 duration = collect_pad->duration;
2297 if (duration != 0) {
2298 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2299 GST_TIME_ARGS (duration));
2300 pos = mux->ebml_write->pos;
2301 gst_ebml_write_seek (ebml, mux->duration_pos);
2302 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2303 gst_guint64_to_gdouble (duration) /
2304 gst_guint64_to_gdouble (mux->time_scale));
2305 gst_ebml_write_seek (ebml, pos);
2308 guint64 my_pos = ebml->pos;
2310 gst_ebml_write_seek (ebml, mux->duration_pos);
2311 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2312 gst_ebml_write_seek (ebml, my_pos);
2315 /* finish segment - this also writes element length */
2316 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2321 * gst_matroska_mux_best_pad:
2322 * @mux: #GstMatroskaMux
2323 * @popped: True if at least one buffer was popped from #GstCollectPads
2325 * Find a pad with the oldest data
2326 * (data from this pad should be written first).
2328 * Returns: Selected pad.
2330 static GstMatroskaPad *
2331 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2334 GstMatroskaPad *best = NULL;
2337 for (collected = mux->collect->data; collected;
2338 collected = g_slist_next (collected)) {
2339 GstMatroskaPad *collect_pad;
2341 collect_pad = (GstMatroskaPad *) collected->data;
2342 /* fetch a new buffer if needed */
2343 if (collect_pad->buffer == NULL) {
2344 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2345 (GstCollectData *) collect_pad);
2347 if (collect_pad->buffer != NULL)
2351 /* if we have a buffer check if it is better then the current best one */
2352 if (collect_pad->buffer != NULL) {
2353 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2354 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2355 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2356 GST_BUFFER_TIMESTAMP (best->buffer))) {
2366 * gst_matroska_mux_buffer_header:
2367 * @track: Track context.
2368 * @relative_timestamp: relative timestamp of the buffer
2369 * @flags: Buffer flags.
2371 * Create a buffer containing buffer header.
2373 * Returns: New buffer.
2376 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2377 gint16 relative_timestamp, int flags)
2381 hdr = gst_buffer_new_and_alloc (4);
2382 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2383 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2384 /* time relative to clustertime */
2385 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2388 GST_BUFFER_DATA (hdr)[3] = flags;
2393 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2394 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2395 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2398 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2399 GstMatroskaPad * collect_pad, GstBuffer * buf)
2401 GstMatroskaTrackVideoContext *ctx =
2402 (GstMatroskaTrackVideoContext *) collect_pad->track;
2403 const guint8 *data = GST_BUFFER_DATA (buf);
2404 guint size = GST_BUFFER_SIZE (buf);
2406 guint32 next_parse_offset;
2407 GstBuffer *ret = NULL;
2408 gboolean is_muxing_unit = FALSE;
2410 if (GST_BUFFER_SIZE (buf) < 13) {
2411 gst_buffer_unref (buf);
2415 /* Check if this buffer contains a picture or end-of-sequence packet */
2416 while (size >= 13) {
2417 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2418 gst_buffer_unref (buf);
2422 parse_code = GST_READ_UINT8 (data + 4);
2423 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2424 if (ctx->dirac_unit) {
2425 gst_buffer_unref (ctx->dirac_unit);
2426 ctx->dirac_unit = NULL;
2428 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2429 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2430 is_muxing_unit = TRUE;
2434 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2436 if (G_UNLIKELY (next_parse_offset == 0))
2439 data += next_parse_offset;
2440 size -= next_parse_offset;
2443 if (ctx->dirac_unit)
2444 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2446 ctx->dirac_unit = gst_buffer_ref (buf);
2448 if (is_muxing_unit) {
2449 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2450 ctx->dirac_unit = NULL;
2451 gst_buffer_copy_metadata (ret, buf,
2452 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2453 GST_BUFFER_COPY_CAPS);
2454 gst_buffer_unref (buf);
2456 gst_buffer_unref (buf);
2464 * gst_matroska_mux_write_data:
2465 * @mux: #GstMatroskaMux
2466 * @collect_pad: #GstMatroskaPad with the data
2468 * Write collected data (called from gst_matroska_mux_collected).
2470 * Returns: Result of the gst_pad_push issued to write the data.
2472 static GstFlowReturn
2473 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2475 GstEbmlWrite *ebml = mux->ebml_write;
2476 GstBuffer *buf, *hdr;
2478 gboolean write_duration;
2479 gint16 relative_timestamp;
2480 gint64 relative_timestamp64;
2481 guint64 block_duration;
2482 gboolean is_video_keyframe = FALSE;
2485 buf = collect_pad->buffer;
2486 collect_pad->buffer = NULL;
2488 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2489 if (collect_pad->track->xiph_headers_to_skip > 0) {
2490 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2491 gst_buffer_unref (buf);
2492 --collect_pad->track->xiph_headers_to_skip;
2496 /* for dirac we have to queue up everything up to a picture unit */
2497 if (collect_pad->track->codec_id != NULL &&
2498 strcmp (collect_pad->track->codec_id,
2499 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2500 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2505 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2506 * this would wreak havoc with time stored in matroska file */
2507 /* TODO: maybe calculate a timestamp by using the previous timestamp
2508 * and default duration */
2509 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2510 GST_WARNING_OBJECT (collect_pad->collect.pad,
2511 "Invalid buffer timestamp; dropping buffer");
2512 gst_buffer_unref (buf);
2516 /* set the timestamp for outgoing buffers */
2517 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2519 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2520 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2521 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2522 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2523 is_video_keyframe = TRUE;
2527 /* start a new cluster every two seconds or at keyframe */
2528 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2529 || is_video_keyframe) {
2531 gst_ebml_write_master_finish (ebml, mux->cluster);
2532 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2533 mux->cluster_pos = ebml->pos;
2535 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2536 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2537 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2538 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2539 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2540 mux->prev_cluster_size);
2545 mux->cluster_pos = ebml->pos;
2546 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2547 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2548 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2549 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2552 /* update duration of this track */
2553 if (GST_BUFFER_DURATION_IS_VALID (buf))
2554 collect_pad->duration += GST_BUFFER_DURATION (buf);
2556 /* We currently write index entries for all video tracks or for the audio
2557 * track in a single-track audio file. This could be improved by keeping the
2558 * index only for the *first* video track. */
2560 /* TODO: index is useful for every track, should contain the number of
2561 * the block in the cluster which contains the timestamp, should also work
2562 * for files with multiple audio tracks.
2564 if (is_video_keyframe ||
2565 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2566 (mux->num_streams == 1))) {
2569 if (mux->min_index_interval != 0) {
2570 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2571 if (mux->index[last_idx].track == collect_pad->track->num)
2576 if (last_idx < 0 || mux->min_index_interval == 0 ||
2577 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2578 >= mux->min_index_interval)) {
2579 GstMatroskaIndex *idx;
2581 if (mux->num_indexes % 32 == 0) {
2582 mux->index = g_renew (GstMatroskaIndex, mux->index,
2583 mux->num_indexes + 32);
2585 idx = &mux->index[mux->num_indexes++];
2587 idx->pos = mux->cluster_pos;
2588 idx->time = GST_BUFFER_TIMESTAMP (buf);
2589 idx->track = collect_pad->track->num;
2593 /* Check if the duration differs from the default duration. */
2594 write_duration = FALSE;
2595 block_duration = GST_BUFFER_DURATION (buf);
2596 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2597 if (block_duration != collect_pad->track->default_duration) {
2598 write_duration = TRUE;
2602 /* write the block, for doctype v2 use SimpleBlock if possible
2603 * one slice (*breath*).
2604 * FIXME: Need to do correct lacing! */
2605 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2606 if (relative_timestamp64 >= 0) {
2607 /* round the timestamp */
2608 relative_timestamp64 += mux->time_scale / 2;
2610 /* round the timestamp */
2611 relative_timestamp64 -= mux->time_scale / 2;
2613 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2614 if (mux->doctype_version > 1 && !write_duration) {
2616 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2619 gst_matroska_mux_create_buffer_header (collect_pad->track,
2620 relative_timestamp, flags);
2621 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2622 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2623 gst_ebml_write_buffer (ebml, hdr);
2624 gst_ebml_write_buffer (ebml, buf);
2626 return gst_ebml_last_write_result (ebml);
2628 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2630 gst_matroska_mux_create_buffer_header (collect_pad->track,
2631 relative_timestamp, 0);
2632 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2633 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2634 gst_ebml_write_buffer (ebml, hdr);
2635 gst_ebml_write_buffer (ebml, buf);
2636 if (write_duration) {
2637 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2638 block_duration / mux->time_scale);
2640 gst_ebml_write_master_finish (ebml, blockgroup);
2641 return gst_ebml_last_write_result (ebml);
2647 * gst_matroska_mux_collected:
2648 * @pads: #GstCollectPads
2649 * @uuser_data: #GstMatroskaMux
2651 * Collectpads callback.
2653 * Returns: #GstFlowReturn
2655 static GstFlowReturn
2656 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2658 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2659 GstMatroskaPad *best;
2663 GST_DEBUG_OBJECT (mux, "Collected pads");
2665 /* start with a header */
2666 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2667 if (mux->collect->data == NULL) {
2668 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2669 ("No input streams configured"));
2670 return GST_FLOW_ERROR;
2672 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2673 gst_matroska_mux_start (mux);
2674 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2678 /* which stream to write from? */
2679 best = gst_matroska_mux_best_pad (mux, &popped);
2681 /* if there is no best pad, we have reached EOS */
2683 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2684 gst_matroska_mux_finish (mux);
2685 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2686 ret = GST_FLOW_UNEXPECTED;
2689 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2690 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2691 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2692 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2694 /* make note of first and last encountered timestamps, so we can calculate
2695 * the actual duration later when we send an updated header on eos */
2696 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2697 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2698 GstClockTime end_ts = start_ts;
2700 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2701 end_ts += GST_BUFFER_DURATION (best->buffer);
2702 else if (best->track->default_duration)
2703 end_ts += best->track->default_duration;
2705 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2706 best->end_ts = end_ts;
2708 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2709 start_ts < best->start_ts))
2710 best->start_ts = start_ts;
2713 /* write one buffer */
2714 ret = gst_matroska_mux_write_data (mux, best);
2715 } while (ret == GST_FLOW_OK && !popped);
2722 * gst_matroska_mux_change_state:
2723 * @element: #GstMatroskaMux
2724 * @transition: State change transition.
2726 * Change the muxer state.
2728 * Returns: #GstStateChangeReturn
2730 static GstStateChangeReturn
2731 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2733 GstStateChangeReturn ret;
2734 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2736 switch (transition) {
2737 case GST_STATE_CHANGE_NULL_TO_READY:
2739 case GST_STATE_CHANGE_READY_TO_PAUSED:
2740 gst_collect_pads_start (mux->collect);
2742 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2744 case GST_STATE_CHANGE_PAUSED_TO_READY:
2745 gst_collect_pads_stop (mux->collect);
2751 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2753 switch (transition) {
2754 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2756 case GST_STATE_CHANGE_PAUSED_TO_READY:
2757 gst_matroska_mux_reset (GST_ELEMENT (mux));
2759 case GST_STATE_CHANGE_READY_TO_NULL:
2769 gst_matroska_mux_set_property (GObject * object,
2770 guint prop_id, const GValue * value, GParamSpec * pspec)
2772 GstMatroskaMux *mux;
2774 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2775 mux = GST_MATROSKA_MUX (object);
2778 case ARG_WRITING_APP:
2779 if (!g_value_get_string (value)) {
2780 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2783 g_free (mux->writing_app);
2784 mux->writing_app = g_value_dup_string (value);
2786 case ARG_DOCTYPE_VERSION:
2787 mux->doctype_version = g_value_get_int (value);
2789 case ARG_MIN_INDEX_INTERVAL:
2790 mux->min_index_interval = g_value_get_int64 (value);
2793 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2799 gst_matroska_mux_get_property (GObject * object,
2800 guint prop_id, GValue * value, GParamSpec * pspec)
2802 GstMatroskaMux *mux;
2804 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2805 mux = GST_MATROSKA_MUX (object);
2808 case ARG_WRITING_APP:
2809 g_value_set_string (value, mux->writing_app);
2811 case ARG_DOCTYPE_VERSION:
2812 g_value_set_int (value, mux->doctype_version);
2814 case ARG_MIN_INDEX_INTERVAL:
2815 g_value_set_int64 (value, mux->min_index_interval);
2818 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);