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,
69 #define DEFAULT_DOCTYPE_VERSION 2
70 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
71 #define DEFAULT_MIN_INDEX_INTERVAL 0
72 #define DEFAULT_IS_LIVE FALSE
74 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
75 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
77 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
80 GST_STATIC_CAPS ("video/x-matroska")
83 #define COMMON_VIDEO_CAPS \
84 "width = (int) [ 16, 4096 ], " \
85 "height = (int) [ 16, 4096 ], " \
86 "framerate = (fraction) [ 0, MAX ]"
88 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
89 "width = (int) [ 16, 4096 ], " \
90 "height = (int) [ 16, 4096 ] "
93 * * require codec data, etc as needed
96 static GstStaticPadTemplate videosink_templ =
97 GST_STATIC_PAD_TEMPLATE ("video_%d",
100 GST_STATIC_CAPS ("video/mpeg, "
101 "mpegversion = (int) { 1, 2, 4 }, "
102 "systemstream = (boolean) false, "
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 "; "
119 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
122 COMMON_VIDEO_CAPS "; "
123 "video/x-pn-realvideo, "
124 "rmversion = (int) [1, 4], "
125 COMMON_VIDEO_CAPS "; "
127 COMMON_VIDEO_CAPS "; "
129 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
130 COMMON_VIDEO_CAPS "; "
131 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
134 #define COMMON_AUDIO_CAPS \
135 "channels = (int) [ 1, MAX ], " \
136 "rate = (int) [ 1, MAX ]"
139 * * require codec data, etc as needed
141 static GstStaticPadTemplate audiosink_templ =
142 GST_STATIC_PAD_TEMPLATE ("audio_%d",
145 GST_STATIC_CAPS ("audio/mpeg, "
146 "mpegversion = (int) 1, "
147 "layer = (int) [ 1, 3 ], "
148 "stream-format = (string) { raw }, "
149 COMMON_AUDIO_CAPS "; "
151 "mpegversion = (int) { 2, 4 }, "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
160 COMMON_AUDIO_CAPS "; "
164 "signed = (boolean) false, "
165 COMMON_AUDIO_CAPS ";"
169 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
170 "signed = (boolean) true, "
171 COMMON_AUDIO_CAPS ";"
175 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
176 "signed = (boolean) true, "
177 COMMON_AUDIO_CAPS ";"
181 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
182 "signed = (boolean) true, "
183 COMMON_AUDIO_CAPS ";"
184 "audio/x-raw-float, "
185 "width = (int) [ 32, 64 ], "
186 "endianness = (int) LITTLE_ENDIAN, "
187 COMMON_AUDIO_CAPS ";"
189 "width = (int) { 8, 16, 24 }, "
190 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
191 "audio/x-pn-realaudio, "
192 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
193 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
194 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
198 static GstStaticPadTemplate subtitlesink_templ =
199 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
202 GST_STATIC_CAPS_ANY);
204 static GArray *used_uids;
205 G_LOCK_DEFINE_STATIC (used_uids);
207 static void gst_matroska_mux_add_interfaces (GType type);
209 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
210 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
212 /* Matroska muxer destructor */
213 static void gst_matroska_mux_finalize (GObject * object);
215 /* Pads collected callback */
217 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
220 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
222 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
223 GstPadTemplate * templ, const gchar * name);
224 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
226 /* gst internal change state handler */
227 static GstStateChangeReturn
228 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
230 /* gobject bla bla */
231 static void gst_matroska_mux_set_property (GObject * object,
232 guint prop_id, const GValue * value, GParamSpec * pspec);
233 static void gst_matroska_mux_get_property (GObject * object,
234 guint prop_id, GValue * value, GParamSpec * pspec);
237 static void gst_matroska_mux_reset (GstElement * element);
240 static guint64 gst_matroska_mux_create_uid ();
242 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
243 GstMatroskaTrackContext * context);
244 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
245 GstMatroskaTrackContext * context);
246 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
247 GstMatroskaTrackContext * context);
248 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
249 GstMatroskaTrackContext * context);
250 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
251 GstMatroskaTrackContext * context);
254 gst_matroska_mux_add_interfaces (GType type)
256 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
258 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
262 gst_matroska_mux_base_init (gpointer g_class)
267 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
269 GObjectClass *gobject_class;
270 GstElementClass *gstelement_class;
272 gobject_class = (GObjectClass *) klass;
273 gstelement_class = (GstElementClass *) klass;
275 gst_element_class_add_pad_template (gstelement_class,
276 gst_static_pad_template_get (&videosink_templ));
277 gst_element_class_add_pad_template (gstelement_class,
278 gst_static_pad_template_get (&audiosink_templ));
279 gst_element_class_add_pad_template (gstelement_class,
280 gst_static_pad_template_get (&subtitlesink_templ));
281 gst_element_class_add_pad_template (gstelement_class,
282 gst_static_pad_template_get (&src_templ));
283 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
285 "Muxes video/audio/subtitle streams into a matroska stream",
286 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
288 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
291 gobject_class->finalize = gst_matroska_mux_finalize;
293 gobject_class->get_property = gst_matroska_mux_get_property;
294 gobject_class->set_property = gst_matroska_mux_set_property;
296 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
297 g_param_spec_string ("writing-app", "Writing application.",
298 "The name the application that creates the matroska file.",
299 NULL, G_PARAM_READWRITE));
300 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
301 g_param_spec_int ("version", "DocType version",
302 "This parameter determines what Matroska features can be used.",
303 1, 2, DEFAULT_DOCTYPE_VERSION, G_PARAM_READWRITE));
304 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
305 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
306 "entries", "An index entry is created every so many nanoseconds.",
307 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
308 g_object_class_install_property (gobject_class, ARG_IS_LIVE,
309 g_param_spec_boolean ("is-live", "Is Live",
310 "The stream is live and does not need an index", DEFAULT_IS_LIVE,
311 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
313 gstelement_class->change_state =
314 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
315 gstelement_class->request_new_pad =
316 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
317 gstelement_class->release_pad =
318 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
323 * gst_matroska_mux_init:
324 * @mux: #GstMatroskaMux that should be initialized.
325 * @g_class: Class of the muxer.
327 * Matroska muxer constructor.
330 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
332 GstPadTemplate *templ;
335 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
336 mux->srcpad = gst_pad_new_from_template (templ, "src");
338 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
339 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
341 mux->collect = gst_collect_pads_new ();
342 gst_collect_pads_set_function (mux->collect,
343 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
346 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
347 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
349 /* property defaults */
350 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
351 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
352 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
353 mux->is_live = DEFAULT_IS_LIVE;
355 /* initialize internal variables */
357 mux->num_streams = 0;
358 mux->num_a_streams = 0;
359 mux->num_t_streams = 0;
360 mux->num_v_streams = 0;
362 /* initialize remaining variables */
363 gst_matroska_mux_reset (GST_ELEMENT (mux));
368 * gst_matroska_mux_finalize:
369 * @object: #GstMatroskaMux that should be finalized.
371 * Finalize matroska muxer.
374 gst_matroska_mux_finalize (GObject * object)
376 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
378 gst_object_unref (mux->collect);
379 gst_object_unref (mux->ebml_write);
380 if (mux->writing_app)
381 g_free (mux->writing_app);
383 G_OBJECT_CLASS (parent_class)->finalize (object);
388 * gst_matroska_mux_create_uid:
390 * Generate new unused track UID.
392 * Returns: New track UID.
395 gst_matroska_mux_create_uid (void)
402 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
407 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
408 for (i = 0; i < used_uids->len; i++) {
409 if (g_array_index (used_uids, guint64, i) == uid) {
414 g_array_append_val (used_uids, uid);
417 G_UNLOCK (used_uids);
423 * gst_matroska_pad_reset:
424 * @collect_pad: the #GstMatroskaPad
426 * Reset and/or release resources of a matroska collect pad.
429 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
432 GstMatroskaTrackType type = 0;
434 /* free track information */
435 if (collect_pad->track != NULL) {
436 /* retrieve for optional later use */
437 name = collect_pad->track->name;
438 type = collect_pad->track->type;
439 /* extra for video */
440 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
441 GstMatroskaTrackVideoContext *ctx =
442 (GstMatroskaTrackVideoContext *) collect_pad->track;
444 if (ctx->dirac_unit) {
445 gst_buffer_unref (ctx->dirac_unit);
446 ctx->dirac_unit = NULL;
449 g_free (collect_pad->track->codec_id);
450 g_free (collect_pad->track->codec_name);
452 g_free (collect_pad->track->name);
453 g_free (collect_pad->track->language);
454 g_free (collect_pad->track->codec_priv);
455 g_free (collect_pad->track);
456 collect_pad->track = NULL;
459 /* free cached buffer */
460 if (collect_pad->buffer != NULL) {
461 gst_buffer_unref (collect_pad->buffer);
462 collect_pad->buffer = NULL;
465 if (!full && type != 0) {
466 GstMatroskaTrackContext *context;
468 /* create a fresh context */
470 case GST_MATROSKA_TRACK_TYPE_VIDEO:
471 context = (GstMatroskaTrackContext *)
472 g_new0 (GstMatroskaTrackVideoContext, 1);
474 case GST_MATROSKA_TRACK_TYPE_AUDIO:
475 context = (GstMatroskaTrackContext *)
476 g_new0 (GstMatroskaTrackAudioContext, 1);
478 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
479 context = (GstMatroskaTrackContext *)
480 g_new0 (GstMatroskaTrackSubtitleContext, 1);
483 g_assert_not_reached ();
487 context->type = type;
488 context->name = name;
489 /* TODO: check default values for the context */
490 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
491 collect_pad->track = context;
492 collect_pad->buffer = NULL;
493 collect_pad->duration = 0;
494 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
495 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
500 * gst_matroska_pad_free:
501 * @collect_pad: the #GstMatroskaPad
503 * Release resources of a matroska collect pad.
506 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
508 gst_matroska_pad_reset (collect_pad, TRUE);
513 * gst_matroska_mux_reset:
514 * @element: #GstMatroskaMux that should be reseted.
516 * Reset matroska muxer back to initial state.
519 gst_matroska_mux_reset (GstElement * element)
521 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
524 /* reset EBML write */
525 gst_ebml_write_reset (mux->ebml_write);
528 mux->state = GST_MATROSKA_MUX_STATE_START;
530 /* clean up existing streams */
532 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
533 GstMatroskaPad *collect_pad;
535 collect_pad = (GstMatroskaPad *) walk->data;
537 /* reset collect pad to pristine state */
538 gst_matroska_pad_reset (collect_pad, FALSE);
542 mux->num_indexes = 0;
547 mux->time_scale = GST_MSECOND;
552 mux->cluster_time = 0;
553 mux->cluster_pos = 0;
554 mux->prev_cluster_size = 0;
557 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
561 * gst_matroska_mux_handle_src_event:
562 * @pad: Pad which received the event.
563 * @event: Received event.
565 * handle events - copied from oggmux without understanding
567 * Returns: #TRUE on success.
570 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
574 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
578 /* disable seeking for now */
584 return gst_pad_event_default (pad, event);
588 * gst_matroska_mux_handle_sink_event:
589 * @pad: Pad which received the event.
590 * @event: Received event.
592 * handle events - informational ones like tags
594 * Returns: #TRUE on success.
597 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
599 GstMatroskaTrackContext *context;
600 GstMatroskaPad *collect_pad;
605 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
607 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
608 switch (GST_EVENT_TYPE (event)) {
612 GST_DEBUG_OBJECT (mux, "received tag event");
613 gst_event_parse_tag (event, &list);
615 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
616 g_assert (collect_pad);
617 context = collect_pad->track;
620 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
621 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
622 const gchar *lang_code;
624 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
626 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
627 context->language = g_strdup (lang_code);
629 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
634 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
635 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
638 case GST_EVENT_NEWSEGMENT:
639 /* We don't support NEWSEGMENT events */
641 gst_event_unref (event);
647 /* now GstCollectPads can take care of the rest, e.g. EOS */
649 ret = mux->collect_event (pad, event);
650 gst_object_unref (mux);
657 * gst_matroska_mux_video_pad_setcaps:
658 * @pad: Pad which got the caps.
661 * Setcaps function for video sink pad.
663 * Returns: #TRUE on success.
666 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
668 GstMatroskaTrackContext *context = NULL;
669 GstMatroskaTrackVideoContext *videocontext;
671 GstMatroskaPad *collect_pad;
672 GstStructure *structure;
673 const gchar *mimetype;
674 const GValue *value = NULL;
675 const GstBuffer *codec_buf = NULL;
676 gint width, height, pixel_width, pixel_height;
678 gboolean interlaced = FALSE;
680 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
683 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
684 g_assert (collect_pad);
685 context = collect_pad->track;
687 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
688 videocontext = (GstMatroskaTrackVideoContext *) context;
690 /* gst -> matroska ID'ing */
691 structure = gst_caps_get_structure (caps, 0);
693 mimetype = gst_structure_get_name (structure);
695 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
697 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
699 if (!strcmp (mimetype, "video/x-theora")) {
700 /* we'll extract the details later from the theora identification header */
704 /* get general properties */
705 /* spec says it is mandatory */
706 if (!gst_structure_get_int (structure, "width", &width) ||
707 !gst_structure_get_int (structure, "height", &height))
710 videocontext->pixel_width = width;
711 videocontext->pixel_height = height;
712 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
714 context->default_duration =
715 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
716 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
717 GST_TIME_ARGS (context->default_duration));
719 context->default_duration = 0;
721 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
722 &pixel_width, &pixel_height)) {
723 if (pixel_width > pixel_height) {
724 videocontext->display_width = width * pixel_width / pixel_height;
725 videocontext->display_height = height;
726 } else if (pixel_width < pixel_height) {
727 videocontext->display_width = width;
728 videocontext->display_height = height * pixel_height / pixel_width;
730 videocontext->display_width = 0;
731 videocontext->display_height = 0;
734 videocontext->display_width = 0;
735 videocontext->display_height = 0;
740 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
741 videocontext->fourcc = 0;
743 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
744 * data and other settings
748 /* extract codec_data, may turn out needed */
749 value = gst_structure_get_value (structure, "codec_data");
751 codec_buf = gst_value_get_buffer (value);
754 if (!strcmp (mimetype, "video/x-raw-yuv")) {
755 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
756 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
757 } else if (!strcmp (mimetype, "image/jpeg")) {
758 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
759 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
760 ||!strcmp (mimetype, "video/x-huffyuv")
761 || !strcmp (mimetype, "video/x-divx")
762 || !strcmp (mimetype, "video/x-dv")
763 || !strcmp (mimetype, "video/x-h263")
764 || !strcmp (mimetype, "video/x-msmpeg")
765 || !strcmp (mimetype, "video/x-wmv")) {
766 gst_riff_strf_vids *bih;
767 gint size = sizeof (gst_riff_strf_vids);
770 if (!strcmp (mimetype, "video/x-xvid"))
771 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
772 else if (!strcmp (mimetype, "video/x-huffyuv"))
773 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
774 else if (!strcmp (mimetype, "video/x-dv"))
775 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
776 else if (!strcmp (mimetype, "video/x-h263"))
777 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
778 else if (!strcmp (mimetype, "video/x-divx")) {
781 gst_structure_get_int (structure, "divxversion", &divxversion);
782 switch (divxversion) {
784 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
787 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
790 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
793 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
796 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
797 switch (msmpegversion) {
799 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
802 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
808 } else if (!strcmp (mimetype, "video/x-wmv")) {
811 if (gst_structure_get_fourcc (structure, "format", &format)) {
813 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
814 if (wmvversion == 2) {
815 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
816 } else if (wmvversion == 1) {
817 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
818 } else if (wmvversion == 3) {
819 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
827 bih = g_new0 (gst_riff_strf_vids, 1);
828 GST_WRITE_UINT32_LE (&bih->size, size);
829 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
830 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
831 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
832 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
833 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
834 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
835 videocontext->pixel_height * 3);
837 /* process codec private/initialization data, if any */
839 size += GST_BUFFER_SIZE (codec_buf);
840 bih = g_realloc (bih, size);
841 GST_WRITE_UINT32_LE (&bih->size, size);
842 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
843 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
846 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
847 context->codec_priv = (gpointer) bih;
848 context->codec_priv_size = size;
849 } else if (!strcmp (mimetype, "video/x-h264")) {
850 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
852 if (context->codec_priv != NULL) {
853 g_free (context->codec_priv);
854 context->codec_priv = NULL;
855 context->codec_priv_size = 0;
858 /* Create avcC header */
859 if (codec_buf != NULL) {
860 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
861 context->codec_priv = g_malloc0 (context->codec_priv_size);
862 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
863 context->codec_priv_size);
865 } else if (!strcmp (mimetype, "video/x-theora")) {
866 const GValue *streamheader;
868 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
870 if (context->codec_priv != NULL) {
871 g_free (context->codec_priv);
872 context->codec_priv = NULL;
873 context->codec_priv_size = 0;
876 streamheader = gst_structure_get_value (structure, "streamheader");
877 if (!theora_streamheader_to_codecdata (streamheader, context)) {
878 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
879 ("theora stream headers missing or malformed"));
882 } else if (!strcmp (mimetype, "video/x-dirac")) {
883 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
884 } else if (!strcmp (mimetype, "video/x-vp8")) {
885 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
886 } else if (!strcmp (mimetype, "video/mpeg")) {
889 gst_structure_get_int (structure, "mpegversion", &mpegversion);
890 switch (mpegversion) {
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
895 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
898 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
904 /* global headers may be in codec data */
905 if (codec_buf != NULL) {
906 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
907 context->codec_priv = g_malloc0 (context->codec_priv_size);
908 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
909 context->codec_priv_size);
911 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
913 /* can only make it here if preceding case verified it was version 3 */
914 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
915 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
917 const GValue *mdpr_data;
919 gst_structure_get_int (structure, "rmversion", &rmversion);
922 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
925 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
928 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
931 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
937 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
938 if (mdpr_data != NULL) {
939 guint8 *priv_data = NULL;
940 guint priv_data_size = 0;
942 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
944 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
945 priv_data = g_malloc0 (priv_data_size);
947 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
949 context->codec_priv = priv_data;
950 context->codec_priv_size = priv_data_size;
959 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
960 GST_PAD_NAME (pad), caps);
965 /* N > 0 to expect a particular number of headers, negative if the
966 number of headers is variable */
968 xiphN_streamheader_to_codecdata (const GValue * streamheader,
969 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
971 GstBuffer **buf = NULL;
974 guint bufi, i, offset, priv_data_size;
976 if (streamheader == NULL)
977 goto no_stream_headers;
979 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
982 bufarr = g_value_peek_pointer (streamheader);
983 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
985 if (N > 0 && bufarr->len != N)
988 context->xiph_headers_to_skip = bufarr->len;
990 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
991 for (i = 0; i < bufarr->len; i++) {
992 GValue *bufval = &g_array_index (bufarr, GValue, i);
994 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
996 goto wrong_content_type;
999 buf[i] = g_value_peek_pointer (bufval);
1003 if (bufarr->len > 0) {
1004 for (i = 0; i < bufarr->len - 1; i++) {
1005 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1009 for (i = 0; i < bufarr->len; ++i) {
1010 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1013 priv_data = g_malloc0 (priv_data_size);
1015 priv_data[0] = bufarr->len - 1;
1018 if (bufarr->len > 0) {
1019 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1020 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1021 priv_data[offset++] = 0xff;
1023 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1027 for (i = 0; i < bufarr->len; ++i) {
1028 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1029 GST_BUFFER_SIZE (buf[i]));
1030 offset += GST_BUFFER_SIZE (buf[i]);
1033 context->codec_priv = priv_data;
1034 context->codec_priv_size = priv_data_size;
1037 *p_buf0 = gst_buffer_ref (buf[0]);
1046 GST_WARNING ("required streamheaders missing in sink caps!");
1051 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1052 G_VALUE_TYPE_NAME (streamheader));
1057 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1062 GST_WARNING ("streamheaders array does not contain GstBuffers");
1068 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1069 GstMatroskaTrackContext * context)
1071 GstBuffer *buf0 = NULL;
1073 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1076 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1077 GST_WARNING ("First vorbis header too small, ignoring");
1079 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1080 GstMatroskaTrackAudioContext *audiocontext;
1083 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1084 audiocontext = (GstMatroskaTrackAudioContext *) context;
1085 audiocontext->channels = GST_READ_UINT8 (hdr);
1086 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1091 gst_buffer_unref (buf0);
1097 theora_streamheader_to_codecdata (const GValue * streamheader,
1098 GstMatroskaTrackContext * context)
1100 GstBuffer *buf0 = NULL;
1102 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1105 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1106 GST_WARNING ("First theora header too small, ignoring");
1107 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1108 GST_WARNING ("First header not a theora identification header, ignoring");
1110 GstMatroskaTrackVideoContext *videocontext;
1111 guint fps_num, fps_denom, par_num, par_denom;
1114 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1116 videocontext = (GstMatroskaTrackVideoContext *) context;
1117 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1118 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1119 hdr += 3 + 3 + 1 + 1;
1120 fps_num = GST_READ_UINT32_BE (hdr);
1121 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1122 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1123 fps_denom, fps_num);
1125 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1126 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1127 if (par_num > 0 && par_num > 0) {
1128 if (par_num > par_denom) {
1129 videocontext->display_width =
1130 videocontext->pixel_width * par_num / par_denom;
1131 videocontext->display_height = videocontext->pixel_height;
1132 } else if (par_num < par_denom) {
1133 videocontext->display_width = videocontext->pixel_width;
1134 videocontext->display_height =
1135 videocontext->pixel_height * par_denom / par_num;
1137 videocontext->display_width = 0;
1138 videocontext->display_height = 0;
1141 videocontext->display_width = 0;
1142 videocontext->display_height = 0;
1148 gst_buffer_unref (buf0);
1154 kate_streamheader_to_codecdata (const GValue * streamheader,
1155 GstMatroskaTrackContext * context)
1157 GstBuffer *buf0 = NULL;
1159 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1162 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1163 GST_WARNING ("First kate header too small, ignoring");
1164 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1165 GST_WARNING ("First header not a kate identification header, ignoring");
1169 gst_buffer_unref (buf0);
1175 flac_streamheader_to_codecdata (const GValue * streamheader,
1176 GstMatroskaTrackContext * context)
1183 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1184 GST_WARNING ("No or invalid streamheader field in the caps");
1188 bufarr = g_value_peek_pointer (streamheader);
1189 if (bufarr->len < 2) {
1190 GST_WARNING ("Too few headers in streamheader field");
1194 context->xiph_headers_to_skip = bufarr->len + 1;
1196 bufval = &g_array_index (bufarr, GValue, 0);
1197 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1198 GST_WARNING ("streamheaders array does not contain GstBuffers");
1202 buffer = g_value_peek_pointer (bufval);
1204 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1205 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1206 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1207 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1208 GST_WARNING ("Invalid streamheader for FLAC");
1212 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1213 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1214 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1215 GST_BUFFER_SIZE (buffer) - 9);
1217 for (i = 1; i < bufarr->len; i++) {
1218 bufval = &g_array_index (bufarr, GValue, i);
1220 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1221 g_free (context->codec_priv);
1222 context->codec_priv = NULL;
1223 context->codec_priv_size = 0;
1224 GST_WARNING ("streamheaders array does not contain GstBuffers");
1228 buffer = g_value_peek_pointer (bufval);
1230 context->codec_priv =
1231 g_realloc (context->codec_priv,
1232 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1233 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1234 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1235 context->codec_priv_size =
1236 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1243 speex_streamheader_to_codecdata (const GValue * streamheader,
1244 GstMatroskaTrackContext * context)
1250 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1251 GST_WARNING ("No or invalid streamheader field in the caps");
1255 bufarr = g_value_peek_pointer (streamheader);
1256 if (bufarr->len != 2) {
1257 GST_WARNING ("Too few headers in streamheader field");
1261 context->xiph_headers_to_skip = bufarr->len + 1;
1263 bufval = &g_array_index (bufarr, GValue, 0);
1264 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1265 GST_WARNING ("streamheaders array does not contain GstBuffers");
1269 buffer = g_value_peek_pointer (bufval);
1271 if (GST_BUFFER_SIZE (buffer) < 80
1272 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1273 GST_WARNING ("Invalid streamheader for Speex");
1277 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1278 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1279 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1280 GST_BUFFER_SIZE (buffer));
1282 bufval = &g_array_index (bufarr, GValue, 1);
1284 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1285 g_free (context->codec_priv);
1286 context->codec_priv = NULL;
1287 context->codec_priv_size = 0;
1288 GST_WARNING ("streamheaders array does not contain GstBuffers");
1292 buffer = g_value_peek_pointer (bufval);
1294 context->codec_priv =
1295 g_realloc (context->codec_priv,
1296 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1297 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1298 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1299 context->codec_priv_size =
1300 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1305 static const gchar *
1306 aac_codec_data_to_codec_id (const GstBuffer * buf)
1308 const gchar *result;
1311 /* default to MAIN */
1314 if (GST_BUFFER_SIZE (buf) >= 2) {
1315 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1333 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1342 * gst_matroska_mux_audio_pad_setcaps:
1343 * @pad: Pad which got the caps.
1346 * Setcaps function for audio sink pad.
1348 * Returns: #TRUE on success.
1351 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1353 GstMatroskaTrackContext *context = NULL;
1354 GstMatroskaTrackAudioContext *audiocontext;
1355 GstMatroskaMux *mux;
1356 GstMatroskaPad *collect_pad;
1357 const gchar *mimetype;
1358 gint samplerate = 0, channels = 0;
1359 GstStructure *structure;
1360 const GValue *codec_data = NULL;
1361 const GstBuffer *buf = NULL;
1362 const gchar *stream_format = NULL;
1364 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1367 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1368 g_assert (collect_pad);
1369 context = collect_pad->track;
1371 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1372 audiocontext = (GstMatroskaTrackAudioContext *) context;
1374 structure = gst_caps_get_structure (caps, 0);
1375 mimetype = gst_structure_get_name (structure);
1378 gst_structure_get_int (structure, "rate", &samplerate);
1379 gst_structure_get_int (structure, "channels", &channels);
1381 audiocontext->samplerate = samplerate;
1382 audiocontext->channels = channels;
1383 audiocontext->bitdepth = 0;
1384 context->default_duration = 0;
1386 codec_data = gst_structure_get_value (structure, "codec_data");
1388 buf = gst_value_get_buffer (codec_data);
1390 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1391 * data and other settings
1395 if (!strcmp (mimetype, "audio/mpeg")) {
1396 gint mpegversion = 0;
1398 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1399 switch (mpegversion) {
1405 gst_structure_get_int (structure, "layer", &layer);
1407 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1408 GST_WARNING_OBJECT (mux,
1409 "Unable to determine MPEG audio version, assuming 1");
1415 else if (layer == 2)
1417 else if (version == 2)
1422 context->default_duration =
1423 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1427 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1430 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1433 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1442 stream_format = gst_structure_get_string (structure, "stream-format");
1443 /* check this is raw aac */
1444 if (stream_format) {
1445 if (strcmp (stream_format, "raw") != 0) {
1446 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1450 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1455 if (mpegversion == 2)
1457 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1458 aac_codec_data_to_codec_id (buf));
1459 else if (mpegversion == 4)
1461 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1462 aac_codec_data_to_codec_id (buf));
1464 g_assert_not_reached ();
1466 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1473 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1475 gint endianness = G_LITTLE_ENDIAN;
1476 gboolean signedness = TRUE;
1478 if (!gst_structure_get_int (structure, "width", &width) ||
1479 !gst_structure_get_int (structure, "depth", &depth) ||
1480 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1481 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1486 !gst_structure_get_int (structure, "endianness", &endianness)) {
1487 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1491 if (width != depth) {
1492 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1496 /* FIXME: where is this spec'ed out? (tpm) */
1497 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1498 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1502 audiocontext->bitdepth = depth;
1503 if (endianness == G_BIG_ENDIAN)
1504 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1506 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1508 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1511 if (!gst_structure_get_int (structure, "width", &width)) {
1512 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1516 audiocontext->bitdepth = width;
1517 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1519 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1520 const GValue *streamheader;
1522 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1524 if (context->codec_priv != NULL) {
1525 g_free (context->codec_priv);
1526 context->codec_priv = NULL;
1527 context->codec_priv_size = 0;
1530 streamheader = gst_structure_get_value (structure, "streamheader");
1531 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1532 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1533 ("vorbis stream headers missing or malformed"));
1536 } else if (!strcmp (mimetype, "audio/x-flac")) {
1537 const GValue *streamheader;
1539 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1540 if (context->codec_priv != NULL) {
1541 g_free (context->codec_priv);
1542 context->codec_priv = NULL;
1543 context->codec_priv_size = 0;
1546 streamheader = gst_structure_get_value (structure, "streamheader");
1547 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1548 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1549 ("flac stream headers missing or malformed"));
1552 } else if (!strcmp (mimetype, "audio/x-speex")) {
1553 const GValue *streamheader;
1555 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1556 if (context->codec_priv != NULL) {
1557 g_free (context->codec_priv);
1558 context->codec_priv = NULL;
1559 context->codec_priv_size = 0;
1562 streamheader = gst_structure_get_value (structure, "streamheader");
1563 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1564 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1565 ("speex stream headers missing or malformed"));
1568 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1569 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1570 } else if (!strcmp (mimetype, "audio/x-tta")) {
1573 /* TTA frame duration */
1574 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1576 gst_structure_get_int (structure, "width", &width);
1577 audiocontext->bitdepth = width;
1578 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1580 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1582 const GValue *mdpr_data;
1584 gst_structure_get_int (structure, "raversion", &raversion);
1585 switch (raversion) {
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1590 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1593 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1599 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1600 if (mdpr_data != NULL) {
1601 guint8 *priv_data = NULL;
1602 guint priv_data_size = 0;
1604 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1606 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1607 priv_data = g_malloc0 (priv_data_size);
1609 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1611 context->codec_priv = priv_data;
1612 context->codec_priv_size = priv_data_size;
1615 } else if (!strcmp (mimetype, "audio/x-wma")) {
1617 guint codec_priv_size;
1624 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1625 || !gst_structure_get_int (structure, "block_align", &block_align)
1626 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1627 || samplerate == 0 || channels == 0) {
1628 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1629 "channels/rate on WMA caps");
1633 switch (wmaversion) {
1635 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1638 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1641 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1644 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1648 if (gst_structure_get_int (structure, "depth", &depth))
1649 audiocontext->bitdepth = depth;
1651 codec_priv_size = WAVEFORMATEX_SIZE;
1653 codec_priv_size += GST_BUFFER_SIZE (buf);
1655 /* serialize waveformatex structure */
1656 codec_priv = g_malloc0 (codec_priv_size);
1657 GST_WRITE_UINT16_LE (codec_priv, format);
1658 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1659 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1660 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1661 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1662 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1664 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1666 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1668 /* process codec private/initialization data, if any */
1670 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1671 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1674 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1675 context->codec_priv = (gpointer) codec_priv;
1676 context->codec_priv_size = codec_priv_size;
1684 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1685 GST_PAD_NAME (pad), caps);
1692 * gst_matroska_mux_subtitle_pad_setcaps:
1693 * @pad: Pad which got the caps.
1696 * Setcaps function for subtitle sink pad.
1698 * Returns: #TRUE on success.
1701 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1704 * Consider this as boilerplate code for now. There is
1705 * no single subtitle creation element in GStreamer,
1706 * neither do I know how subtitling works at all. */
1708 /* There is now (at least) one such alement (kateenc), and I'm going
1709 to handle it here and claim it works when it can be piped back
1710 through GStreamer and VLC */
1712 GstMatroskaTrackContext *context = NULL;
1713 GstMatroskaTrackSubtitleContext *scontext;
1714 GstMatroskaMux *mux;
1715 GstMatroskaPad *collect_pad;
1716 const gchar *mimetype;
1717 GstStructure *structure;
1719 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1722 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1723 g_assert (collect_pad);
1724 context = collect_pad->track;
1726 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1727 scontext = (GstMatroskaTrackSubtitleContext *) context;
1729 structure = gst_caps_get_structure (caps, 0);
1730 mimetype = gst_structure_get_name (structure);
1733 scontext->check_utf8 = 1;
1734 scontext->invalid_utf8 = 0;
1735 context->default_duration = 0;
1737 /* TODO: - other format than Kate */
1739 if (!strcmp (mimetype, "subtitle/x-kate")) {
1740 const GValue *streamheader;
1742 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1744 if (context->codec_priv != NULL) {
1745 g_free (context->codec_priv);
1746 context->codec_priv = NULL;
1747 context->codec_priv_size = 0;
1750 streamheader = gst_structure_get_value (structure, "streamheader");
1751 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1752 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1753 ("kate stream headers missing or malformed"));
1764 * gst_matroska_mux_request_new_pad:
1765 * @element: #GstMatroskaMux.
1766 * @templ: #GstPadTemplate.
1767 * @pad_name: New pad name.
1769 * Request pad function for sink templates.
1771 * Returns: New #GstPad.
1774 gst_matroska_mux_request_new_pad (GstElement * element,
1775 GstPadTemplate * templ, const gchar * pad_name)
1777 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1778 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1779 GstMatroskaPad *collect_pad;
1780 GstPad *newpad = NULL;
1782 GstPadSetCapsFunction setcapsfunc = NULL;
1783 GstMatroskaTrackContext *context = NULL;
1785 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1786 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1787 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1788 context = (GstMatroskaTrackContext *)
1789 g_new0 (GstMatroskaTrackAudioContext, 1);
1790 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1791 context->name = g_strdup ("Audio");
1792 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1793 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1794 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1795 context = (GstMatroskaTrackContext *)
1796 g_new0 (GstMatroskaTrackVideoContext, 1);
1797 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1798 context->name = g_strdup ("Video");
1799 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1800 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1801 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1802 context = (GstMatroskaTrackContext *)
1803 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1804 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1805 context->name = g_strdup ("Subtitle");
1807 GST_WARNING_OBJECT (mux, "This is not our template!");
1811 newpad = gst_pad_new_from_template (templ, name);
1813 collect_pad = (GstMatroskaPad *)
1814 gst_collect_pads_add_pad_full (mux->collect, newpad,
1815 sizeof (GstMatroskaPad),
1816 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1818 collect_pad->track = context;
1819 gst_matroska_pad_reset (collect_pad, FALSE);
1821 /* FIXME: hacked way to override/extend the event function of
1822 * GstCollectPads; because it sets its own event function giving the
1823 * element no access to events.
1824 * TODO GstCollectPads should really give its 'users' a clean chance to
1825 * properly handle events that are not meant for collectpads itself.
1826 * Perhaps a callback or so, though rejected (?) in #340060.
1827 * This would allow (clean) transcoding of info from demuxer/streams
1828 * to another muxer */
1829 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1830 gst_pad_set_event_function (newpad,
1831 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1833 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1834 gst_pad_set_active (newpad, TRUE);
1835 gst_element_add_pad (element, newpad);
1842 * gst_matroska_mux_release_pad:
1843 * @element: #GstMatroskaMux.
1844 * @pad: Pad to release.
1846 * Release a previously requested pad.
1849 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1851 GstMatroskaMux *mux;
1854 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1856 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1857 GstCollectData *cdata = (GstCollectData *) walk->data;
1858 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1860 if (cdata->pad == pad) {
1861 GstClockTime min_dur; /* observed minimum duration */
1863 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1864 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1865 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1866 if (collect_pad->duration < min_dur)
1867 collect_pad->duration = min_dur;
1870 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1871 mux->duration < collect_pad->duration)
1872 mux->duration = collect_pad->duration;
1878 gst_collect_pads_remove_pad (mux->collect, pad);
1879 if (gst_element_remove_pad (element, pad))
1885 * gst_matroska_mux_track_header:
1886 * @mux: #GstMatroskaMux
1887 * @context: Tack context.
1889 * Write a track header.
1892 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1893 GstMatroskaTrackContext * context)
1895 GstEbmlWrite *ebml = mux->ebml_write;
1898 /* TODO: check if everything necessary is written and check default values */
1900 /* track type goes before the type-specific stuff */
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1902 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1904 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1905 gst_matroska_mux_create_uid ());
1906 if (context->default_duration) {
1907 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1908 context->default_duration);
1910 if (context->language) {
1911 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1915 /* type-specific stuff */
1916 switch (context->type) {
1917 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1918 GstMatroskaTrackVideoContext *videocontext =
1919 (GstMatroskaTrackVideoContext *) context;
1921 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1923 videocontext->pixel_width);
1924 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1925 videocontext->pixel_height);
1926 if (videocontext->display_width && videocontext->display_height) {
1927 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1928 videocontext->display_width);
1929 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1930 videocontext->display_height);
1932 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1933 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1934 if (videocontext->fourcc) {
1935 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1937 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1938 (gpointer) & fcc_le, 4);
1940 gst_ebml_write_master_finish (ebml, master);
1945 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1946 GstMatroskaTrackAudioContext *audiocontext =
1947 (GstMatroskaTrackAudioContext *) context;
1949 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1950 if (audiocontext->samplerate != 8000)
1951 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1952 audiocontext->samplerate);
1953 if (audiocontext->channels != 1)
1954 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1955 audiocontext->channels);
1956 if (audiocontext->bitdepth) {
1957 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1958 audiocontext->bitdepth);
1960 gst_ebml_write_master_finish (ebml, master);
1966 /* doesn't need type-specific data */
1970 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1971 if (context->codec_priv)
1972 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1973 context->codec_priv, context->codec_priv_size);
1974 /* FIXME: until we have a nice way of getting the codecname
1975 * out of the caps, I'm not going to enable this. Too much
1976 * (useless, double, boring) work... */
1977 /* TODO: Use value from tags if any */
1978 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1979 context->codec_name); */
1980 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1985 * gst_matroska_mux_start:
1986 * @mux: #GstMatroskaMux
1988 * Start a new matroska file (write headers etc...)
1991 gst_matroska_mux_start (GstMatroskaMux * mux)
1993 GstEbmlWrite *ebml = mux->ebml_write;
1994 const gchar *doctype;
1995 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1996 GST_MATROSKA_ID_TRACKS,
1997 GST_MATROSKA_ID_CUES,
1998 GST_MATROSKA_ID_TAGS,
2001 guint64 master, child;
2005 GstClockTime duration = 0;
2006 guint32 segment_uid[4];
2007 GTimeVal time = { 0, 0 };
2009 /* we start with a EBML header */
2010 doctype = mux->doctype;
2011 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2012 doctype, mux->doctype_version);
2013 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2015 /* the rest of the header is cached */
2016 gst_ebml_write_set_cache (ebml, 0x1000);
2018 /* start a segment */
2020 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2021 mux->segment_master = ebml->pos;
2023 /* seekhead (table of contents) - we set the positions later */
2024 mux->seekhead_pos = ebml->pos;
2025 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2026 for (i = 0; seekhead_id[i] != 0; i++) {
2027 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2028 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2029 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2030 gst_ebml_write_master_finish (ebml, child);
2032 gst_ebml_write_master_finish (ebml, master);
2035 mux->info_pos = ebml->pos;
2036 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2037 for (i = 0; i < 4; i++) {
2038 segment_uid[i] = g_random_int ();
2040 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2041 (guint8 *) segment_uid, 16);
2042 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2043 mux->duration_pos = ebml->pos;
2045 for (collected = mux->collect->data; collected;
2046 collected = g_slist_next (collected)) {
2047 GstMatroskaPad *collect_pad;
2048 GstFormat format = GST_FORMAT_TIME;
2050 gint64 trackduration;
2052 collect_pad = (GstMatroskaPad *) collected->data;
2053 thepad = collect_pad->collect.pad;
2055 /* Query the total length of the track. */
2056 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2057 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2058 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2059 GST_TIME_ARGS (trackduration));
2060 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2061 duration = (GstClockTime) trackduration;
2065 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2066 gst_guint64_to_gdouble (duration) /
2067 gst_guint64_to_gdouble (mux->time_scale));
2069 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2070 "GStreamer plugin version " PACKAGE_VERSION);
2071 if (mux->writing_app && mux->writing_app[0]) {
2072 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2074 g_get_current_time (&time);
2075 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2076 gst_ebml_write_master_finish (ebml, master);
2079 mux->tracks_pos = ebml->pos;
2080 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2082 for (collected = mux->collect->data; collected;
2083 collected = g_slist_next (collected)) {
2084 GstMatroskaPad *collect_pad;
2087 collect_pad = (GstMatroskaPad *) collected->data;
2088 thepad = collect_pad->collect.pad;
2090 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2091 collect_pad->track->codec_id != 0) {
2092 collect_pad->track->num = tracknum++;
2093 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2094 gst_matroska_mux_track_header (mux, collect_pad->track);
2095 gst_ebml_write_master_finish (ebml, child);
2098 gst_ebml_write_master_finish (ebml, master);
2100 /* lastly, flush the cache */
2101 gst_ebml_write_flush_cache (ebml);
2105 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2108 /* TODO: more sensible tag mappings */
2111 const gchar *matroska_tagname;
2112 const gchar *gstreamer_tagname;
2116 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2117 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2118 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2119 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2120 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2121 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2122 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2123 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2124 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2125 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2126 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2127 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2128 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2129 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2130 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2132 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2134 guint64 simpletag_master;
2136 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2137 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2138 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2140 if (strcmp (tagname_gst, tag) == 0) {
2141 GValue src = { 0, };
2144 if (!gst_tag_list_copy_value (&src, list, tag))
2146 if ((dest = gst_value_serialize (&src))) {
2148 simpletag_master = gst_ebml_write_master_start (ebml,
2149 GST_MATROSKA_ID_SIMPLETAG);
2150 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2151 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2152 gst_ebml_write_master_finish (ebml, simpletag_master);
2155 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2157 g_value_unset (&src);
2165 * gst_matroska_mux_finish:
2166 * @mux: #GstMatroskaMux
2168 * Finish a new matroska file (write index etc...)
2171 gst_matroska_mux_finish (GstMatroskaMux * mux)
2173 GstEbmlWrite *ebml = mux->ebml_write;
2175 guint64 duration = 0;
2177 const GstTagList *tags;
2179 /* finish last cluster */
2181 gst_ebml_write_master_finish (ebml, mux->cluster);
2185 if (mux->index != NULL) {
2187 guint64 master, pointentry_master, trackpos_master;
2189 mux->cues_pos = ebml->pos;
2190 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2191 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2193 for (n = 0; n < mux->num_indexes; n++) {
2194 GstMatroskaIndex *idx = &mux->index[n];
2196 pointentry_master = gst_ebml_write_master_start (ebml,
2197 GST_MATROSKA_ID_POINTENTRY);
2198 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2199 idx->time / mux->time_scale);
2200 trackpos_master = gst_ebml_write_master_start (ebml,
2201 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2202 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2203 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2204 idx->pos - mux->segment_master);
2205 gst_ebml_write_master_finish (ebml, trackpos_master);
2206 gst_ebml_write_master_finish (ebml, pointentry_master);
2209 gst_ebml_write_master_finish (ebml, master);
2210 gst_ebml_write_flush_cache (ebml);
2214 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (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;
2534 gst_ebml_write_set_cache (ebml, 0x20);
2536 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2537 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2538 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2539 gst_ebml_write_flush_cache (ebml);
2540 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2541 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2542 mux->prev_cluster_size);
2547 mux->cluster_pos = ebml->pos;
2548 gst_ebml_write_set_cache (ebml, 0x20);
2549 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2550 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2551 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2552 gst_ebml_write_flush_cache (ebml);
2553 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2556 /* update duration of this track */
2557 if (GST_BUFFER_DURATION_IS_VALID (buf))
2558 collect_pad->duration += GST_BUFFER_DURATION (buf);
2560 /* We currently write index entries for all video tracks or for the audio
2561 * track in a single-track audio file. This could be improved by keeping the
2562 * index only for the *first* video track. */
2564 /* TODO: index is useful for every track, should contain the number of
2565 * the block in the cluster which contains the timestamp, should also work
2566 * for files with multiple audio tracks.
2568 if (is_video_keyframe ||
2569 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2570 (mux->num_streams == 1))) {
2573 if (mux->min_index_interval != 0) {
2574 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2575 if (mux->index[last_idx].track == collect_pad->track->num)
2580 if (last_idx < 0 || mux->min_index_interval == 0 ||
2581 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2582 >= mux->min_index_interval)) {
2583 GstMatroskaIndex *idx;
2585 if (mux->num_indexes % 32 == 0) {
2586 mux->index = g_renew (GstMatroskaIndex, mux->index,
2587 mux->num_indexes + 32);
2589 idx = &mux->index[mux->num_indexes++];
2591 idx->pos = mux->cluster_pos;
2592 idx->time = GST_BUFFER_TIMESTAMP (buf);
2593 idx->track = collect_pad->track->num;
2597 /* Check if the duration differs from the default duration. */
2598 write_duration = FALSE;
2599 block_duration = GST_BUFFER_DURATION (buf);
2600 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2601 if (block_duration != collect_pad->track->default_duration) {
2602 write_duration = TRUE;
2606 /* write the block, for doctype v2 use SimpleBlock if possible
2607 * one slice (*breath*).
2608 * FIXME: Need to do correct lacing! */
2609 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2610 if (relative_timestamp64 >= 0) {
2611 /* round the timestamp */
2612 relative_timestamp64 += mux->time_scale / 2;
2614 /* round the timestamp */
2615 relative_timestamp64 -= mux->time_scale / 2;
2617 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2618 if (mux->doctype_version > 1 && !write_duration) {
2620 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2623 gst_matroska_mux_create_buffer_header (collect_pad->track,
2624 relative_timestamp, flags);
2625 gst_ebml_write_set_cache (ebml, 0x40);
2626 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2627 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2628 gst_ebml_write_buffer (ebml, hdr);
2629 gst_ebml_write_flush_cache (ebml);
2630 gst_ebml_write_buffer (ebml, buf);
2632 return gst_ebml_last_write_result (ebml);
2634 gst_ebml_write_set_cache (ebml, 0x40);
2635 /* write and call order slightly unnatural,
2636 * but avoids seek and minizes pushing */
2637 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2639 gst_matroska_mux_create_buffer_header (collect_pad->track,
2640 relative_timestamp, 0);
2641 if (write_duration) {
2642 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2643 block_duration / mux->time_scale);
2645 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2646 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2647 gst_ebml_write_buffer (ebml, hdr);
2648 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2649 gst_ebml_write_flush_cache (ebml);
2650 gst_ebml_write_buffer (ebml, buf);
2651 return gst_ebml_last_write_result (ebml);
2657 * gst_matroska_mux_collected:
2658 * @pads: #GstCollectPads
2659 * @uuser_data: #GstMatroskaMux
2661 * Collectpads callback.
2663 * Returns: #GstFlowReturn
2665 static GstFlowReturn
2666 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2668 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2669 GstMatroskaPad *best;
2673 GST_DEBUG_OBJECT (mux, "Collected pads");
2675 /* start with a header */
2676 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2677 if (mux->collect->data == NULL) {
2678 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2679 ("No input streams configured"));
2680 return GST_FLOW_ERROR;
2682 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2683 gst_matroska_mux_start (mux);
2684 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2688 /* which stream to write from? */
2689 best = gst_matroska_mux_best_pad (mux, &popped);
2691 /* if there is no best pad, we have reached EOS */
2693 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2694 gst_matroska_mux_finish (mux);
2695 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2696 ret = GST_FLOW_UNEXPECTED;
2699 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2700 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2701 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2702 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2704 /* make note of first and last encountered timestamps, so we can calculate
2705 * the actual duration later when we send an updated header on eos */
2706 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2707 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2708 GstClockTime end_ts = start_ts;
2710 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2711 end_ts += GST_BUFFER_DURATION (best->buffer);
2712 else if (best->track->default_duration)
2713 end_ts += best->track->default_duration;
2715 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2716 best->end_ts = end_ts;
2718 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2719 start_ts < best->start_ts))
2720 best->start_ts = start_ts;
2723 /* write one buffer */
2724 ret = gst_matroska_mux_write_data (mux, best);
2725 } while (ret == GST_FLOW_OK && !popped);
2732 * gst_matroska_mux_change_state:
2733 * @element: #GstMatroskaMux
2734 * @transition: State change transition.
2736 * Change the muxer state.
2738 * Returns: #GstStateChangeReturn
2740 static GstStateChangeReturn
2741 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2743 GstStateChangeReturn ret;
2744 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2746 switch (transition) {
2747 case GST_STATE_CHANGE_NULL_TO_READY:
2749 case GST_STATE_CHANGE_READY_TO_PAUSED:
2750 gst_collect_pads_start (mux->collect);
2752 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2754 case GST_STATE_CHANGE_PAUSED_TO_READY:
2755 gst_collect_pads_stop (mux->collect);
2761 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2763 switch (transition) {
2764 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2766 case GST_STATE_CHANGE_PAUSED_TO_READY:
2767 gst_matroska_mux_reset (GST_ELEMENT (mux));
2769 case GST_STATE_CHANGE_READY_TO_NULL:
2779 gst_matroska_mux_set_property (GObject * object,
2780 guint prop_id, const GValue * value, GParamSpec * pspec)
2782 GstMatroskaMux *mux;
2784 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2785 mux = GST_MATROSKA_MUX (object);
2788 case ARG_WRITING_APP:
2789 if (!g_value_get_string (value)) {
2790 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2793 g_free (mux->writing_app);
2794 mux->writing_app = g_value_dup_string (value);
2796 case ARG_DOCTYPE_VERSION:
2797 mux->doctype_version = g_value_get_int (value);
2799 case ARG_MIN_INDEX_INTERVAL:
2800 mux->min_index_interval = g_value_get_int64 (value);
2803 mux->is_live = g_value_get_boolean (value);
2806 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2812 gst_matroska_mux_get_property (GObject * object,
2813 guint prop_id, GValue * value, GParamSpec * pspec)
2815 GstMatroskaMux *mux;
2817 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2818 mux = GST_MATROSKA_MUX (object);
2821 case ARG_WRITING_APP:
2822 g_value_set_string (value, mux->writing_app);
2824 case ARG_DOCTYPE_VERSION:
2825 g_value_set_int (value, mux->doctype_version);
2827 case ARG_MIN_INDEX_INTERVAL:
2828 g_value_set_int64 (value, mux->min_index_interval);
2831 g_value_set_boolean (value, mux->is_live);
2834 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);