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_STREAMABLE 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 | G_PARAM_STATIC_STRINGS));
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,
304 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
306 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
307 "entries", "An index entry is created every so many nanoseconds.",
308 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
309 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
310 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
311 g_param_spec_boolean ("streamable", "Determines whether output should "
312 "be streamable", "If set to true, the output should be as if it is "
313 "to be streamed and hence no indexes written or duration written.",
315 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
317 gstelement_class->change_state =
318 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
319 gstelement_class->request_new_pad =
320 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
321 gstelement_class->release_pad =
322 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
327 * gst_matroska_mux_init:
328 * @mux: #GstMatroskaMux that should be initialized.
329 * @g_class: Class of the muxer.
331 * Matroska muxer constructor.
334 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
336 GstPadTemplate *templ;
339 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
340 mux->srcpad = gst_pad_new_from_template (templ, "src");
342 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
343 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
345 mux->collect = gst_collect_pads_new ();
346 gst_collect_pads_set_function (mux->collect,
347 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
350 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
351 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
353 /* property defaults */
354 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
355 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
356 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
357 mux->streamable = DEFAULT_STREAMABLE;
359 /* initialize internal variables */
361 mux->num_streams = 0;
362 mux->num_a_streams = 0;
363 mux->num_t_streams = 0;
364 mux->num_v_streams = 0;
366 /* initialize remaining variables */
367 gst_matroska_mux_reset (GST_ELEMENT (mux));
372 * gst_matroska_mux_finalize:
373 * @object: #GstMatroskaMux that should be finalized.
375 * Finalize matroska muxer.
378 gst_matroska_mux_finalize (GObject * object)
380 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
382 gst_object_unref (mux->collect);
383 gst_object_unref (mux->ebml_write);
384 if (mux->writing_app)
385 g_free (mux->writing_app);
387 G_OBJECT_CLASS (parent_class)->finalize (object);
392 * gst_matroska_mux_create_uid:
394 * Generate new unused track UID.
396 * Returns: New track UID.
399 gst_matroska_mux_create_uid (void)
406 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
411 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
412 for (i = 0; i < used_uids->len; i++) {
413 if (g_array_index (used_uids, guint64, i) == uid) {
418 g_array_append_val (used_uids, uid);
421 G_UNLOCK (used_uids);
427 * gst_matroska_pad_reset:
428 * @collect_pad: the #GstMatroskaPad
430 * Reset and/or release resources of a matroska collect pad.
433 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
436 GstMatroskaTrackType type = 0;
438 /* free track information */
439 if (collect_pad->track != NULL) {
440 /* retrieve for optional later use */
441 name = collect_pad->track->name;
442 type = collect_pad->track->type;
443 /* extra for video */
444 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
445 GstMatroskaTrackVideoContext *ctx =
446 (GstMatroskaTrackVideoContext *) collect_pad->track;
448 if (ctx->dirac_unit) {
449 gst_buffer_unref (ctx->dirac_unit);
450 ctx->dirac_unit = NULL;
453 g_free (collect_pad->track->codec_id);
454 g_free (collect_pad->track->codec_name);
456 g_free (collect_pad->track->name);
457 g_free (collect_pad->track->language);
458 g_free (collect_pad->track->codec_priv);
459 g_free (collect_pad->track);
460 collect_pad->track = NULL;
463 /* free cached buffer */
464 if (collect_pad->buffer != NULL) {
465 gst_buffer_unref (collect_pad->buffer);
466 collect_pad->buffer = NULL;
469 if (!full && type != 0) {
470 GstMatroskaTrackContext *context;
472 /* create a fresh context */
474 case GST_MATROSKA_TRACK_TYPE_VIDEO:
475 context = (GstMatroskaTrackContext *)
476 g_new0 (GstMatroskaTrackVideoContext, 1);
478 case GST_MATROSKA_TRACK_TYPE_AUDIO:
479 context = (GstMatroskaTrackContext *)
480 g_new0 (GstMatroskaTrackAudioContext, 1);
482 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
483 context = (GstMatroskaTrackContext *)
484 g_new0 (GstMatroskaTrackSubtitleContext, 1);
487 g_assert_not_reached ();
491 context->type = type;
492 context->name = name;
493 /* TODO: check default values for the context */
494 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
495 collect_pad->track = context;
496 collect_pad->buffer = NULL;
497 collect_pad->duration = 0;
498 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
499 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
504 * gst_matroska_pad_free:
505 * @collect_pad: the #GstMatroskaPad
507 * Release resources of a matroska collect pad.
510 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
512 gst_matroska_pad_reset (collect_pad, TRUE);
517 * gst_matroska_mux_reset:
518 * @element: #GstMatroskaMux that should be reseted.
520 * Reset matroska muxer back to initial state.
523 gst_matroska_mux_reset (GstElement * element)
525 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
528 /* reset EBML write */
529 gst_ebml_write_reset (mux->ebml_write);
532 mux->state = GST_MATROSKA_MUX_STATE_START;
534 /* clean up existing streams */
536 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
537 GstMatroskaPad *collect_pad;
539 collect_pad = (GstMatroskaPad *) walk->data;
541 /* reset collect pad to pristine state */
542 gst_matroska_pad_reset (collect_pad, FALSE);
546 mux->num_indexes = 0;
551 mux->time_scale = GST_MSECOND;
552 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
557 mux->cluster_time = 0;
558 mux->cluster_pos = 0;
559 mux->prev_cluster_size = 0;
562 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
566 * gst_matroska_mux_handle_src_event:
567 * @pad: Pad which received the event.
568 * @event: Received event.
570 * handle events - copied from oggmux without understanding
572 * Returns: #TRUE on success.
575 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
579 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
583 /* disable seeking for now */
589 return gst_pad_event_default (pad, event);
593 * gst_matroska_mux_handle_sink_event:
594 * @pad: Pad which received the event.
595 * @event: Received event.
597 * handle events - informational ones like tags
599 * Returns: #TRUE on success.
602 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
604 GstMatroskaTrackContext *context;
605 GstMatroskaPad *collect_pad;
610 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
612 switch (GST_EVENT_TYPE (event)) {
616 GST_DEBUG_OBJECT (mux, "received tag event");
617 gst_event_parse_tag (event, &list);
619 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
620 g_assert (collect_pad);
621 context = collect_pad->track;
624 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
625 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
626 const gchar *lang_code;
628 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
630 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
631 context->language = g_strdup (lang_code);
633 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
638 /* FIXME: what about stream-specific tags? */
639 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
640 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
642 /* handled this, don't want collectpads to forward it downstream */
644 gst_event_unref (event);
647 case GST_EVENT_NEWSEGMENT:
648 /* We don't support NEWSEGMENT events */
650 gst_event_unref (event);
656 /* now GstCollectPads can take care of the rest, e.g. EOS */
658 ret = mux->collect_event (pad, event);
660 gst_object_unref (mux);
667 * gst_matroska_mux_video_pad_setcaps:
668 * @pad: Pad which got the caps.
671 * Setcaps function for video sink pad.
673 * Returns: #TRUE on success.
676 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
678 GstMatroskaTrackContext *context = NULL;
679 GstMatroskaTrackVideoContext *videocontext;
681 GstMatroskaPad *collect_pad;
682 GstStructure *structure;
683 const gchar *mimetype;
684 const GValue *value = NULL;
685 const GstBuffer *codec_buf = NULL;
686 gint width, height, pixel_width, pixel_height;
688 gboolean interlaced = FALSE;
690 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
693 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
694 g_assert (collect_pad);
695 context = collect_pad->track;
697 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
698 videocontext = (GstMatroskaTrackVideoContext *) context;
700 /* gst -> matroska ID'ing */
701 structure = gst_caps_get_structure (caps, 0);
703 mimetype = gst_structure_get_name (structure);
705 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
707 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
709 if (!strcmp (mimetype, "video/x-theora")) {
710 /* we'll extract the details later from the theora identification header */
714 /* get general properties */
715 /* spec says it is mandatory */
716 if (!gst_structure_get_int (structure, "width", &width) ||
717 !gst_structure_get_int (structure, "height", &height))
720 videocontext->pixel_width = width;
721 videocontext->pixel_height = height;
722 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
724 context->default_duration =
725 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
726 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
727 GST_TIME_ARGS (context->default_duration));
729 context->default_duration = 0;
731 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
732 &pixel_width, &pixel_height)) {
733 if (pixel_width > pixel_height) {
734 videocontext->display_width = width * pixel_width / pixel_height;
735 videocontext->display_height = height;
736 } else if (pixel_width < pixel_height) {
737 videocontext->display_width = width;
738 videocontext->display_height = height * pixel_height / pixel_width;
740 videocontext->display_width = 0;
741 videocontext->display_height = 0;
744 videocontext->display_width = 0;
745 videocontext->display_height = 0;
750 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
751 videocontext->fourcc = 0;
753 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
754 * data and other settings
758 /* extract codec_data, may turn out needed */
759 value = gst_structure_get_value (structure, "codec_data");
761 codec_buf = gst_value_get_buffer (value);
764 if (!strcmp (mimetype, "video/x-raw-yuv")) {
765 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
766 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
767 } else if (!strcmp (mimetype, "image/jpeg")) {
768 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
769 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
770 ||!strcmp (mimetype, "video/x-huffyuv")
771 || !strcmp (mimetype, "video/x-divx")
772 || !strcmp (mimetype, "video/x-dv")
773 || !strcmp (mimetype, "video/x-h263")
774 || !strcmp (mimetype, "video/x-msmpeg")
775 || !strcmp (mimetype, "video/x-wmv")) {
776 gst_riff_strf_vids *bih;
777 gint size = sizeof (gst_riff_strf_vids);
780 if (!strcmp (mimetype, "video/x-xvid"))
781 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
782 else if (!strcmp (mimetype, "video/x-huffyuv"))
783 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
784 else if (!strcmp (mimetype, "video/x-dv"))
785 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
786 else if (!strcmp (mimetype, "video/x-h263"))
787 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
788 else if (!strcmp (mimetype, "video/x-divx")) {
791 gst_structure_get_int (structure, "divxversion", &divxversion);
792 switch (divxversion) {
794 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
797 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
800 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
803 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
806 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
807 switch (msmpegversion) {
809 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
812 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
818 } else if (!strcmp (mimetype, "video/x-wmv")) {
821 if (gst_structure_get_fourcc (structure, "format", &format)) {
823 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
824 if (wmvversion == 2) {
825 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
826 } else if (wmvversion == 1) {
827 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
828 } else if (wmvversion == 3) {
829 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
837 bih = g_new0 (gst_riff_strf_vids, 1);
838 GST_WRITE_UINT32_LE (&bih->size, size);
839 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
840 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
841 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
842 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
843 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
844 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
845 videocontext->pixel_height * 3);
847 /* process codec private/initialization data, if any */
849 size += GST_BUFFER_SIZE (codec_buf);
850 bih = g_realloc (bih, size);
851 GST_WRITE_UINT32_LE (&bih->size, size);
852 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
853 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
856 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
857 context->codec_priv = (gpointer) bih;
858 context->codec_priv_size = size;
859 } else if (!strcmp (mimetype, "video/x-h264")) {
860 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
862 if (context->codec_priv != NULL) {
863 g_free (context->codec_priv);
864 context->codec_priv = NULL;
865 context->codec_priv_size = 0;
868 /* Create avcC header */
869 if (codec_buf != NULL) {
870 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
871 context->codec_priv = g_malloc0 (context->codec_priv_size);
872 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
873 context->codec_priv_size);
875 } else if (!strcmp (mimetype, "video/x-theora")) {
876 const GValue *streamheader;
878 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
880 if (context->codec_priv != NULL) {
881 g_free (context->codec_priv);
882 context->codec_priv = NULL;
883 context->codec_priv_size = 0;
886 streamheader = gst_structure_get_value (structure, "streamheader");
887 if (!theora_streamheader_to_codecdata (streamheader, context)) {
888 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
889 ("theora stream headers missing or malformed"));
892 } else if (!strcmp (mimetype, "video/x-dirac")) {
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
894 } else if (!strcmp (mimetype, "video/x-vp8")) {
895 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
896 } else if (!strcmp (mimetype, "video/mpeg")) {
899 gst_structure_get_int (structure, "mpegversion", &mpegversion);
900 switch (mpegversion) {
902 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
905 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
914 /* global headers may be in codec data */
915 if (codec_buf != NULL) {
916 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
917 context->codec_priv = g_malloc0 (context->codec_priv_size);
918 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
919 context->codec_priv_size);
921 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
923 /* can only make it here if preceding case verified it was version 3 */
924 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
925 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
927 const GValue *mdpr_data;
929 gst_structure_get_int (structure, "rmversion", &rmversion);
932 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
935 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
938 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
941 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
947 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
948 if (mdpr_data != NULL) {
949 guint8 *priv_data = NULL;
950 guint priv_data_size = 0;
952 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
954 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
955 priv_data = g_malloc0 (priv_data_size);
957 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
959 context->codec_priv = priv_data;
960 context->codec_priv_size = priv_data_size;
969 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
970 GST_PAD_NAME (pad), caps);
975 /* N > 0 to expect a particular number of headers, negative if the
976 number of headers is variable */
978 xiphN_streamheader_to_codecdata (const GValue * streamheader,
979 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
981 GstBuffer **buf = NULL;
984 guint bufi, i, offset, priv_data_size;
986 if (streamheader == NULL)
987 goto no_stream_headers;
989 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
992 bufarr = g_value_peek_pointer (streamheader);
993 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
995 if (N > 0 && bufarr->len != N)
998 context->xiph_headers_to_skip = bufarr->len;
1000 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1001 for (i = 0; i < bufarr->len; i++) {
1002 GValue *bufval = &g_array_index (bufarr, GValue, i);
1004 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1006 goto wrong_content_type;
1009 buf[i] = g_value_peek_pointer (bufval);
1013 if (bufarr->len > 0) {
1014 for (i = 0; i < bufarr->len - 1; i++) {
1015 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1019 for (i = 0; i < bufarr->len; ++i) {
1020 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1023 priv_data = g_malloc0 (priv_data_size);
1025 priv_data[0] = bufarr->len - 1;
1028 if (bufarr->len > 0) {
1029 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1030 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1031 priv_data[offset++] = 0xff;
1033 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1037 for (i = 0; i < bufarr->len; ++i) {
1038 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1039 GST_BUFFER_SIZE (buf[i]));
1040 offset += GST_BUFFER_SIZE (buf[i]);
1043 context->codec_priv = priv_data;
1044 context->codec_priv_size = priv_data_size;
1047 *p_buf0 = gst_buffer_ref (buf[0]);
1056 GST_WARNING ("required streamheaders missing in sink caps!");
1061 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1062 G_VALUE_TYPE_NAME (streamheader));
1067 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1072 GST_WARNING ("streamheaders array does not contain GstBuffers");
1078 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1079 GstMatroskaTrackContext * context)
1081 GstBuffer *buf0 = NULL;
1083 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1086 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1087 GST_WARNING ("First vorbis header too small, ignoring");
1089 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1090 GstMatroskaTrackAudioContext *audiocontext;
1093 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1094 audiocontext = (GstMatroskaTrackAudioContext *) context;
1095 audiocontext->channels = GST_READ_UINT8 (hdr);
1096 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1101 gst_buffer_unref (buf0);
1107 theora_streamheader_to_codecdata (const GValue * streamheader,
1108 GstMatroskaTrackContext * context)
1110 GstBuffer *buf0 = NULL;
1112 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1115 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1116 GST_WARNING ("First theora header too small, ignoring");
1117 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1118 GST_WARNING ("First header not a theora identification header, ignoring");
1120 GstMatroskaTrackVideoContext *videocontext;
1121 guint fps_num, fps_denom, par_num, par_denom;
1124 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1126 videocontext = (GstMatroskaTrackVideoContext *) context;
1127 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1128 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1129 hdr += 3 + 3 + 1 + 1;
1130 fps_num = GST_READ_UINT32_BE (hdr);
1131 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1132 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1133 fps_denom, fps_num);
1135 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1136 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1137 if (par_num > 0 && par_num > 0) {
1138 if (par_num > par_denom) {
1139 videocontext->display_width =
1140 videocontext->pixel_width * par_num / par_denom;
1141 videocontext->display_height = videocontext->pixel_height;
1142 } else if (par_num < par_denom) {
1143 videocontext->display_width = videocontext->pixel_width;
1144 videocontext->display_height =
1145 videocontext->pixel_height * par_denom / par_num;
1147 videocontext->display_width = 0;
1148 videocontext->display_height = 0;
1151 videocontext->display_width = 0;
1152 videocontext->display_height = 0;
1158 gst_buffer_unref (buf0);
1164 kate_streamheader_to_codecdata (const GValue * streamheader,
1165 GstMatroskaTrackContext * context)
1167 GstBuffer *buf0 = NULL;
1169 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1172 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1173 GST_WARNING ("First kate header too small, ignoring");
1174 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1175 GST_WARNING ("First header not a kate identification header, ignoring");
1179 gst_buffer_unref (buf0);
1185 flac_streamheader_to_codecdata (const GValue * streamheader,
1186 GstMatroskaTrackContext * context)
1193 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1194 GST_WARNING ("No or invalid streamheader field in the caps");
1198 bufarr = g_value_peek_pointer (streamheader);
1199 if (bufarr->len < 2) {
1200 GST_WARNING ("Too few headers in streamheader field");
1204 context->xiph_headers_to_skip = bufarr->len + 1;
1206 bufval = &g_array_index (bufarr, GValue, 0);
1207 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1208 GST_WARNING ("streamheaders array does not contain GstBuffers");
1212 buffer = g_value_peek_pointer (bufval);
1214 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1215 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1216 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1217 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1218 GST_WARNING ("Invalid streamheader for FLAC");
1222 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1223 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1224 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1225 GST_BUFFER_SIZE (buffer) - 9);
1227 for (i = 1; i < bufarr->len; i++) {
1228 bufval = &g_array_index (bufarr, GValue, i);
1230 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1231 g_free (context->codec_priv);
1232 context->codec_priv = NULL;
1233 context->codec_priv_size = 0;
1234 GST_WARNING ("streamheaders array does not contain GstBuffers");
1238 buffer = g_value_peek_pointer (bufval);
1240 context->codec_priv =
1241 g_realloc (context->codec_priv,
1242 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1243 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1244 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1245 context->codec_priv_size =
1246 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1253 speex_streamheader_to_codecdata (const GValue * streamheader,
1254 GstMatroskaTrackContext * context)
1260 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1261 GST_WARNING ("No or invalid streamheader field in the caps");
1265 bufarr = g_value_peek_pointer (streamheader);
1266 if (bufarr->len != 2) {
1267 GST_WARNING ("Too few headers in streamheader field");
1271 context->xiph_headers_to_skip = bufarr->len + 1;
1273 bufval = &g_array_index (bufarr, GValue, 0);
1274 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1275 GST_WARNING ("streamheaders array does not contain GstBuffers");
1279 buffer = g_value_peek_pointer (bufval);
1281 if (GST_BUFFER_SIZE (buffer) < 80
1282 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1283 GST_WARNING ("Invalid streamheader for Speex");
1287 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1288 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1289 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1290 GST_BUFFER_SIZE (buffer));
1292 bufval = &g_array_index (bufarr, GValue, 1);
1294 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1295 g_free (context->codec_priv);
1296 context->codec_priv = NULL;
1297 context->codec_priv_size = 0;
1298 GST_WARNING ("streamheaders array does not contain GstBuffers");
1302 buffer = g_value_peek_pointer (bufval);
1304 context->codec_priv =
1305 g_realloc (context->codec_priv,
1306 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1307 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1308 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1309 context->codec_priv_size =
1310 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1315 static const gchar *
1316 aac_codec_data_to_codec_id (const GstBuffer * buf)
1318 const gchar *result;
1321 /* default to MAIN */
1324 if (GST_BUFFER_SIZE (buf) >= 2) {
1325 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1343 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1352 * gst_matroska_mux_audio_pad_setcaps:
1353 * @pad: Pad which got the caps.
1356 * Setcaps function for audio sink pad.
1358 * Returns: #TRUE on success.
1361 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1363 GstMatroskaTrackContext *context = NULL;
1364 GstMatroskaTrackAudioContext *audiocontext;
1365 GstMatroskaMux *mux;
1366 GstMatroskaPad *collect_pad;
1367 const gchar *mimetype;
1368 gint samplerate = 0, channels = 0;
1369 GstStructure *structure;
1370 const GValue *codec_data = NULL;
1371 const GstBuffer *buf = NULL;
1372 const gchar *stream_format = NULL;
1374 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1377 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1378 g_assert (collect_pad);
1379 context = collect_pad->track;
1381 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1382 audiocontext = (GstMatroskaTrackAudioContext *) context;
1384 structure = gst_caps_get_structure (caps, 0);
1385 mimetype = gst_structure_get_name (structure);
1388 gst_structure_get_int (structure, "rate", &samplerate);
1389 gst_structure_get_int (structure, "channels", &channels);
1391 audiocontext->samplerate = samplerate;
1392 audiocontext->channels = channels;
1393 audiocontext->bitdepth = 0;
1394 context->default_duration = 0;
1396 codec_data = gst_structure_get_value (structure, "codec_data");
1398 buf = gst_value_get_buffer (codec_data);
1400 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1401 * data and other settings
1405 if (!strcmp (mimetype, "audio/mpeg")) {
1406 gint mpegversion = 0;
1408 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1409 switch (mpegversion) {
1415 gst_structure_get_int (structure, "layer", &layer);
1417 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1418 GST_WARNING_OBJECT (mux,
1419 "Unable to determine MPEG audio version, assuming 1");
1425 else if (layer == 2)
1427 else if (version == 2)
1432 context->default_duration =
1433 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1437 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1440 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1443 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1452 stream_format = gst_structure_get_string (structure, "stream-format");
1453 /* check this is raw aac */
1454 if (stream_format) {
1455 if (strcmp (stream_format, "raw") != 0) {
1456 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1460 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1465 if (mpegversion == 2)
1467 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1468 aac_codec_data_to_codec_id (buf));
1469 else if (mpegversion == 4)
1471 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1472 aac_codec_data_to_codec_id (buf));
1474 g_assert_not_reached ();
1476 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1483 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1485 gint endianness = G_LITTLE_ENDIAN;
1486 gboolean signedness = TRUE;
1488 if (!gst_structure_get_int (structure, "width", &width) ||
1489 !gst_structure_get_int (structure, "depth", &depth) ||
1490 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1491 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1496 !gst_structure_get_int (structure, "endianness", &endianness)) {
1497 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1501 if (width != depth) {
1502 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1506 /* FIXME: where is this spec'ed out? (tpm) */
1507 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1508 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1512 audiocontext->bitdepth = depth;
1513 if (endianness == G_BIG_ENDIAN)
1514 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1516 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1518 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1521 if (!gst_structure_get_int (structure, "width", &width)) {
1522 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1526 audiocontext->bitdepth = width;
1527 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1529 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1530 const GValue *streamheader;
1532 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1534 if (context->codec_priv != NULL) {
1535 g_free (context->codec_priv);
1536 context->codec_priv = NULL;
1537 context->codec_priv_size = 0;
1540 streamheader = gst_structure_get_value (structure, "streamheader");
1541 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1542 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1543 ("vorbis stream headers missing or malformed"));
1546 } else if (!strcmp (mimetype, "audio/x-flac")) {
1547 const GValue *streamheader;
1549 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1550 if (context->codec_priv != NULL) {
1551 g_free (context->codec_priv);
1552 context->codec_priv = NULL;
1553 context->codec_priv_size = 0;
1556 streamheader = gst_structure_get_value (structure, "streamheader");
1557 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1558 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1559 ("flac stream headers missing or malformed"));
1562 } else if (!strcmp (mimetype, "audio/x-speex")) {
1563 const GValue *streamheader;
1565 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1566 if (context->codec_priv != NULL) {
1567 g_free (context->codec_priv);
1568 context->codec_priv = NULL;
1569 context->codec_priv_size = 0;
1572 streamheader = gst_structure_get_value (structure, "streamheader");
1573 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1574 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1575 ("speex stream headers missing or malformed"));
1578 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1579 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1580 } else if (!strcmp (mimetype, "audio/x-tta")) {
1583 /* TTA frame duration */
1584 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1586 gst_structure_get_int (structure, "width", &width);
1587 audiocontext->bitdepth = width;
1588 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1590 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1592 const GValue *mdpr_data;
1594 gst_structure_get_int (structure, "raversion", &raversion);
1595 switch (raversion) {
1597 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1600 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1603 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1609 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1610 if (mdpr_data != NULL) {
1611 guint8 *priv_data = NULL;
1612 guint priv_data_size = 0;
1614 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1616 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1617 priv_data = g_malloc0 (priv_data_size);
1619 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1621 context->codec_priv = priv_data;
1622 context->codec_priv_size = priv_data_size;
1625 } else if (!strcmp (mimetype, "audio/x-wma")) {
1627 guint codec_priv_size;
1634 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1635 || !gst_structure_get_int (structure, "block_align", &block_align)
1636 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1637 || samplerate == 0 || channels == 0) {
1638 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1639 "channels/rate on WMA caps");
1643 switch (wmaversion) {
1645 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1648 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1651 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1654 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1658 if (gst_structure_get_int (structure, "depth", &depth))
1659 audiocontext->bitdepth = depth;
1661 codec_priv_size = WAVEFORMATEX_SIZE;
1663 codec_priv_size += GST_BUFFER_SIZE (buf);
1665 /* serialize waveformatex structure */
1666 codec_priv = g_malloc0 (codec_priv_size);
1667 GST_WRITE_UINT16_LE (codec_priv, format);
1668 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1669 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1670 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1671 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1672 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1674 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1676 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1678 /* process codec private/initialization data, if any */
1680 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1681 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1684 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1685 context->codec_priv = (gpointer) codec_priv;
1686 context->codec_priv_size = codec_priv_size;
1694 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1695 GST_PAD_NAME (pad), caps);
1702 * gst_matroska_mux_subtitle_pad_setcaps:
1703 * @pad: Pad which got the caps.
1706 * Setcaps function for subtitle sink pad.
1708 * Returns: #TRUE on success.
1711 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1714 * Consider this as boilerplate code for now. There is
1715 * no single subtitle creation element in GStreamer,
1716 * neither do I know how subtitling works at all. */
1718 /* There is now (at least) one such alement (kateenc), and I'm going
1719 to handle it here and claim it works when it can be piped back
1720 through GStreamer and VLC */
1722 GstMatroskaTrackContext *context = NULL;
1723 GstMatroskaTrackSubtitleContext *scontext;
1724 GstMatroskaMux *mux;
1725 GstMatroskaPad *collect_pad;
1726 const gchar *mimetype;
1727 GstStructure *structure;
1729 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1732 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1733 g_assert (collect_pad);
1734 context = collect_pad->track;
1736 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1737 scontext = (GstMatroskaTrackSubtitleContext *) context;
1739 structure = gst_caps_get_structure (caps, 0);
1740 mimetype = gst_structure_get_name (structure);
1743 scontext->check_utf8 = 1;
1744 scontext->invalid_utf8 = 0;
1745 context->default_duration = 0;
1747 /* TODO: - other format than Kate */
1749 if (!strcmp (mimetype, "subtitle/x-kate")) {
1750 const GValue *streamheader;
1752 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1754 if (context->codec_priv != NULL) {
1755 g_free (context->codec_priv);
1756 context->codec_priv = NULL;
1757 context->codec_priv_size = 0;
1760 streamheader = gst_structure_get_value (structure, "streamheader");
1761 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1762 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1763 ("kate stream headers missing or malformed"));
1774 * gst_matroska_mux_request_new_pad:
1775 * @element: #GstMatroskaMux.
1776 * @templ: #GstPadTemplate.
1777 * @pad_name: New pad name.
1779 * Request pad function for sink templates.
1781 * Returns: New #GstPad.
1784 gst_matroska_mux_request_new_pad (GstElement * element,
1785 GstPadTemplate * templ, const gchar * pad_name)
1787 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1788 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1789 GstMatroskaPad *collect_pad;
1790 GstPad *newpad = NULL;
1792 GstPadSetCapsFunction setcapsfunc = NULL;
1793 GstMatroskaTrackContext *context = NULL;
1795 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1796 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1797 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1798 context = (GstMatroskaTrackContext *)
1799 g_new0 (GstMatroskaTrackAudioContext, 1);
1800 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1801 context->name = g_strdup ("Audio");
1802 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1803 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1804 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1805 context = (GstMatroskaTrackContext *)
1806 g_new0 (GstMatroskaTrackVideoContext, 1);
1807 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1808 context->name = g_strdup ("Video");
1809 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1810 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1811 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1812 context = (GstMatroskaTrackContext *)
1813 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1814 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1815 context->name = g_strdup ("Subtitle");
1817 GST_WARNING_OBJECT (mux, "This is not our template!");
1821 newpad = gst_pad_new_from_template (templ, name);
1823 collect_pad = (GstMatroskaPad *)
1824 gst_collect_pads_add_pad_full (mux->collect, newpad,
1825 sizeof (GstMatroskaPad),
1826 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1828 collect_pad->track = context;
1829 gst_matroska_pad_reset (collect_pad, FALSE);
1831 /* FIXME: hacked way to override/extend the event function of
1832 * GstCollectPads; because it sets its own event function giving the
1833 * element no access to events.
1834 * TODO GstCollectPads should really give its 'users' a clean chance to
1835 * properly handle events that are not meant for collectpads itself.
1836 * Perhaps a callback or so, though rejected (?) in #340060.
1837 * This would allow (clean) transcoding of info from demuxer/streams
1838 * to another muxer */
1839 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1840 gst_pad_set_event_function (newpad,
1841 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1843 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1844 gst_pad_set_active (newpad, TRUE);
1845 gst_element_add_pad (element, newpad);
1852 * gst_matroska_mux_release_pad:
1853 * @element: #GstMatroskaMux.
1854 * @pad: Pad to release.
1856 * Release a previously requested pad.
1859 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1861 GstMatroskaMux *mux;
1864 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1866 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1867 GstCollectData *cdata = (GstCollectData *) walk->data;
1868 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1870 if (cdata->pad == pad) {
1871 GstClockTime min_dur; /* observed minimum duration */
1873 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1874 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1875 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1876 if (collect_pad->duration < min_dur)
1877 collect_pad->duration = min_dur;
1880 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1881 mux->duration < collect_pad->duration)
1882 mux->duration = collect_pad->duration;
1888 gst_collect_pads_remove_pad (mux->collect, pad);
1889 if (gst_element_remove_pad (element, pad))
1895 * gst_matroska_mux_track_header:
1896 * @mux: #GstMatroskaMux
1897 * @context: Tack context.
1899 * Write a track header.
1902 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1903 GstMatroskaTrackContext * context)
1905 GstEbmlWrite *ebml = mux->ebml_write;
1908 /* TODO: check if everything necessary is written and check default values */
1910 /* track type goes before the type-specific stuff */
1911 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1912 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1914 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1915 gst_matroska_mux_create_uid ());
1916 if (context->default_duration) {
1917 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1918 context->default_duration);
1920 if (context->language) {
1921 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1925 /* type-specific stuff */
1926 switch (context->type) {
1927 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1928 GstMatroskaTrackVideoContext *videocontext =
1929 (GstMatroskaTrackVideoContext *) context;
1931 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1932 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1933 videocontext->pixel_width);
1934 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1935 videocontext->pixel_height);
1936 if (videocontext->display_width && videocontext->display_height) {
1937 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1938 videocontext->display_width);
1939 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1940 videocontext->display_height);
1942 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1943 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1944 if (videocontext->fourcc) {
1945 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1947 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1948 (gpointer) & fcc_le, 4);
1950 gst_ebml_write_master_finish (ebml, master);
1955 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1956 GstMatroskaTrackAudioContext *audiocontext =
1957 (GstMatroskaTrackAudioContext *) context;
1959 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1960 if (audiocontext->samplerate != 8000)
1961 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1962 audiocontext->samplerate);
1963 if (audiocontext->channels != 1)
1964 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1965 audiocontext->channels);
1966 if (audiocontext->bitdepth) {
1967 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1968 audiocontext->bitdepth);
1970 gst_ebml_write_master_finish (ebml, master);
1976 /* doesn't need type-specific data */
1980 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1981 if (context->codec_priv)
1982 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1983 context->codec_priv, context->codec_priv_size);
1984 /* FIXME: until we have a nice way of getting the codecname
1985 * out of the caps, I'm not going to enable this. Too much
1986 * (useless, double, boring) work... */
1987 /* TODO: Use value from tags if any */
1988 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1989 context->codec_name); */
1990 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1995 * gst_matroska_mux_start:
1996 * @mux: #GstMatroskaMux
1998 * Start a new matroska file (write headers etc...)
2001 gst_matroska_mux_start (GstMatroskaMux * mux)
2003 GstEbmlWrite *ebml = mux->ebml_write;
2004 const gchar *doctype;
2005 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2006 GST_MATROSKA_ID_TRACKS,
2007 GST_MATROSKA_ID_CUES,
2008 GST_MATROSKA_ID_TAGS,
2011 guint64 master, child;
2015 GstClockTime duration = 0;
2016 guint32 segment_uid[4];
2017 GTimeVal time = { 0, 0 };
2019 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2020 ebml->caps = gst_caps_from_string ("video/webm");
2022 ebml->caps = gst_caps_from_string ("video/x-matroska");
2024 /* we start with a EBML header */
2025 doctype = mux->doctype;
2026 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2027 doctype, mux->doctype_version);
2028 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2030 /* the rest of the header is cached */
2031 gst_ebml_write_set_cache (ebml, 0x1000);
2033 /* start a segment */
2035 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2036 mux->segment_master = ebml->pos;
2038 if (!mux->streamable) {
2039 /* seekhead (table of contents) - we set the positions later */
2040 mux->seekhead_pos = ebml->pos;
2041 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2042 for (i = 0; seekhead_id[i] != 0; i++) {
2043 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2044 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2045 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2046 gst_ebml_write_master_finish (ebml, child);
2048 gst_ebml_write_master_finish (ebml, master);
2052 mux->info_pos = ebml->pos;
2053 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2054 for (i = 0; i < 4; i++) {
2055 segment_uid[i] = g_random_int ();
2057 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2058 (guint8 *) segment_uid, 16);
2059 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2060 mux->duration_pos = ebml->pos;
2062 if (!mux->streamable) {
2063 for (collected = mux->collect->data; collected;
2064 collected = g_slist_next (collected)) {
2065 GstMatroskaPad *collect_pad;
2066 GstFormat format = GST_FORMAT_TIME;
2068 gint64 trackduration;
2070 collect_pad = (GstMatroskaPad *) collected->data;
2071 thepad = collect_pad->collect.pad;
2073 /* Query the total length of the track. */
2074 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2075 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2076 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2077 GST_TIME_ARGS (trackduration));
2078 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2079 duration = (GstClockTime) trackduration;
2083 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2084 gst_guint64_to_gdouble (duration) /
2085 gst_guint64_to_gdouble (mux->time_scale));
2087 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2088 "GStreamer plugin version " PACKAGE_VERSION);
2089 if (mux->writing_app && mux->writing_app[0]) {
2090 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2092 g_get_current_time (&time);
2093 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2094 gst_ebml_write_master_finish (ebml, master);
2097 mux->tracks_pos = ebml->pos;
2098 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2100 for (collected = mux->collect->data; collected;
2101 collected = g_slist_next (collected)) {
2102 GstMatroskaPad *collect_pad;
2105 collect_pad = (GstMatroskaPad *) collected->data;
2106 thepad = collect_pad->collect.pad;
2108 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2109 collect_pad->track->codec_id != 0) {
2110 collect_pad->track->num = tracknum++;
2111 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2112 gst_matroska_mux_track_header (mux, collect_pad->track);
2113 gst_ebml_write_master_finish (ebml, child);
2116 gst_ebml_write_master_finish (ebml, master);
2118 /* lastly, flush the cache */
2119 gst_ebml_write_flush_cache (ebml, FALSE);
2123 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2126 /* TODO: more sensible tag mappings */
2129 const gchar *matroska_tagname;
2130 const gchar *gstreamer_tagname;
2134 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2135 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2136 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2137 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2138 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2139 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2140 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2141 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2142 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2143 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2144 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2145 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2146 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2147 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2148 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2150 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2152 guint64 simpletag_master;
2154 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2155 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2156 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2158 if (strcmp (tagname_gst, tag) == 0) {
2159 GValue src = { 0, };
2162 if (!gst_tag_list_copy_value (&src, list, tag))
2164 if ((dest = gst_value_serialize (&src))) {
2166 simpletag_master = gst_ebml_write_master_start (ebml,
2167 GST_MATROSKA_ID_SIMPLETAG);
2168 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2169 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2170 gst_ebml_write_master_finish (ebml, simpletag_master);
2173 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2175 g_value_unset (&src);
2183 * gst_matroska_mux_finish:
2184 * @mux: #GstMatroskaMux
2186 * Finish a new matroska file (write index etc...)
2189 gst_matroska_mux_finish (GstMatroskaMux * mux)
2191 GstEbmlWrite *ebml = mux->ebml_write;
2193 guint64 duration = 0;
2195 const GstTagList *tags;
2197 /* finish last cluster */
2199 gst_ebml_write_master_finish (ebml, mux->cluster);
2203 if (mux->index != NULL) {
2205 guint64 master, pointentry_master, trackpos_master;
2207 mux->cues_pos = ebml->pos;
2208 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2209 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2211 for (n = 0; n < mux->num_indexes; n++) {
2212 GstMatroskaIndex *idx = &mux->index[n];
2214 pointentry_master = gst_ebml_write_master_start (ebml,
2215 GST_MATROSKA_ID_POINTENTRY);
2216 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2217 idx->time / mux->time_scale);
2218 trackpos_master = gst_ebml_write_master_start (ebml,
2219 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2220 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2221 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2222 idx->pos - mux->segment_master);
2223 gst_ebml_write_master_finish (ebml, trackpos_master);
2224 gst_ebml_write_master_finish (ebml, pointentry_master);
2227 gst_ebml_write_master_finish (ebml, master);
2228 gst_ebml_write_flush_cache (ebml, FALSE);
2232 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2234 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2235 guint64 master_tags, master_tag;
2237 GST_DEBUG ("Writing tags");
2239 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2240 mux->tags_pos = ebml->pos;
2241 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2242 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2243 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2244 gst_ebml_write_master_finish (ebml, master_tag);
2245 gst_ebml_write_master_finish (ebml, master_tags);
2248 /* update seekhead. We know that:
2249 * - a seekhead contains 4 entries.
2250 * - order of entries is as above.
2251 * - a seekhead has a 4-byte header + 8-byte length
2252 * - each entry is 2-byte master, 2-byte ID pointer,
2253 * 2-byte length pointer, all 8/1-byte length, 4-
2254 * byte ID and 8-byte length pointer, where the
2255 * length pointer starts at 20.
2256 * - all entries are local to the segment (so pos - segment_master).
2257 * - so each entry is at 12 + 20 + num * 28. */
2258 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2259 mux->info_pos - mux->segment_master);
2260 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2261 mux->tracks_pos - mux->segment_master);
2262 if (mux->index != NULL) {
2263 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2264 mux->cues_pos - mux->segment_master);
2267 guint64 my_pos = ebml->pos;
2269 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2270 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2271 gst_ebml_write_seek (ebml, my_pos);
2274 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2275 mux->tags_pos - mux->segment_master);
2278 guint64 my_pos = ebml->pos;
2280 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2281 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2282 gst_ebml_write_seek (ebml, my_pos);
2285 /* update duration */
2286 /* first get the overall duration */
2287 /* a released track may have left a duration in here */
2288 duration = mux->duration;
2289 for (collected = mux->collect->data; collected;
2290 collected = g_slist_next (collected)) {
2291 GstMatroskaPad *collect_pad;
2292 GstClockTime min_duration; /* observed minimum duration */
2294 collect_pad = (GstMatroskaPad *) collected->data;
2296 GST_DEBUG_OBJECT (mux,
2297 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2298 " end ts %" GST_TIME_FORMAT, collect_pad,
2299 GST_TIME_ARGS (collect_pad->start_ts),
2300 GST_TIME_ARGS (collect_pad->end_ts));
2302 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2303 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2305 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2306 if (collect_pad->duration < min_duration)
2307 collect_pad->duration = min_duration;
2308 GST_DEBUG_OBJECT (collect_pad,
2309 "final track duration: %" GST_TIME_FORMAT,
2310 GST_TIME_ARGS (collect_pad->duration));
2313 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2314 duration < collect_pad->duration)
2315 duration = collect_pad->duration;
2317 if (duration != 0) {
2318 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2319 GST_TIME_ARGS (duration));
2320 pos = mux->ebml_write->pos;
2321 gst_ebml_write_seek (ebml, mux->duration_pos);
2322 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2323 gst_guint64_to_gdouble (duration) /
2324 gst_guint64_to_gdouble (mux->time_scale));
2325 gst_ebml_write_seek (ebml, pos);
2328 guint64 my_pos = ebml->pos;
2330 gst_ebml_write_seek (ebml, mux->duration_pos);
2331 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2332 gst_ebml_write_seek (ebml, my_pos);
2334 GST_DEBUG_OBJECT (mux, "finishing segment");
2335 /* finish segment - this also writes element length */
2336 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2341 * gst_matroska_mux_best_pad:
2342 * @mux: #GstMatroskaMux
2343 * @popped: True if at least one buffer was popped from #GstCollectPads
2345 * Find a pad with the oldest data
2346 * (data from this pad should be written first).
2348 * Returns: Selected pad.
2350 static GstMatroskaPad *
2351 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2354 GstMatroskaPad *best = NULL;
2357 for (collected = mux->collect->data; collected;
2358 collected = g_slist_next (collected)) {
2359 GstMatroskaPad *collect_pad;
2361 collect_pad = (GstMatroskaPad *) collected->data;
2362 /* fetch a new buffer if needed */
2363 if (collect_pad->buffer == NULL) {
2364 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2365 (GstCollectData *) collect_pad);
2367 if (collect_pad->buffer != NULL)
2371 /* if we have a buffer check if it is better then the current best one */
2372 if (collect_pad->buffer != NULL) {
2373 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2374 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2375 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2376 GST_BUFFER_TIMESTAMP (best->buffer))) {
2386 * gst_matroska_mux_buffer_header:
2387 * @track: Track context.
2388 * @relative_timestamp: relative timestamp of the buffer
2389 * @flags: Buffer flags.
2391 * Create a buffer containing buffer header.
2393 * Returns: New buffer.
2396 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2397 gint16 relative_timestamp, int flags)
2401 hdr = gst_buffer_new_and_alloc (4);
2402 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2403 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2404 /* time relative to clustertime */
2405 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2408 GST_BUFFER_DATA (hdr)[3] = flags;
2413 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2414 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2415 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2418 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2419 GstMatroskaPad * collect_pad, GstBuffer * buf)
2421 GstMatroskaTrackVideoContext *ctx =
2422 (GstMatroskaTrackVideoContext *) collect_pad->track;
2423 const guint8 *data = GST_BUFFER_DATA (buf);
2424 guint size = GST_BUFFER_SIZE (buf);
2426 guint32 next_parse_offset;
2427 GstBuffer *ret = NULL;
2428 gboolean is_muxing_unit = FALSE;
2430 if (GST_BUFFER_SIZE (buf) < 13) {
2431 gst_buffer_unref (buf);
2435 /* Check if this buffer contains a picture or end-of-sequence packet */
2436 while (size >= 13) {
2437 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2438 gst_buffer_unref (buf);
2442 parse_code = GST_READ_UINT8 (data + 4);
2443 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2444 if (ctx->dirac_unit) {
2445 gst_buffer_unref (ctx->dirac_unit);
2446 ctx->dirac_unit = NULL;
2448 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2449 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2450 is_muxing_unit = TRUE;
2454 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2456 if (G_UNLIKELY (next_parse_offset == 0))
2459 data += next_parse_offset;
2460 size -= next_parse_offset;
2463 if (ctx->dirac_unit)
2464 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2466 ctx->dirac_unit = gst_buffer_ref (buf);
2468 if (is_muxing_unit) {
2469 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2470 ctx->dirac_unit = NULL;
2471 gst_buffer_copy_metadata (ret, buf,
2472 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2473 GST_BUFFER_COPY_CAPS);
2474 gst_buffer_unref (buf);
2476 gst_buffer_unref (buf);
2484 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2488 GValue streamheader = { 0 };
2489 GValue bufval = { 0 };
2490 GstBuffer *streamheader_buffer;
2491 GstEbmlWrite *ebml = mux->ebml_write;
2493 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2494 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2495 caps = gst_caps_from_string ("video/webm");
2497 caps = gst_caps_from_string ("video/x-matroska");
2499 s = gst_caps_get_structure (caps, 0);
2500 g_value_init (&streamheader, GST_TYPE_ARRAY);
2501 g_value_init (&bufval, GST_TYPE_BUFFER);
2502 gst_value_set_buffer (&bufval, streamheader_buffer);
2503 gst_value_array_append_value (&streamheader, &bufval);
2504 g_value_unset (&bufval);
2505 gst_structure_set_value (s, "streamheader", &streamheader);
2506 g_value_unset (&streamheader);
2507 gst_caps_unref (ebml->caps);
2508 gst_buffer_unref (streamheader_buffer);
2513 * gst_matroska_mux_write_data:
2514 * @mux: #GstMatroskaMux
2515 * @collect_pad: #GstMatroskaPad with the data
2517 * Write collected data (called from gst_matroska_mux_collected).
2519 * Returns: Result of the gst_pad_push issued to write the data.
2521 static GstFlowReturn
2522 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2524 GstEbmlWrite *ebml = mux->ebml_write;
2525 GstBuffer *buf, *hdr;
2527 gboolean write_duration;
2528 gint16 relative_timestamp;
2529 gint64 relative_timestamp64;
2530 guint64 block_duration;
2531 gboolean is_video_keyframe = FALSE;
2534 buf = collect_pad->buffer;
2535 collect_pad->buffer = NULL;
2537 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2538 if (collect_pad->track->xiph_headers_to_skip > 0) {
2539 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2540 gst_buffer_unref (buf);
2541 --collect_pad->track->xiph_headers_to_skip;
2545 /* for dirac we have to queue up everything up to a picture unit */
2546 if (collect_pad->track->codec_id != NULL &&
2547 strcmp (collect_pad->track->codec_id,
2548 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2549 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2554 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2555 * this would wreak havoc with time stored in matroska file */
2556 /* TODO: maybe calculate a timestamp by using the previous timestamp
2557 * and default duration */
2558 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2559 GST_WARNING_OBJECT (collect_pad->collect.pad,
2560 "Invalid buffer timestamp; dropping buffer");
2561 gst_buffer_unref (buf);
2565 /* set the timestamp for outgoing buffers */
2566 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2568 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2569 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2570 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2571 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2572 is_video_keyframe = TRUE;
2576 /* start a new cluster at every keyframe or when we may be reaching the
2577 * limit of the relative timestamp */
2578 if (mux->cluster_time +
2579 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2580 || is_video_keyframe) {
2581 if (!mux->streamable)
2582 gst_ebml_write_master_finish (ebml, mux->cluster);
2583 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2584 mux->cluster_pos = ebml->pos;
2585 gst_ebml_write_set_cache (ebml, 0x20);
2587 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2588 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2589 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2591 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2592 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2594 gst_ebml_write_flush_cache (ebml, TRUE);
2595 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2596 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2597 mux->prev_cluster_size);
2602 mux->cluster_pos = ebml->pos;
2603 gst_ebml_write_set_cache (ebml, 0x20);
2604 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2605 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2606 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2607 gst_ebml_write_flush_cache (ebml, TRUE);
2608 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2611 /* update duration of this track */
2612 if (GST_BUFFER_DURATION_IS_VALID (buf))
2613 collect_pad->duration += GST_BUFFER_DURATION (buf);
2615 /* We currently write index entries for all video tracks or for the audio
2616 * track in a single-track audio file. This could be improved by keeping the
2617 * index only for the *first* video track. */
2619 /* TODO: index is useful for every track, should contain the number of
2620 * the block in the cluster which contains the timestamp, should also work
2621 * for files with multiple audio tracks.
2623 if (is_video_keyframe ||
2624 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2625 (mux->num_streams == 1))) {
2628 if (mux->min_index_interval != 0) {
2629 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2630 if (mux->index[last_idx].track == collect_pad->track->num)
2635 if (last_idx < 0 || mux->min_index_interval == 0 ||
2636 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2637 >= mux->min_index_interval)) {
2638 GstMatroskaIndex *idx;
2640 if (mux->num_indexes % 32 == 0) {
2641 mux->index = g_renew (GstMatroskaIndex, mux->index,
2642 mux->num_indexes + 32);
2644 idx = &mux->index[mux->num_indexes++];
2646 idx->pos = mux->cluster_pos;
2647 idx->time = GST_BUFFER_TIMESTAMP (buf);
2648 idx->track = collect_pad->track->num;
2652 /* Check if the duration differs from the default duration. */
2653 write_duration = FALSE;
2654 block_duration = GST_BUFFER_DURATION (buf);
2655 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2656 if (block_duration != collect_pad->track->default_duration) {
2657 write_duration = TRUE;
2661 /* write the block, for doctype v2 use SimpleBlock if possible
2662 * one slice (*breath*).
2663 * FIXME: Need to do correct lacing! */
2664 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2665 if (relative_timestamp64 >= 0) {
2666 /* round the timestamp */
2667 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2669 /* round the timestamp */
2670 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2672 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2674 if (mux->doctype_version > 1 && !write_duration) {
2676 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2679 gst_matroska_mux_create_buffer_header (collect_pad->track,
2680 relative_timestamp, flags);
2681 gst_ebml_write_set_cache (ebml, 0x40);
2682 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2683 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2684 gst_ebml_write_buffer (ebml, hdr);
2685 gst_ebml_write_flush_cache (ebml, FALSE);
2686 gst_ebml_write_buffer (ebml, buf);
2688 return gst_ebml_last_write_result (ebml);
2690 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2691 /* write and call order slightly unnatural,
2692 * but avoids seek and minizes pushing */
2693 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2695 gst_matroska_mux_create_buffer_header (collect_pad->track,
2696 relative_timestamp, 0);
2697 if (write_duration) {
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 if (!mux->streamable) {
2754 gst_matroska_mux_finish (mux);
2756 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2758 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2759 ret = GST_FLOW_UNEXPECTED;
2762 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2763 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2764 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2765 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2767 /* make note of first and last encountered timestamps, so we can calculate
2768 * the actual duration later when we send an updated header on eos */
2769 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2770 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2771 GstClockTime end_ts = start_ts;
2773 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2774 end_ts += GST_BUFFER_DURATION (best->buffer);
2775 else if (best->track->default_duration)
2776 end_ts += best->track->default_duration;
2778 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2779 best->end_ts = end_ts;
2781 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2782 start_ts < best->start_ts))
2783 best->start_ts = start_ts;
2786 /* write one buffer */
2787 ret = gst_matroska_mux_write_data (mux, best);
2788 } while (ret == GST_FLOW_OK && !popped);
2795 * gst_matroska_mux_change_state:
2796 * @element: #GstMatroskaMux
2797 * @transition: State change transition.
2799 * Change the muxer state.
2801 * Returns: #GstStateChangeReturn
2803 static GstStateChangeReturn
2804 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2806 GstStateChangeReturn ret;
2807 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2809 switch (transition) {
2810 case GST_STATE_CHANGE_NULL_TO_READY:
2812 case GST_STATE_CHANGE_READY_TO_PAUSED:
2813 gst_collect_pads_start (mux->collect);
2815 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2817 case GST_STATE_CHANGE_PAUSED_TO_READY:
2818 gst_collect_pads_stop (mux->collect);
2824 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2826 switch (transition) {
2827 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2829 case GST_STATE_CHANGE_PAUSED_TO_READY:
2830 gst_matroska_mux_reset (GST_ELEMENT (mux));
2832 case GST_STATE_CHANGE_READY_TO_NULL:
2842 gst_matroska_mux_set_property (GObject * object,
2843 guint prop_id, const GValue * value, GParamSpec * pspec)
2845 GstMatroskaMux *mux;
2847 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2848 mux = GST_MATROSKA_MUX (object);
2851 case ARG_WRITING_APP:
2852 if (!g_value_get_string (value)) {
2853 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2856 g_free (mux->writing_app);
2857 mux->writing_app = g_value_dup_string (value);
2859 case ARG_DOCTYPE_VERSION:
2860 mux->doctype_version = g_value_get_int (value);
2862 case ARG_MIN_INDEX_INTERVAL:
2863 mux->min_index_interval = g_value_get_int64 (value);
2865 case ARG_STREAMABLE:
2866 mux->streamable = g_value_get_boolean (value);
2869 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2875 gst_matroska_mux_get_property (GObject * object,
2876 guint prop_id, GValue * value, GParamSpec * pspec)
2878 GstMatroskaMux *mux;
2880 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2881 mux = GST_MATROSKA_MUX (object);
2884 case ARG_WRITING_APP:
2885 g_value_set_string (value, mux->writing_app);
2887 case ARG_DOCTYPE_VERSION:
2888 g_value_set_int (value, mux->doctype_version);
2890 case ARG_MIN_INDEX_INTERVAL:
2891 g_value_set_int64 (value, mux->min_index_interval);
2893 case ARG_STREAMABLE:
2894 g_value_set_boolean (value, mux->streamable);
2897 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);