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 if (!mux->is_live) {
2024 /* seekhead (table of contents) - we set the positions later */
2025 mux->seekhead_pos = ebml->pos;
2026 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2027 for (i = 0; seekhead_id[i] != 0; i++) {
2028 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2029 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2030 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2031 gst_ebml_write_master_finish (ebml, child);
2033 gst_ebml_write_master_finish (ebml, master);
2037 mux->info_pos = ebml->pos;
2038 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2039 for (i = 0; i < 4; i++) {
2040 segment_uid[i] = g_random_int ();
2042 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2043 (guint8 *) segment_uid, 16);
2044 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2045 mux->duration_pos = ebml->pos;
2047 for (collected = mux->collect->data; collected;
2048 collected = g_slist_next (collected)) {
2049 GstMatroskaPad *collect_pad;
2050 GstFormat format = GST_FORMAT_TIME;
2052 gint64 trackduration;
2054 collect_pad = (GstMatroskaPad *) collected->data;
2055 thepad = collect_pad->collect.pad;
2057 /* Query the total length of the track. */
2058 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2059 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2060 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2061 GST_TIME_ARGS (trackduration));
2062 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2063 duration = (GstClockTime) trackduration;
2067 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2068 gst_guint64_to_gdouble (duration) /
2069 gst_guint64_to_gdouble (mux->time_scale));
2071 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2072 "GStreamer plugin version " PACKAGE_VERSION);
2073 if (mux->writing_app && mux->writing_app[0]) {
2074 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2076 g_get_current_time (&time);
2077 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2078 gst_ebml_write_master_finish (ebml, master);
2081 mux->tracks_pos = ebml->pos;
2082 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2084 for (collected = mux->collect->data; collected;
2085 collected = g_slist_next (collected)) {
2086 GstMatroskaPad *collect_pad;
2089 collect_pad = (GstMatroskaPad *) collected->data;
2090 thepad = collect_pad->collect.pad;
2092 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2093 collect_pad->track->codec_id != 0) {
2094 collect_pad->track->num = tracknum++;
2095 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2096 gst_matroska_mux_track_header (mux, collect_pad->track);
2097 gst_ebml_write_master_finish (ebml, child);
2100 gst_ebml_write_master_finish (ebml, master);
2102 /* lastly, flush the cache */
2103 gst_ebml_write_flush_cache (ebml);
2107 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2110 /* TODO: more sensible tag mappings */
2113 const gchar *matroska_tagname;
2114 const gchar *gstreamer_tagname;
2118 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2119 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2120 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2121 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2122 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2123 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2124 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2125 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2126 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2127 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2128 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2129 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2130 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2131 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2132 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2134 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2136 guint64 simpletag_master;
2138 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2139 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2140 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2142 if (strcmp (tagname_gst, tag) == 0) {
2143 GValue src = { 0, };
2146 if (!gst_tag_list_copy_value (&src, list, tag))
2148 if ((dest = gst_value_serialize (&src))) {
2150 simpletag_master = gst_ebml_write_master_start (ebml,
2151 GST_MATROSKA_ID_SIMPLETAG);
2152 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2153 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2154 gst_ebml_write_master_finish (ebml, simpletag_master);
2157 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2159 g_value_unset (&src);
2167 * gst_matroska_mux_finish:
2168 * @mux: #GstMatroskaMux
2170 * Finish a new matroska file (write index etc...)
2173 gst_matroska_mux_finish (GstMatroskaMux * mux)
2175 GstEbmlWrite *ebml = mux->ebml_write;
2177 guint64 duration = 0;
2179 const GstTagList *tags;
2181 /* finish last cluster */
2183 gst_ebml_write_master_finish (ebml, mux->cluster);
2187 if (mux->index != NULL && !mux->is_live) {
2189 guint64 master, pointentry_master, trackpos_master;
2191 mux->cues_pos = ebml->pos;
2192 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2193 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2195 for (n = 0; n < mux->num_indexes; n++) {
2196 GstMatroskaIndex *idx = &mux->index[n];
2198 pointentry_master = gst_ebml_write_master_start (ebml,
2199 GST_MATROSKA_ID_POINTENTRY);
2200 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2201 idx->time / mux->time_scale);
2202 trackpos_master = gst_ebml_write_master_start (ebml,
2203 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2204 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2205 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2206 idx->pos - mux->segment_master);
2207 gst_ebml_write_master_finish (ebml, trackpos_master);
2208 gst_ebml_write_master_finish (ebml, pointentry_master);
2211 gst_ebml_write_master_finish (ebml, master);
2212 gst_ebml_write_flush_cache (ebml);
2216 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2218 if (tags != NULL && !gst_tag_list_is_empty (tags) && !mux->is_live) {
2219 guint64 master_tags, master_tag;
2221 GST_DEBUG ("Writing tags");
2223 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2224 mux->tags_pos = ebml->pos;
2225 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2226 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2227 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2228 gst_ebml_write_master_finish (ebml, master_tag);
2229 gst_ebml_write_master_finish (ebml, master_tags);
2232 /* update seekhead. We know that:
2233 * - a seekhead contains 4 entries.
2234 * - order of entries is as above.
2235 * - a seekhead has a 4-byte header + 8-byte length
2236 * - each entry is 2-byte master, 2-byte ID pointer,
2237 * 2-byte length pointer, all 8/1-byte length, 4-
2238 * byte ID and 8-byte length pointer, where the
2239 * length pointer starts at 20.
2240 * - all entries are local to the segment (so pos - segment_master).
2241 * - so each entry is at 12 + 20 + num * 28. */
2242 if (!mux->is_live) {
2243 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2244 mux->info_pos - mux->segment_master);
2245 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2246 mux->tracks_pos - mux->segment_master);
2247 if (mux->index != NULL) {
2248 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2249 mux->cues_pos - mux->segment_master);
2252 guint64 my_pos = ebml->pos;
2254 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2255 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2256 gst_ebml_write_seek (ebml, my_pos);
2259 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2260 mux->tags_pos - mux->segment_master);
2263 guint64 my_pos = ebml->pos;
2265 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2266 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2267 gst_ebml_write_seek (ebml, my_pos);
2270 /* update duration */
2271 /* first get the overall duration */
2272 /* a released track may have left a duration in here */
2273 duration = mux->duration;
2274 for (collected = mux->collect->data; collected;
2275 collected = g_slist_next (collected)) {
2276 GstMatroskaPad *collect_pad;
2277 GstClockTime min_duration; /* observed minimum duration */
2279 collect_pad = (GstMatroskaPad *) collected->data;
2281 GST_DEBUG_OBJECT (mux,
2282 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2283 " end ts %" GST_TIME_FORMAT, collect_pad,
2284 GST_TIME_ARGS (collect_pad->start_ts),
2285 GST_TIME_ARGS (collect_pad->end_ts));
2287 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2288 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2290 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2291 if (collect_pad->duration < min_duration)
2292 collect_pad->duration = min_duration;
2293 GST_DEBUG_OBJECT (collect_pad,
2294 "final track duration: %" GST_TIME_FORMAT,
2295 GST_TIME_ARGS (collect_pad->duration));
2298 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2299 duration < collect_pad->duration)
2300 duration = collect_pad->duration;
2302 if (duration != 0) {
2303 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2304 GST_TIME_ARGS (duration));
2305 pos = mux->ebml_write->pos;
2306 gst_ebml_write_seek (ebml, mux->duration_pos);
2307 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2308 gst_guint64_to_gdouble (duration) /
2309 gst_guint64_to_gdouble (mux->time_scale));
2310 gst_ebml_write_seek (ebml, pos);
2313 guint64 my_pos = ebml->pos;
2315 gst_ebml_write_seek (ebml, mux->duration_pos);
2316 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2317 gst_ebml_write_seek (ebml, my_pos);
2320 /* finish segment - this also writes element length */
2321 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2326 * gst_matroska_mux_best_pad:
2327 * @mux: #GstMatroskaMux
2328 * @popped: True if at least one buffer was popped from #GstCollectPads
2330 * Find a pad with the oldest data
2331 * (data from this pad should be written first).
2333 * Returns: Selected pad.
2335 static GstMatroskaPad *
2336 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2339 GstMatroskaPad *best = NULL;
2342 for (collected = mux->collect->data; collected;
2343 collected = g_slist_next (collected)) {
2344 GstMatroskaPad *collect_pad;
2346 collect_pad = (GstMatroskaPad *) collected->data;
2347 /* fetch a new buffer if needed */
2348 if (collect_pad->buffer == NULL) {
2349 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2350 (GstCollectData *) collect_pad);
2352 if (collect_pad->buffer != NULL)
2356 /* if we have a buffer check if it is better then the current best one */
2357 if (collect_pad->buffer != NULL) {
2358 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2359 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2360 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2361 GST_BUFFER_TIMESTAMP (best->buffer))) {
2371 * gst_matroska_mux_buffer_header:
2372 * @track: Track context.
2373 * @relative_timestamp: relative timestamp of the buffer
2374 * @flags: Buffer flags.
2376 * Create a buffer containing buffer header.
2378 * Returns: New buffer.
2381 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2382 gint16 relative_timestamp, int flags)
2386 hdr = gst_buffer_new_and_alloc (4);
2387 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2388 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2389 /* time relative to clustertime */
2390 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2393 GST_BUFFER_DATA (hdr)[3] = flags;
2398 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2399 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2400 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2403 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2404 GstMatroskaPad * collect_pad, GstBuffer * buf)
2406 GstMatroskaTrackVideoContext *ctx =
2407 (GstMatroskaTrackVideoContext *) collect_pad->track;
2408 const guint8 *data = GST_BUFFER_DATA (buf);
2409 guint size = GST_BUFFER_SIZE (buf);
2411 guint32 next_parse_offset;
2412 GstBuffer *ret = NULL;
2413 gboolean is_muxing_unit = FALSE;
2415 if (GST_BUFFER_SIZE (buf) < 13) {
2416 gst_buffer_unref (buf);
2420 /* Check if this buffer contains a picture or end-of-sequence packet */
2421 while (size >= 13) {
2422 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2423 gst_buffer_unref (buf);
2427 parse_code = GST_READ_UINT8 (data + 4);
2428 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2429 if (ctx->dirac_unit) {
2430 gst_buffer_unref (ctx->dirac_unit);
2431 ctx->dirac_unit = NULL;
2433 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2434 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2435 is_muxing_unit = TRUE;
2439 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2441 if (G_UNLIKELY (next_parse_offset == 0))
2444 data += next_parse_offset;
2445 size -= next_parse_offset;
2448 if (ctx->dirac_unit)
2449 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2451 ctx->dirac_unit = gst_buffer_ref (buf);
2453 if (is_muxing_unit) {
2454 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2455 ctx->dirac_unit = NULL;
2456 gst_buffer_copy_metadata (ret, buf,
2457 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2458 GST_BUFFER_COPY_CAPS);
2459 gst_buffer_unref (buf);
2461 gst_buffer_unref (buf);
2469 * gst_matroska_mux_write_data:
2470 * @mux: #GstMatroskaMux
2471 * @collect_pad: #GstMatroskaPad with the data
2473 * Write collected data (called from gst_matroska_mux_collected).
2475 * Returns: Result of the gst_pad_push issued to write the data.
2477 static GstFlowReturn
2478 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2480 GstEbmlWrite *ebml = mux->ebml_write;
2481 GstBuffer *buf, *hdr;
2483 gboolean write_duration;
2484 gint16 relative_timestamp;
2485 gint64 relative_timestamp64;
2486 guint64 block_duration;
2487 gboolean is_video_keyframe = FALSE;
2490 buf = collect_pad->buffer;
2491 collect_pad->buffer = NULL;
2493 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2494 if (collect_pad->track->xiph_headers_to_skip > 0) {
2495 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2496 gst_buffer_unref (buf);
2497 --collect_pad->track->xiph_headers_to_skip;
2501 /* for dirac we have to queue up everything up to a picture unit */
2502 if (collect_pad->track->codec_id != NULL &&
2503 strcmp (collect_pad->track->codec_id,
2504 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2505 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2510 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2511 * this would wreak havoc with time stored in matroska file */
2512 /* TODO: maybe calculate a timestamp by using the previous timestamp
2513 * and default duration */
2514 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2515 GST_WARNING_OBJECT (collect_pad->collect.pad,
2516 "Invalid buffer timestamp; dropping buffer");
2517 gst_buffer_unref (buf);
2521 /* set the timestamp for outgoing buffers */
2522 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2524 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2525 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2526 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2527 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2528 is_video_keyframe = TRUE;
2532 /* start a new cluster every two seconds or at keyframe */
2533 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2534 || is_video_keyframe) {
2536 gst_ebml_write_master_finish (ebml, mux->cluster);
2537 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2538 mux->cluster_pos = ebml->pos;
2539 gst_ebml_write_set_cache (ebml, 0x20);
2541 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2542 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2543 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2544 gst_ebml_write_flush_cache (ebml);
2545 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2546 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2547 mux->prev_cluster_size);
2552 mux->cluster_pos = ebml->pos;
2553 gst_ebml_write_set_cache (ebml, 0x20);
2554 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2555 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2556 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2557 gst_ebml_write_flush_cache (ebml);
2558 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2561 /* update duration of this track */
2562 if (GST_BUFFER_DURATION_IS_VALID (buf))
2563 collect_pad->duration += GST_BUFFER_DURATION (buf);
2565 /* We currently write index entries for all video tracks or for the audio
2566 * track in a single-track audio file. This could be improved by keeping the
2567 * index only for the *first* video track. */
2569 /* TODO: index is useful for every track, should contain the number of
2570 * the block in the cluster which contains the timestamp, should also work
2571 * for files with multiple audio tracks.
2573 if (is_video_keyframe ||
2574 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2575 (mux->num_streams == 1))) {
2578 if (mux->min_index_interval != 0) {
2579 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2580 if (mux->index[last_idx].track == collect_pad->track->num)
2585 if (last_idx < 0 || mux->min_index_interval == 0 ||
2586 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2587 >= mux->min_index_interval)) {
2588 GstMatroskaIndex *idx;
2590 if (mux->num_indexes % 32 == 0) {
2591 mux->index = g_renew (GstMatroskaIndex, mux->index,
2592 mux->num_indexes + 32);
2594 idx = &mux->index[mux->num_indexes++];
2596 idx->pos = mux->cluster_pos;
2597 idx->time = GST_BUFFER_TIMESTAMP (buf);
2598 idx->track = collect_pad->track->num;
2602 /* Check if the duration differs from the default duration. */
2603 write_duration = FALSE;
2604 block_duration = GST_BUFFER_DURATION (buf);
2605 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2606 if (block_duration != collect_pad->track->default_duration) {
2607 write_duration = TRUE;
2611 /* write the block, for doctype v2 use SimpleBlock if possible
2612 * one slice (*breath*).
2613 * FIXME: Need to do correct lacing! */
2614 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2615 if (relative_timestamp64 >= 0) {
2616 /* round the timestamp */
2617 relative_timestamp64 += mux->time_scale / 2;
2619 /* round the timestamp */
2620 relative_timestamp64 -= mux->time_scale / 2;
2622 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2623 if (mux->doctype_version > 1 && !write_duration) {
2625 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2628 gst_matroska_mux_create_buffer_header (collect_pad->track,
2629 relative_timestamp, flags);
2630 gst_ebml_write_set_cache (ebml, 0x40);
2631 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2632 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2633 gst_ebml_write_buffer (ebml, hdr);
2634 gst_ebml_write_flush_cache (ebml);
2635 gst_ebml_write_buffer (ebml, buf);
2637 return gst_ebml_last_write_result (ebml);
2639 gst_ebml_write_set_cache (ebml, 0x40);
2640 /* write and call order slightly unnatural,
2641 * but avoids seek and minizes pushing */
2642 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2644 gst_matroska_mux_create_buffer_header (collect_pad->track,
2645 relative_timestamp, 0);
2646 if (write_duration) {
2647 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2648 block_duration / mux->time_scale);
2650 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2651 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2652 gst_ebml_write_buffer (ebml, hdr);
2653 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2654 gst_ebml_write_flush_cache (ebml);
2655 gst_ebml_write_buffer (ebml, buf);
2656 return gst_ebml_last_write_result (ebml);
2662 * gst_matroska_mux_collected:
2663 * @pads: #GstCollectPads
2664 * @uuser_data: #GstMatroskaMux
2666 * Collectpads callback.
2668 * Returns: #GstFlowReturn
2670 static GstFlowReturn
2671 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2673 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2674 GstMatroskaPad *best;
2678 GST_DEBUG_OBJECT (mux, "Collected pads");
2680 /* start with a header */
2681 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2682 if (mux->collect->data == NULL) {
2683 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2684 ("No input streams configured"));
2685 return GST_FLOW_ERROR;
2687 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2688 gst_matroska_mux_start (mux);
2689 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2693 /* which stream to write from? */
2694 best = gst_matroska_mux_best_pad (mux, &popped);
2696 /* if there is no best pad, we have reached EOS */
2698 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2699 gst_matroska_mux_finish (mux);
2700 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2701 ret = GST_FLOW_UNEXPECTED;
2704 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2705 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2706 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2707 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2709 /* make note of first and last encountered timestamps, so we can calculate
2710 * the actual duration later when we send an updated header on eos */
2711 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2712 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2713 GstClockTime end_ts = start_ts;
2715 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2716 end_ts += GST_BUFFER_DURATION (best->buffer);
2717 else if (best->track->default_duration)
2718 end_ts += best->track->default_duration;
2720 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2721 best->end_ts = end_ts;
2723 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2724 start_ts < best->start_ts))
2725 best->start_ts = start_ts;
2728 /* write one buffer */
2729 ret = gst_matroska_mux_write_data (mux, best);
2730 } while (ret == GST_FLOW_OK && !popped);
2737 * gst_matroska_mux_change_state:
2738 * @element: #GstMatroskaMux
2739 * @transition: State change transition.
2741 * Change the muxer state.
2743 * Returns: #GstStateChangeReturn
2745 static GstStateChangeReturn
2746 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2748 GstStateChangeReturn ret;
2749 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2751 switch (transition) {
2752 case GST_STATE_CHANGE_NULL_TO_READY:
2754 case GST_STATE_CHANGE_READY_TO_PAUSED:
2755 gst_collect_pads_start (mux->collect);
2757 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2759 case GST_STATE_CHANGE_PAUSED_TO_READY:
2760 gst_collect_pads_stop (mux->collect);
2766 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2768 switch (transition) {
2769 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2771 case GST_STATE_CHANGE_PAUSED_TO_READY:
2772 gst_matroska_mux_reset (GST_ELEMENT (mux));
2774 case GST_STATE_CHANGE_READY_TO_NULL:
2784 gst_matroska_mux_set_property (GObject * object,
2785 guint prop_id, const GValue * value, GParamSpec * pspec)
2787 GstMatroskaMux *mux;
2789 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2790 mux = GST_MATROSKA_MUX (object);
2793 case ARG_WRITING_APP:
2794 if (!g_value_get_string (value)) {
2795 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2798 g_free (mux->writing_app);
2799 mux->writing_app = g_value_dup_string (value);
2801 case ARG_DOCTYPE_VERSION:
2802 mux->doctype_version = g_value_get_int (value);
2804 case ARG_MIN_INDEX_INTERVAL:
2805 mux->min_index_interval = g_value_get_int64 (value);
2808 mux->is_live = g_value_get_boolean (value);
2811 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2817 gst_matroska_mux_get_property (GObject * object,
2818 guint prop_id, GValue * value, GParamSpec * pspec)
2820 GstMatroskaMux *mux;
2822 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2823 mux = GST_MATROSKA_MUX (object);
2826 case ARG_WRITING_APP:
2827 g_value_set_string (value, mux->writing_app);
2829 case ARG_DOCTYPE_VERSION:
2830 g_value_set_int (value, mux->doctype_version);
2832 case ARG_MIN_INDEX_INTERVAL:
2833 g_value_set_int64 (value, mux->min_index_interval);
2836 g_value_set_boolean (value, mux->is_live);
2839 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);