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 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2010 ebml->caps = gst_caps_from_string ("video/webm");
2012 ebml->caps = gst_caps_from_string ("video/x-matroska");
2014 /* we start with a EBML header */
2015 doctype = mux->doctype;
2016 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2017 doctype, mux->doctype_version);
2018 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2020 /* the rest of the header is cached */
2021 gst_ebml_write_set_cache (ebml, 0x1000);
2023 /* start a segment */
2025 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2026 mux->segment_master = ebml->pos;
2028 if (!mux->is_live) {
2029 /* seekhead (table of contents) - we set the positions later */
2030 mux->seekhead_pos = ebml->pos;
2031 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2032 for (i = 0; seekhead_id[i] != 0; i++) {
2033 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2034 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2035 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2036 gst_ebml_write_master_finish (ebml, child);
2038 gst_ebml_write_master_finish (ebml, master);
2042 mux->info_pos = ebml->pos;
2043 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2044 for (i = 0; i < 4; i++) {
2045 segment_uid[i] = g_random_int ();
2047 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2048 (guint8 *) segment_uid, 16);
2049 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2050 mux->duration_pos = ebml->pos;
2052 if (!mux->is_live) {
2053 for (collected = mux->collect->data; collected;
2054 collected = g_slist_next (collected)) {
2055 GstMatroskaPad *collect_pad;
2056 GstFormat format = GST_FORMAT_TIME;
2058 gint64 trackduration;
2060 collect_pad = (GstMatroskaPad *) collected->data;
2061 thepad = collect_pad->collect.pad;
2063 /* Query the total length of the track. */
2064 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2065 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2066 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2067 GST_TIME_ARGS (trackduration));
2068 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2069 duration = (GstClockTime) trackduration;
2073 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2074 gst_guint64_to_gdouble (duration) /
2075 gst_guint64_to_gdouble (mux->time_scale));
2077 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2078 "GStreamer plugin version " PACKAGE_VERSION);
2079 if (mux->writing_app && mux->writing_app[0]) {
2080 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2082 g_get_current_time (&time);
2083 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2084 gst_ebml_write_master_finish (ebml, master);
2087 mux->tracks_pos = ebml->pos;
2088 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2090 for (collected = mux->collect->data; collected;
2091 collected = g_slist_next (collected)) {
2092 GstMatroskaPad *collect_pad;
2095 collect_pad = (GstMatroskaPad *) collected->data;
2096 thepad = collect_pad->collect.pad;
2098 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2099 collect_pad->track->codec_id != 0) {
2100 collect_pad->track->num = tracknum++;
2101 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2102 gst_matroska_mux_track_header (mux, collect_pad->track);
2103 gst_ebml_write_master_finish (ebml, child);
2106 gst_ebml_write_master_finish (ebml, master);
2108 /* lastly, flush the cache */
2109 gst_ebml_write_flush_cache (ebml, FALSE);
2113 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2116 /* TODO: more sensible tag mappings */
2119 const gchar *matroska_tagname;
2120 const gchar *gstreamer_tagname;
2124 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2125 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2126 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2127 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2128 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2129 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2130 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2131 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2132 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2133 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2134 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2135 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2136 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2137 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2138 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2140 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2142 guint64 simpletag_master;
2144 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2145 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2146 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2148 if (strcmp (tagname_gst, tag) == 0) {
2149 GValue src = { 0, };
2152 if (!gst_tag_list_copy_value (&src, list, tag))
2154 if ((dest = gst_value_serialize (&src))) {
2156 simpletag_master = gst_ebml_write_master_start (ebml,
2157 GST_MATROSKA_ID_SIMPLETAG);
2158 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2159 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2160 gst_ebml_write_master_finish (ebml, simpletag_master);
2163 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2165 g_value_unset (&src);
2173 * gst_matroska_mux_finish:
2174 * @mux: #GstMatroskaMux
2176 * Finish a new matroska file (write index etc...)
2179 gst_matroska_mux_finish (GstMatroskaMux * mux)
2181 GstEbmlWrite *ebml = mux->ebml_write;
2183 guint64 duration = 0;
2185 const GstTagList *tags;
2187 /* finish last cluster */
2189 gst_ebml_write_master_finish (ebml, mux->cluster);
2193 if (mux->index != NULL && !mux->is_live) {
2195 guint64 master, pointentry_master, trackpos_master;
2197 mux->cues_pos = ebml->pos;
2198 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2199 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2201 for (n = 0; n < mux->num_indexes; n++) {
2202 GstMatroskaIndex *idx = &mux->index[n];
2204 pointentry_master = gst_ebml_write_master_start (ebml,
2205 GST_MATROSKA_ID_POINTENTRY);
2206 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2207 idx->time / mux->time_scale);
2208 trackpos_master = gst_ebml_write_master_start (ebml,
2209 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2210 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2211 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2212 idx->pos - mux->segment_master);
2213 gst_ebml_write_master_finish (ebml, trackpos_master);
2214 gst_ebml_write_master_finish (ebml, pointentry_master);
2217 gst_ebml_write_master_finish (ebml, master);
2218 gst_ebml_write_flush_cache (ebml, FALSE);
2222 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2224 if (tags != NULL && !gst_tag_list_is_empty (tags) && !mux->is_live) {
2225 guint64 master_tags, master_tag;
2227 GST_DEBUG ("Writing tags");
2229 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2230 mux->tags_pos = ebml->pos;
2231 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2232 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2233 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2234 gst_ebml_write_master_finish (ebml, master_tag);
2235 gst_ebml_write_master_finish (ebml, master_tags);
2238 /* update seekhead. We know that:
2239 * - a seekhead contains 4 entries.
2240 * - order of entries is as above.
2241 * - a seekhead has a 4-byte header + 8-byte length
2242 * - each entry is 2-byte master, 2-byte ID pointer,
2243 * 2-byte length pointer, all 8/1-byte length, 4-
2244 * byte ID and 8-byte length pointer, where the
2245 * length pointer starts at 20.
2246 * - all entries are local to the segment (so pos - segment_master).
2247 * - so each entry is at 12 + 20 + num * 28. */
2248 if (!mux->is_live) {
2249 GST_DEBUG_OBJECT (mux, "not live");
2250 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2251 mux->info_pos - mux->segment_master);
2252 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2253 mux->tracks_pos - mux->segment_master);
2254 if (mux->index != NULL) {
2255 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2256 mux->cues_pos - mux->segment_master);
2259 guint64 my_pos = ebml->pos;
2261 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2262 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2263 gst_ebml_write_seek (ebml, my_pos);
2266 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2267 mux->tags_pos - mux->segment_master);
2270 guint64 my_pos = ebml->pos;
2272 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2273 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2274 gst_ebml_write_seek (ebml, my_pos);
2277 /* update duration */
2278 /* first get the overall duration */
2279 /* a released track may have left a duration in here */
2280 duration = mux->duration;
2281 for (collected = mux->collect->data; collected;
2282 collected = g_slist_next (collected)) {
2283 GstMatroskaPad *collect_pad;
2284 GstClockTime min_duration; /* observed minimum duration */
2286 collect_pad = (GstMatroskaPad *) collected->data;
2288 GST_DEBUG_OBJECT (mux,
2289 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2290 " end ts %" GST_TIME_FORMAT, collect_pad,
2291 GST_TIME_ARGS (collect_pad->start_ts),
2292 GST_TIME_ARGS (collect_pad->end_ts));
2294 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2295 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2297 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2298 if (collect_pad->duration < min_duration)
2299 collect_pad->duration = min_duration;
2300 GST_DEBUG_OBJECT (collect_pad,
2301 "final track duration: %" GST_TIME_FORMAT,
2302 GST_TIME_ARGS (collect_pad->duration));
2305 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2306 duration < collect_pad->duration)
2307 duration = collect_pad->duration;
2309 if (duration != 0) {
2310 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2311 GST_TIME_ARGS (duration));
2312 pos = mux->ebml_write->pos;
2313 gst_ebml_write_seek (ebml, mux->duration_pos);
2314 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2315 gst_guint64_to_gdouble (duration) /
2316 gst_guint64_to_gdouble (mux->time_scale));
2317 gst_ebml_write_seek (ebml, pos);
2320 guint64 my_pos = ebml->pos;
2322 gst_ebml_write_seek (ebml, mux->duration_pos);
2323 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2324 gst_ebml_write_seek (ebml, my_pos);
2327 GST_DEBUG_OBJECT (mux, "finishing segment");
2328 /* finish segment - this also writes element length */
2329 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2334 * gst_matroska_mux_best_pad:
2335 * @mux: #GstMatroskaMux
2336 * @popped: True if at least one buffer was popped from #GstCollectPads
2338 * Find a pad with the oldest data
2339 * (data from this pad should be written first).
2341 * Returns: Selected pad.
2343 static GstMatroskaPad *
2344 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2347 GstMatroskaPad *best = NULL;
2350 for (collected = mux->collect->data; collected;
2351 collected = g_slist_next (collected)) {
2352 GstMatroskaPad *collect_pad;
2354 collect_pad = (GstMatroskaPad *) collected->data;
2355 /* fetch a new buffer if needed */
2356 if (collect_pad->buffer == NULL) {
2357 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2358 (GstCollectData *) collect_pad);
2360 if (collect_pad->buffer != NULL)
2364 /* if we have a buffer check if it is better then the current best one */
2365 if (collect_pad->buffer != NULL) {
2366 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2367 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2368 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2369 GST_BUFFER_TIMESTAMP (best->buffer))) {
2379 * gst_matroska_mux_buffer_header:
2380 * @track: Track context.
2381 * @relative_timestamp: relative timestamp of the buffer
2382 * @flags: Buffer flags.
2384 * Create a buffer containing buffer header.
2386 * Returns: New buffer.
2389 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2390 gint16 relative_timestamp, int flags)
2394 hdr = gst_buffer_new_and_alloc (4);
2395 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2396 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2397 /* time relative to clustertime */
2398 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2401 GST_BUFFER_DATA (hdr)[3] = flags;
2406 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2407 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2408 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2411 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2412 GstMatroskaPad * collect_pad, GstBuffer * buf)
2414 GstMatroskaTrackVideoContext *ctx =
2415 (GstMatroskaTrackVideoContext *) collect_pad->track;
2416 const guint8 *data = GST_BUFFER_DATA (buf);
2417 guint size = GST_BUFFER_SIZE (buf);
2419 guint32 next_parse_offset;
2420 GstBuffer *ret = NULL;
2421 gboolean is_muxing_unit = FALSE;
2423 if (GST_BUFFER_SIZE (buf) < 13) {
2424 gst_buffer_unref (buf);
2428 /* Check if this buffer contains a picture or end-of-sequence packet */
2429 while (size >= 13) {
2430 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2431 gst_buffer_unref (buf);
2435 parse_code = GST_READ_UINT8 (data + 4);
2436 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2437 if (ctx->dirac_unit) {
2438 gst_buffer_unref (ctx->dirac_unit);
2439 ctx->dirac_unit = NULL;
2441 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2442 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2443 is_muxing_unit = TRUE;
2447 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2449 if (G_UNLIKELY (next_parse_offset == 0))
2452 data += next_parse_offset;
2453 size -= next_parse_offset;
2456 if (ctx->dirac_unit)
2457 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2459 ctx->dirac_unit = gst_buffer_ref (buf);
2461 if (is_muxing_unit) {
2462 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2463 ctx->dirac_unit = NULL;
2464 gst_buffer_copy_metadata (ret, buf,
2465 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2466 GST_BUFFER_COPY_CAPS);
2467 gst_buffer_unref (buf);
2469 gst_buffer_unref (buf);
2477 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2481 GValue streamheader = { 0 };
2482 GValue bufval = { 0 };
2483 GstBuffer *streamheader_buffer;
2484 GstEbmlWrite *ebml = mux->ebml_write;
2486 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2487 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2488 caps = gst_caps_from_string ("video/webm");
2490 caps = gst_caps_from_string ("video/x-matroska");
2492 s = gst_caps_get_structure (caps, 0);
2493 g_value_init (&streamheader, GST_TYPE_ARRAY);
2494 g_value_init (&bufval, GST_TYPE_BUFFER);
2495 gst_value_set_buffer (&bufval, streamheader_buffer);
2496 gst_value_array_append_value (&streamheader, &bufval);
2497 g_value_unset (&bufval);
2498 gst_structure_set_value (s, "streamheader", &streamheader);
2499 g_value_unset (&streamheader);
2500 gst_caps_unref (ebml->caps);
2505 * gst_matroska_mux_write_data:
2506 * @mux: #GstMatroskaMux
2507 * @collect_pad: #GstMatroskaPad with the data
2509 * Write collected data (called from gst_matroska_mux_collected).
2511 * Returns: Result of the gst_pad_push issued to write the data.
2513 static GstFlowReturn
2514 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2516 GstEbmlWrite *ebml = mux->ebml_write;
2517 GstBuffer *buf, *hdr;
2519 gboolean write_duration;
2520 gint16 relative_timestamp;
2521 gint64 relative_timestamp64;
2522 guint64 block_duration;
2523 gboolean is_video_keyframe = FALSE;
2526 buf = collect_pad->buffer;
2527 collect_pad->buffer = NULL;
2529 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2530 if (collect_pad->track->xiph_headers_to_skip > 0) {
2531 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2532 gst_buffer_unref (buf);
2533 --collect_pad->track->xiph_headers_to_skip;
2537 /* for dirac we have to queue up everything up to a picture unit */
2538 if (collect_pad->track->codec_id != NULL &&
2539 strcmp (collect_pad->track->codec_id,
2540 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2541 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2546 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2547 * this would wreak havoc with time stored in matroska file */
2548 /* TODO: maybe calculate a timestamp by using the previous timestamp
2549 * and default duration */
2550 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2551 GST_WARNING_OBJECT (collect_pad->collect.pad,
2552 "Invalid buffer timestamp; dropping buffer");
2553 gst_buffer_unref (buf);
2557 /* set the timestamp for outgoing buffers */
2558 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2560 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2561 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2562 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2563 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2564 is_video_keyframe = TRUE;
2568 /* start a new cluster every two seconds or at keyframe */
2569 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2570 || is_video_keyframe) {
2572 gst_ebml_write_master_finish (ebml, mux->cluster);
2573 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2574 mux->cluster_pos = ebml->pos;
2575 gst_ebml_write_set_cache (ebml, 0x20);
2577 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2578 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2579 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2581 GST_WARNING_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2582 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2584 gst_ebml_write_flush_cache (ebml, TRUE);
2585 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2586 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2587 mux->prev_cluster_size);
2592 mux->cluster_pos = ebml->pos;
2593 gst_ebml_write_set_cache (ebml, 0x20);
2594 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2595 GST_WARNING_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2596 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2597 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2598 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2599 gst_ebml_write_flush_cache (ebml, TRUE);
2600 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2603 /* update duration of this track */
2604 if (GST_BUFFER_DURATION_IS_VALID (buf))
2605 collect_pad->duration += GST_BUFFER_DURATION (buf);
2607 /* We currently write index entries for all video tracks or for the audio
2608 * track in a single-track audio file. This could be improved by keeping the
2609 * index only for the *first* video track. */
2611 /* TODO: index is useful for every track, should contain the number of
2612 * the block in the cluster which contains the timestamp, should also work
2613 * for files with multiple audio tracks.
2615 if (is_video_keyframe ||
2616 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2617 (mux->num_streams == 1))) {
2620 if (mux->min_index_interval != 0) {
2621 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2622 if (mux->index[last_idx].track == collect_pad->track->num)
2627 if (last_idx < 0 || mux->min_index_interval == 0 ||
2628 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2629 >= mux->min_index_interval)) {
2630 GstMatroskaIndex *idx;
2632 if (mux->num_indexes % 32 == 0) {
2633 mux->index = g_renew (GstMatroskaIndex, mux->index,
2634 mux->num_indexes + 32);
2636 idx = &mux->index[mux->num_indexes++];
2638 idx->pos = mux->cluster_pos;
2639 idx->time = GST_BUFFER_TIMESTAMP (buf);
2640 idx->track = collect_pad->track->num;
2644 /* Check if the duration differs from the default duration. */
2645 write_duration = FALSE;
2646 block_duration = GST_BUFFER_DURATION (buf);
2647 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2648 if (block_duration != collect_pad->track->default_duration) {
2649 write_duration = TRUE;
2652 GST_WARNING_OBJECT (mux, "block duration set as %" G_GUINT64_FORMAT,
2655 /* write the block, for doctype v2 use SimpleBlock if possible
2656 * one slice (*breath*).
2657 * FIXME: Need to do correct lacing! */
2658 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2659 if (relative_timestamp64 >= 0) {
2660 /* round the timestamp */
2661 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2663 /* round the timestamp */
2664 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2666 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2668 GST_WARNING_OBJECT (mux,
2669 "incoming timestamp %" G_GUINT64_FORMAT " relative timestamp output %"
2670 G_GUINT64_FORMAT, GST_BUFFER_TIMESTAMP (buf), relative_timestamp);
2671 if (mux->doctype_version > 1 && !write_duration) {
2673 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2676 gst_matroska_mux_create_buffer_header (collect_pad->track,
2677 relative_timestamp, flags);
2678 gst_ebml_write_set_cache (ebml, 0x40);
2679 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2680 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2681 gst_ebml_write_buffer (ebml, hdr);
2682 gst_ebml_write_flush_cache (ebml, FALSE);
2683 gst_ebml_write_buffer (ebml, buf);
2685 return gst_ebml_last_write_result (ebml);
2687 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2688 /* write and call order slightly unnatural,
2689 * but avoids seek and minizes pushing */
2690 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2692 gst_matroska_mux_create_buffer_header (collect_pad->track,
2693 relative_timestamp, 0);
2694 if (write_duration) {
2695 GST_WARNING_OBJECT (mux, "duration output as %" G_GUINT64_FORMAT,
2696 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2698 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2699 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2701 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2702 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2703 gst_ebml_write_buffer (ebml, hdr);
2704 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2705 gst_ebml_write_flush_cache (ebml, FALSE);
2706 gst_ebml_write_buffer (ebml, buf);
2707 return gst_ebml_last_write_result (ebml);
2713 * gst_matroska_mux_collected:
2714 * @pads: #GstCollectPads
2715 * @uuser_data: #GstMatroskaMux
2717 * Collectpads callback.
2719 * Returns: #GstFlowReturn
2721 static GstFlowReturn
2722 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2724 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2725 GstEbmlWrite *ebml = mux->ebml_write;
2726 GstMatroskaPad *best;
2730 GST_DEBUG_OBJECT (mux, "Collected pads");
2732 /* start with a header */
2733 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2734 if (mux->collect->data == NULL) {
2735 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2736 ("No input streams configured"));
2737 return GST_FLOW_ERROR;
2739 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2740 gst_ebml_start_streamheader (ebml);
2741 gst_matroska_mux_start (mux);
2742 gst_matroska_mux_stop_streamheader (mux);
2743 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2747 /* which stream to write from? */
2748 best = gst_matroska_mux_best_pad (mux, &popped);
2750 /* if there is no best pad, we have reached EOS */
2752 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2753 gst_matroska_mux_finish (mux);
2754 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2755 ret = GST_FLOW_UNEXPECTED;
2758 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2759 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2760 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2761 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2763 /* make note of first and last encountered timestamps, so we can calculate
2764 * the actual duration later when we send an updated header on eos */
2765 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2766 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2767 GstClockTime end_ts = start_ts;
2769 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2770 end_ts += GST_BUFFER_DURATION (best->buffer);
2771 else if (best->track->default_duration)
2772 end_ts += best->track->default_duration;
2774 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2775 best->end_ts = end_ts;
2777 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2778 start_ts < best->start_ts))
2779 best->start_ts = start_ts;
2782 /* write one buffer */
2783 ret = gst_matroska_mux_write_data (mux, best);
2784 } while (ret == GST_FLOW_OK && !popped);
2791 * gst_matroska_mux_change_state:
2792 * @element: #GstMatroskaMux
2793 * @transition: State change transition.
2795 * Change the muxer state.
2797 * Returns: #GstStateChangeReturn
2799 static GstStateChangeReturn
2800 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2802 GstStateChangeReturn ret;
2803 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2805 switch (transition) {
2806 case GST_STATE_CHANGE_NULL_TO_READY:
2808 case GST_STATE_CHANGE_READY_TO_PAUSED:
2809 gst_collect_pads_start (mux->collect);
2811 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2813 case GST_STATE_CHANGE_PAUSED_TO_READY:
2814 gst_collect_pads_stop (mux->collect);
2820 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2822 switch (transition) {
2823 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2825 case GST_STATE_CHANGE_PAUSED_TO_READY:
2826 gst_matroska_mux_reset (GST_ELEMENT (mux));
2828 case GST_STATE_CHANGE_READY_TO_NULL:
2838 gst_matroska_mux_set_property (GObject * object,
2839 guint prop_id, const GValue * value, GParamSpec * pspec)
2841 GstMatroskaMux *mux;
2843 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2844 mux = GST_MATROSKA_MUX (object);
2847 case ARG_WRITING_APP:
2848 if (!g_value_get_string (value)) {
2849 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2852 g_free (mux->writing_app);
2853 mux->writing_app = g_value_dup_string (value);
2855 case ARG_DOCTYPE_VERSION:
2856 mux->doctype_version = g_value_get_int (value);
2858 case ARG_MIN_INDEX_INTERVAL:
2859 mux->min_index_interval = g_value_get_int64 (value);
2862 mux->is_live = g_value_get_boolean (value);
2865 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2871 gst_matroska_mux_get_property (GObject * object,
2872 guint prop_id, GValue * value, GParamSpec * pspec)
2874 GstMatroskaMux *mux;
2876 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2877 mux = GST_MATROSKA_MUX (object);
2880 case ARG_WRITING_APP:
2881 g_value_set_string (value, mux->writing_app);
2883 case ARG_DOCTYPE_VERSION:
2884 g_value_set_int (value, mux->doctype_version);
2886 case ARG_MIN_INDEX_INTERVAL:
2887 g_value_set_int64 (value, mux->min_index_interval);
2890 g_value_set_boolean (value, mux->is_live);
2893 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);