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 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
613 switch (GST_EVENT_TYPE (event)) {
617 GST_DEBUG_OBJECT (mux, "received tag event");
618 gst_event_parse_tag (event, &list);
620 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
621 g_assert (collect_pad);
622 context = collect_pad->track;
625 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
626 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
627 const gchar *lang_code;
629 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
631 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
632 context->language = g_strdup (lang_code);
634 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
639 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
640 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
643 case GST_EVENT_NEWSEGMENT:
644 /* We don't support NEWSEGMENT events */
646 gst_event_unref (event);
652 /* now GstCollectPads can take care of the rest, e.g. EOS */
654 ret = mux->collect_event (pad, event);
655 gst_object_unref (mux);
662 * gst_matroska_mux_video_pad_setcaps:
663 * @pad: Pad which got the caps.
666 * Setcaps function for video sink pad.
668 * Returns: #TRUE on success.
671 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
673 GstMatroskaTrackContext *context = NULL;
674 GstMatroskaTrackVideoContext *videocontext;
676 GstMatroskaPad *collect_pad;
677 GstStructure *structure;
678 const gchar *mimetype;
679 const GValue *value = NULL;
680 const GstBuffer *codec_buf = NULL;
681 gint width, height, pixel_width, pixel_height;
683 gboolean interlaced = FALSE;
685 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
688 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
689 g_assert (collect_pad);
690 context = collect_pad->track;
692 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
693 videocontext = (GstMatroskaTrackVideoContext *) context;
695 /* gst -> matroska ID'ing */
696 structure = gst_caps_get_structure (caps, 0);
698 mimetype = gst_structure_get_name (structure);
700 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
702 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
704 if (!strcmp (mimetype, "video/x-theora")) {
705 /* we'll extract the details later from the theora identification header */
709 /* get general properties */
710 /* spec says it is mandatory */
711 if (!gst_structure_get_int (structure, "width", &width) ||
712 !gst_structure_get_int (structure, "height", &height))
715 videocontext->pixel_width = width;
716 videocontext->pixel_height = height;
717 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
719 context->default_duration =
720 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
721 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
722 GST_TIME_ARGS (context->default_duration));
724 context->default_duration = 0;
726 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
727 &pixel_width, &pixel_height)) {
728 if (pixel_width > pixel_height) {
729 videocontext->display_width = width * pixel_width / pixel_height;
730 videocontext->display_height = height;
731 } else if (pixel_width < pixel_height) {
732 videocontext->display_width = width;
733 videocontext->display_height = height * pixel_height / pixel_width;
735 videocontext->display_width = 0;
736 videocontext->display_height = 0;
739 videocontext->display_width = 0;
740 videocontext->display_height = 0;
745 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
746 videocontext->fourcc = 0;
748 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
749 * data and other settings
753 /* extract codec_data, may turn out needed */
754 value = gst_structure_get_value (structure, "codec_data");
756 codec_buf = gst_value_get_buffer (value);
759 if (!strcmp (mimetype, "video/x-raw-yuv")) {
760 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
761 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
762 } else if (!strcmp (mimetype, "image/jpeg")) {
763 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
764 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
765 ||!strcmp (mimetype, "video/x-huffyuv")
766 || !strcmp (mimetype, "video/x-divx")
767 || !strcmp (mimetype, "video/x-dv")
768 || !strcmp (mimetype, "video/x-h263")
769 || !strcmp (mimetype, "video/x-msmpeg")
770 || !strcmp (mimetype, "video/x-wmv")) {
771 gst_riff_strf_vids *bih;
772 gint size = sizeof (gst_riff_strf_vids);
775 if (!strcmp (mimetype, "video/x-xvid"))
776 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
777 else if (!strcmp (mimetype, "video/x-huffyuv"))
778 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
779 else if (!strcmp (mimetype, "video/x-dv"))
780 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
781 else if (!strcmp (mimetype, "video/x-h263"))
782 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
783 else if (!strcmp (mimetype, "video/x-divx")) {
786 gst_structure_get_int (structure, "divxversion", &divxversion);
787 switch (divxversion) {
789 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
792 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
795 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
798 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
801 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
802 switch (msmpegversion) {
804 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
807 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
813 } else if (!strcmp (mimetype, "video/x-wmv")) {
816 if (gst_structure_get_fourcc (structure, "format", &format)) {
818 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
819 if (wmvversion == 2) {
820 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
821 } else if (wmvversion == 1) {
822 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
823 } else if (wmvversion == 3) {
824 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
832 bih = g_new0 (gst_riff_strf_vids, 1);
833 GST_WRITE_UINT32_LE (&bih->size, size);
834 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
835 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
836 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
837 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
838 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
839 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
840 videocontext->pixel_height * 3);
842 /* process codec private/initialization data, if any */
844 size += GST_BUFFER_SIZE (codec_buf);
845 bih = g_realloc (bih, size);
846 GST_WRITE_UINT32_LE (&bih->size, size);
847 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
848 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
851 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
852 context->codec_priv = (gpointer) bih;
853 context->codec_priv_size = size;
854 } else if (!strcmp (mimetype, "video/x-h264")) {
855 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
857 if (context->codec_priv != NULL) {
858 g_free (context->codec_priv);
859 context->codec_priv = NULL;
860 context->codec_priv_size = 0;
863 /* Create avcC header */
864 if (codec_buf != NULL) {
865 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
866 context->codec_priv = g_malloc0 (context->codec_priv_size);
867 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
868 context->codec_priv_size);
870 } else if (!strcmp (mimetype, "video/x-theora")) {
871 const GValue *streamheader;
873 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
875 if (context->codec_priv != NULL) {
876 g_free (context->codec_priv);
877 context->codec_priv = NULL;
878 context->codec_priv_size = 0;
881 streamheader = gst_structure_get_value (structure, "streamheader");
882 if (!theora_streamheader_to_codecdata (streamheader, context)) {
883 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
884 ("theora stream headers missing or malformed"));
887 } else if (!strcmp (mimetype, "video/x-dirac")) {
888 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
889 } else if (!strcmp (mimetype, "video/x-vp8")) {
890 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
891 } else if (!strcmp (mimetype, "video/mpeg")) {
894 gst_structure_get_int (structure, "mpegversion", &mpegversion);
895 switch (mpegversion) {
897 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
900 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
903 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
909 /* global headers may be in codec data */
910 if (codec_buf != NULL) {
911 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
912 context->codec_priv = g_malloc0 (context->codec_priv_size);
913 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
914 context->codec_priv_size);
916 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
918 /* can only make it here if preceding case verified it was version 3 */
919 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
920 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
922 const GValue *mdpr_data;
924 gst_structure_get_int (structure, "rmversion", &rmversion);
927 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
930 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
933 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
936 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
942 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
943 if (mdpr_data != NULL) {
944 guint8 *priv_data = NULL;
945 guint priv_data_size = 0;
947 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
949 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
950 priv_data = g_malloc0 (priv_data_size);
952 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
954 context->codec_priv = priv_data;
955 context->codec_priv_size = priv_data_size;
964 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
965 GST_PAD_NAME (pad), caps);
970 /* N > 0 to expect a particular number of headers, negative if the
971 number of headers is variable */
973 xiphN_streamheader_to_codecdata (const GValue * streamheader,
974 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
976 GstBuffer **buf = NULL;
979 guint bufi, i, offset, priv_data_size;
981 if (streamheader == NULL)
982 goto no_stream_headers;
984 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
987 bufarr = g_value_peek_pointer (streamheader);
988 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
990 if (N > 0 && bufarr->len != N)
993 context->xiph_headers_to_skip = bufarr->len;
995 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
996 for (i = 0; i < bufarr->len; i++) {
997 GValue *bufval = &g_array_index (bufarr, GValue, i);
999 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1001 goto wrong_content_type;
1004 buf[i] = g_value_peek_pointer (bufval);
1008 if (bufarr->len > 0) {
1009 for (i = 0; i < bufarr->len - 1; i++) {
1010 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1014 for (i = 0; i < bufarr->len; ++i) {
1015 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1018 priv_data = g_malloc0 (priv_data_size);
1020 priv_data[0] = bufarr->len - 1;
1023 if (bufarr->len > 0) {
1024 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1025 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1026 priv_data[offset++] = 0xff;
1028 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1032 for (i = 0; i < bufarr->len; ++i) {
1033 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1034 GST_BUFFER_SIZE (buf[i]));
1035 offset += GST_BUFFER_SIZE (buf[i]);
1038 context->codec_priv = priv_data;
1039 context->codec_priv_size = priv_data_size;
1042 *p_buf0 = gst_buffer_ref (buf[0]);
1051 GST_WARNING ("required streamheaders missing in sink caps!");
1056 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1057 G_VALUE_TYPE_NAME (streamheader));
1062 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1067 GST_WARNING ("streamheaders array does not contain GstBuffers");
1073 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1074 GstMatroskaTrackContext * context)
1076 GstBuffer *buf0 = NULL;
1078 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1081 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1082 GST_WARNING ("First vorbis header too small, ignoring");
1084 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1085 GstMatroskaTrackAudioContext *audiocontext;
1088 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1089 audiocontext = (GstMatroskaTrackAudioContext *) context;
1090 audiocontext->channels = GST_READ_UINT8 (hdr);
1091 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1096 gst_buffer_unref (buf0);
1102 theora_streamheader_to_codecdata (const GValue * streamheader,
1103 GstMatroskaTrackContext * context)
1105 GstBuffer *buf0 = NULL;
1107 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1110 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1111 GST_WARNING ("First theora header too small, ignoring");
1112 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1113 GST_WARNING ("First header not a theora identification header, ignoring");
1115 GstMatroskaTrackVideoContext *videocontext;
1116 guint fps_num, fps_denom, par_num, par_denom;
1119 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1121 videocontext = (GstMatroskaTrackVideoContext *) context;
1122 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1123 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1124 hdr += 3 + 3 + 1 + 1;
1125 fps_num = GST_READ_UINT32_BE (hdr);
1126 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1127 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1128 fps_denom, fps_num);
1130 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1131 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1132 if (par_num > 0 && par_num > 0) {
1133 if (par_num > par_denom) {
1134 videocontext->display_width =
1135 videocontext->pixel_width * par_num / par_denom;
1136 videocontext->display_height = videocontext->pixel_height;
1137 } else if (par_num < par_denom) {
1138 videocontext->display_width = videocontext->pixel_width;
1139 videocontext->display_height =
1140 videocontext->pixel_height * par_denom / par_num;
1142 videocontext->display_width = 0;
1143 videocontext->display_height = 0;
1146 videocontext->display_width = 0;
1147 videocontext->display_height = 0;
1153 gst_buffer_unref (buf0);
1159 kate_streamheader_to_codecdata (const GValue * streamheader,
1160 GstMatroskaTrackContext * context)
1162 GstBuffer *buf0 = NULL;
1164 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1167 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1168 GST_WARNING ("First kate header too small, ignoring");
1169 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1170 GST_WARNING ("First header not a kate identification header, ignoring");
1174 gst_buffer_unref (buf0);
1180 flac_streamheader_to_codecdata (const GValue * streamheader,
1181 GstMatroskaTrackContext * context)
1188 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1189 GST_WARNING ("No or invalid streamheader field in the caps");
1193 bufarr = g_value_peek_pointer (streamheader);
1194 if (bufarr->len < 2) {
1195 GST_WARNING ("Too few headers in streamheader field");
1199 context->xiph_headers_to_skip = bufarr->len + 1;
1201 bufval = &g_array_index (bufarr, GValue, 0);
1202 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1203 GST_WARNING ("streamheaders array does not contain GstBuffers");
1207 buffer = g_value_peek_pointer (bufval);
1209 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1210 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1211 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1212 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1213 GST_WARNING ("Invalid streamheader for FLAC");
1217 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1218 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1219 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1220 GST_BUFFER_SIZE (buffer) - 9);
1222 for (i = 1; i < bufarr->len; i++) {
1223 bufval = &g_array_index (bufarr, GValue, i);
1225 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1226 g_free (context->codec_priv);
1227 context->codec_priv = NULL;
1228 context->codec_priv_size = 0;
1229 GST_WARNING ("streamheaders array does not contain GstBuffers");
1233 buffer = g_value_peek_pointer (bufval);
1235 context->codec_priv =
1236 g_realloc (context->codec_priv,
1237 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1238 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1239 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1240 context->codec_priv_size =
1241 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1248 speex_streamheader_to_codecdata (const GValue * streamheader,
1249 GstMatroskaTrackContext * context)
1255 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1256 GST_WARNING ("No or invalid streamheader field in the caps");
1260 bufarr = g_value_peek_pointer (streamheader);
1261 if (bufarr->len != 2) {
1262 GST_WARNING ("Too few headers in streamheader field");
1266 context->xiph_headers_to_skip = bufarr->len + 1;
1268 bufval = &g_array_index (bufarr, GValue, 0);
1269 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1270 GST_WARNING ("streamheaders array does not contain GstBuffers");
1274 buffer = g_value_peek_pointer (bufval);
1276 if (GST_BUFFER_SIZE (buffer) < 80
1277 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1278 GST_WARNING ("Invalid streamheader for Speex");
1282 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1283 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1284 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1285 GST_BUFFER_SIZE (buffer));
1287 bufval = &g_array_index (bufarr, GValue, 1);
1289 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1290 g_free (context->codec_priv);
1291 context->codec_priv = NULL;
1292 context->codec_priv_size = 0;
1293 GST_WARNING ("streamheaders array does not contain GstBuffers");
1297 buffer = g_value_peek_pointer (bufval);
1299 context->codec_priv =
1300 g_realloc (context->codec_priv,
1301 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1302 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1303 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1304 context->codec_priv_size =
1305 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1310 static const gchar *
1311 aac_codec_data_to_codec_id (const GstBuffer * buf)
1313 const gchar *result;
1316 /* default to MAIN */
1319 if (GST_BUFFER_SIZE (buf) >= 2) {
1320 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1338 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1347 * gst_matroska_mux_audio_pad_setcaps:
1348 * @pad: Pad which got the caps.
1351 * Setcaps function for audio sink pad.
1353 * Returns: #TRUE on success.
1356 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1358 GstMatroskaTrackContext *context = NULL;
1359 GstMatroskaTrackAudioContext *audiocontext;
1360 GstMatroskaMux *mux;
1361 GstMatroskaPad *collect_pad;
1362 const gchar *mimetype;
1363 gint samplerate = 0, channels = 0;
1364 GstStructure *structure;
1365 const GValue *codec_data = NULL;
1366 const GstBuffer *buf = NULL;
1367 const gchar *stream_format = NULL;
1369 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1372 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1373 g_assert (collect_pad);
1374 context = collect_pad->track;
1376 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1377 audiocontext = (GstMatroskaTrackAudioContext *) context;
1379 structure = gst_caps_get_structure (caps, 0);
1380 mimetype = gst_structure_get_name (structure);
1383 gst_structure_get_int (structure, "rate", &samplerate);
1384 gst_structure_get_int (structure, "channels", &channels);
1386 audiocontext->samplerate = samplerate;
1387 audiocontext->channels = channels;
1388 audiocontext->bitdepth = 0;
1389 context->default_duration = 0;
1391 codec_data = gst_structure_get_value (structure, "codec_data");
1393 buf = gst_value_get_buffer (codec_data);
1395 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1396 * data and other settings
1400 if (!strcmp (mimetype, "audio/mpeg")) {
1401 gint mpegversion = 0;
1403 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1404 switch (mpegversion) {
1410 gst_structure_get_int (structure, "layer", &layer);
1412 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1413 GST_WARNING_OBJECT (mux,
1414 "Unable to determine MPEG audio version, assuming 1");
1420 else if (layer == 2)
1422 else if (version == 2)
1427 context->default_duration =
1428 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1432 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1435 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1438 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1447 stream_format = gst_structure_get_string (structure, "stream-format");
1448 /* check this is raw aac */
1449 if (stream_format) {
1450 if (strcmp (stream_format, "raw") != 0) {
1451 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1455 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1460 if (mpegversion == 2)
1462 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1463 aac_codec_data_to_codec_id (buf));
1464 else if (mpegversion == 4)
1466 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1467 aac_codec_data_to_codec_id (buf));
1469 g_assert_not_reached ();
1471 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1478 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1480 gint endianness = G_LITTLE_ENDIAN;
1481 gboolean signedness = TRUE;
1483 if (!gst_structure_get_int (structure, "width", &width) ||
1484 !gst_structure_get_int (structure, "depth", &depth) ||
1485 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1486 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1491 !gst_structure_get_int (structure, "endianness", &endianness)) {
1492 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1496 if (width != depth) {
1497 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1501 /* FIXME: where is this spec'ed out? (tpm) */
1502 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1503 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1507 audiocontext->bitdepth = depth;
1508 if (endianness == G_BIG_ENDIAN)
1509 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1511 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1513 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1516 if (!gst_structure_get_int (structure, "width", &width)) {
1517 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1521 audiocontext->bitdepth = width;
1522 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1524 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1525 const GValue *streamheader;
1527 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1529 if (context->codec_priv != NULL) {
1530 g_free (context->codec_priv);
1531 context->codec_priv = NULL;
1532 context->codec_priv_size = 0;
1535 streamheader = gst_structure_get_value (structure, "streamheader");
1536 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1537 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1538 ("vorbis stream headers missing or malformed"));
1541 } else if (!strcmp (mimetype, "audio/x-flac")) {
1542 const GValue *streamheader;
1544 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1545 if (context->codec_priv != NULL) {
1546 g_free (context->codec_priv);
1547 context->codec_priv = NULL;
1548 context->codec_priv_size = 0;
1551 streamheader = gst_structure_get_value (structure, "streamheader");
1552 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1553 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1554 ("flac stream headers missing or malformed"));
1557 } else if (!strcmp (mimetype, "audio/x-speex")) {
1558 const GValue *streamheader;
1560 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1561 if (context->codec_priv != NULL) {
1562 g_free (context->codec_priv);
1563 context->codec_priv = NULL;
1564 context->codec_priv_size = 0;
1567 streamheader = gst_structure_get_value (structure, "streamheader");
1568 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1569 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1570 ("speex stream headers missing or malformed"));
1573 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1574 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1575 } else if (!strcmp (mimetype, "audio/x-tta")) {
1578 /* TTA frame duration */
1579 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1581 gst_structure_get_int (structure, "width", &width);
1582 audiocontext->bitdepth = width;
1583 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1585 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1587 const GValue *mdpr_data;
1589 gst_structure_get_int (structure, "raversion", &raversion);
1590 switch (raversion) {
1592 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1595 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1598 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1604 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1605 if (mdpr_data != NULL) {
1606 guint8 *priv_data = NULL;
1607 guint priv_data_size = 0;
1609 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1611 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1612 priv_data = g_malloc0 (priv_data_size);
1614 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1616 context->codec_priv = priv_data;
1617 context->codec_priv_size = priv_data_size;
1620 } else if (!strcmp (mimetype, "audio/x-wma")) {
1622 guint codec_priv_size;
1629 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1630 || !gst_structure_get_int (structure, "block_align", &block_align)
1631 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1632 || samplerate == 0 || channels == 0) {
1633 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1634 "channels/rate on WMA caps");
1638 switch (wmaversion) {
1640 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1643 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1646 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1649 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1653 if (gst_structure_get_int (structure, "depth", &depth))
1654 audiocontext->bitdepth = depth;
1656 codec_priv_size = WAVEFORMATEX_SIZE;
1658 codec_priv_size += GST_BUFFER_SIZE (buf);
1660 /* serialize waveformatex structure */
1661 codec_priv = g_malloc0 (codec_priv_size);
1662 GST_WRITE_UINT16_LE (codec_priv, format);
1663 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1664 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1665 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1666 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1667 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1669 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1671 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1673 /* process codec private/initialization data, if any */
1675 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1676 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1679 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1680 context->codec_priv = (gpointer) codec_priv;
1681 context->codec_priv_size = codec_priv_size;
1689 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1690 GST_PAD_NAME (pad), caps);
1697 * gst_matroska_mux_subtitle_pad_setcaps:
1698 * @pad: Pad which got the caps.
1701 * Setcaps function for subtitle sink pad.
1703 * Returns: #TRUE on success.
1706 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1709 * Consider this as boilerplate code for now. There is
1710 * no single subtitle creation element in GStreamer,
1711 * neither do I know how subtitling works at all. */
1713 /* There is now (at least) one such alement (kateenc), and I'm going
1714 to handle it here and claim it works when it can be piped back
1715 through GStreamer and VLC */
1717 GstMatroskaTrackContext *context = NULL;
1718 GstMatroskaTrackSubtitleContext *scontext;
1719 GstMatroskaMux *mux;
1720 GstMatroskaPad *collect_pad;
1721 const gchar *mimetype;
1722 GstStructure *structure;
1724 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1727 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1728 g_assert (collect_pad);
1729 context = collect_pad->track;
1731 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1732 scontext = (GstMatroskaTrackSubtitleContext *) context;
1734 structure = gst_caps_get_structure (caps, 0);
1735 mimetype = gst_structure_get_name (structure);
1738 scontext->check_utf8 = 1;
1739 scontext->invalid_utf8 = 0;
1740 context->default_duration = 0;
1742 /* TODO: - other format than Kate */
1744 if (!strcmp (mimetype, "subtitle/x-kate")) {
1745 const GValue *streamheader;
1747 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1749 if (context->codec_priv != NULL) {
1750 g_free (context->codec_priv);
1751 context->codec_priv = NULL;
1752 context->codec_priv_size = 0;
1755 streamheader = gst_structure_get_value (structure, "streamheader");
1756 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1757 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1758 ("kate stream headers missing or malformed"));
1769 * gst_matroska_mux_request_new_pad:
1770 * @element: #GstMatroskaMux.
1771 * @templ: #GstPadTemplate.
1772 * @pad_name: New pad name.
1774 * Request pad function for sink templates.
1776 * Returns: New #GstPad.
1779 gst_matroska_mux_request_new_pad (GstElement * element,
1780 GstPadTemplate * templ, const gchar * pad_name)
1782 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1783 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1784 GstMatroskaPad *collect_pad;
1785 GstPad *newpad = NULL;
1787 GstPadSetCapsFunction setcapsfunc = NULL;
1788 GstMatroskaTrackContext *context = NULL;
1790 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1791 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1792 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1793 context = (GstMatroskaTrackContext *)
1794 g_new0 (GstMatroskaTrackAudioContext, 1);
1795 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1796 context->name = g_strdup ("Audio");
1797 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1798 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1799 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1800 context = (GstMatroskaTrackContext *)
1801 g_new0 (GstMatroskaTrackVideoContext, 1);
1802 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1803 context->name = g_strdup ("Video");
1804 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1805 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1806 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1807 context = (GstMatroskaTrackContext *)
1808 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1809 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1810 context->name = g_strdup ("Subtitle");
1812 GST_WARNING_OBJECT (mux, "This is not our template!");
1816 newpad = gst_pad_new_from_template (templ, name);
1818 collect_pad = (GstMatroskaPad *)
1819 gst_collect_pads_add_pad_full (mux->collect, newpad,
1820 sizeof (GstMatroskaPad),
1821 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1823 collect_pad->track = context;
1824 gst_matroska_pad_reset (collect_pad, FALSE);
1826 /* FIXME: hacked way to override/extend the event function of
1827 * GstCollectPads; because it sets its own event function giving the
1828 * element no access to events.
1829 * TODO GstCollectPads should really give its 'users' a clean chance to
1830 * properly handle events that are not meant for collectpads itself.
1831 * Perhaps a callback or so, though rejected (?) in #340060.
1832 * This would allow (clean) transcoding of info from demuxer/streams
1833 * to another muxer */
1834 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1835 gst_pad_set_event_function (newpad,
1836 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1838 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1839 gst_pad_set_active (newpad, TRUE);
1840 gst_element_add_pad (element, newpad);
1847 * gst_matroska_mux_release_pad:
1848 * @element: #GstMatroskaMux.
1849 * @pad: Pad to release.
1851 * Release a previously requested pad.
1854 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1856 GstMatroskaMux *mux;
1859 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1861 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1862 GstCollectData *cdata = (GstCollectData *) walk->data;
1863 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1865 if (cdata->pad == pad) {
1866 GstClockTime min_dur; /* observed minimum duration */
1868 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1869 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1870 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1871 if (collect_pad->duration < min_dur)
1872 collect_pad->duration = min_dur;
1875 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1876 mux->duration < collect_pad->duration)
1877 mux->duration = collect_pad->duration;
1883 gst_collect_pads_remove_pad (mux->collect, pad);
1884 if (gst_element_remove_pad (element, pad))
1890 * gst_matroska_mux_track_header:
1891 * @mux: #GstMatroskaMux
1892 * @context: Tack context.
1894 * Write a track header.
1897 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1898 GstMatroskaTrackContext * context)
1900 GstEbmlWrite *ebml = mux->ebml_write;
1903 /* TODO: check if everything necessary is written and check default values */
1905 /* track type goes before the type-specific stuff */
1906 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1907 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1909 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1910 gst_matroska_mux_create_uid ());
1911 if (context->default_duration) {
1912 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1913 context->default_duration);
1915 if (context->language) {
1916 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1920 /* type-specific stuff */
1921 switch (context->type) {
1922 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1923 GstMatroskaTrackVideoContext *videocontext =
1924 (GstMatroskaTrackVideoContext *) context;
1926 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1927 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1928 videocontext->pixel_width);
1929 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1930 videocontext->pixel_height);
1931 if (videocontext->display_width && videocontext->display_height) {
1932 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1933 videocontext->display_width);
1934 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1935 videocontext->display_height);
1937 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1938 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1939 if (videocontext->fourcc) {
1940 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1942 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1943 (gpointer) & fcc_le, 4);
1945 gst_ebml_write_master_finish (ebml, master);
1950 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1951 GstMatroskaTrackAudioContext *audiocontext =
1952 (GstMatroskaTrackAudioContext *) context;
1954 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1955 if (audiocontext->samplerate != 8000)
1956 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1957 audiocontext->samplerate);
1958 if (audiocontext->channels != 1)
1959 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1960 audiocontext->channels);
1961 if (audiocontext->bitdepth) {
1962 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1963 audiocontext->bitdepth);
1965 gst_ebml_write_master_finish (ebml, master);
1971 /* doesn't need type-specific data */
1975 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1976 if (context->codec_priv)
1977 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1978 context->codec_priv, context->codec_priv_size);
1979 /* FIXME: until we have a nice way of getting the codecname
1980 * out of the caps, I'm not going to enable this. Too much
1981 * (useless, double, boring) work... */
1982 /* TODO: Use value from tags if any */
1983 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1984 context->codec_name); */
1985 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1990 * gst_matroska_mux_start:
1991 * @mux: #GstMatroskaMux
1993 * Start a new matroska file (write headers etc...)
1996 gst_matroska_mux_start (GstMatroskaMux * mux)
1998 GstEbmlWrite *ebml = mux->ebml_write;
1999 const gchar *doctype;
2000 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2001 GST_MATROSKA_ID_TRACKS,
2002 GST_MATROSKA_ID_CUES,
2003 GST_MATROSKA_ID_TAGS,
2006 guint64 master, child;
2010 GstClockTime duration = 0;
2011 guint32 segment_uid[4];
2012 GTimeVal time = { 0, 0 };
2014 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2015 ebml->caps = gst_caps_from_string ("video/webm");
2017 ebml->caps = gst_caps_from_string ("video/x-matroska");
2019 /* we start with a EBML header */
2020 doctype = mux->doctype;
2021 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2022 doctype, mux->doctype_version);
2023 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2025 /* the rest of the header is cached */
2026 gst_ebml_write_set_cache (ebml, 0x1000);
2028 /* start a segment */
2030 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2031 mux->segment_master = ebml->pos;
2033 if (!mux->streamable) {
2034 /* seekhead (table of contents) - we set the positions later */
2035 mux->seekhead_pos = ebml->pos;
2036 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2037 for (i = 0; seekhead_id[i] != 0; i++) {
2038 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2039 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2040 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2041 gst_ebml_write_master_finish (ebml, child);
2043 gst_ebml_write_master_finish (ebml, master);
2047 mux->info_pos = ebml->pos;
2048 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2049 for (i = 0; i < 4; i++) {
2050 segment_uid[i] = g_random_int ();
2052 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2053 (guint8 *) segment_uid, 16);
2054 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2055 mux->duration_pos = ebml->pos;
2057 if (!mux->streamable) {
2058 for (collected = mux->collect->data; collected;
2059 collected = g_slist_next (collected)) {
2060 GstMatroskaPad *collect_pad;
2061 GstFormat format = GST_FORMAT_TIME;
2063 gint64 trackduration;
2065 collect_pad = (GstMatroskaPad *) collected->data;
2066 thepad = collect_pad->collect.pad;
2068 /* Query the total length of the track. */
2069 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2070 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2071 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2072 GST_TIME_ARGS (trackduration));
2073 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2074 duration = (GstClockTime) trackduration;
2078 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2079 gst_guint64_to_gdouble (duration) /
2080 gst_guint64_to_gdouble (mux->time_scale));
2082 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2083 "GStreamer plugin version " PACKAGE_VERSION);
2084 if (mux->writing_app && mux->writing_app[0]) {
2085 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2087 g_get_current_time (&time);
2088 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2089 gst_ebml_write_master_finish (ebml, master);
2092 mux->tracks_pos = ebml->pos;
2093 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2095 for (collected = mux->collect->data; collected;
2096 collected = g_slist_next (collected)) {
2097 GstMatroskaPad *collect_pad;
2100 collect_pad = (GstMatroskaPad *) collected->data;
2101 thepad = collect_pad->collect.pad;
2103 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2104 collect_pad->track->codec_id != 0) {
2105 collect_pad->track->num = tracknum++;
2106 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2107 gst_matroska_mux_track_header (mux, collect_pad->track);
2108 gst_ebml_write_master_finish (ebml, child);
2111 gst_ebml_write_master_finish (ebml, master);
2113 /* lastly, flush the cache */
2114 gst_ebml_write_flush_cache (ebml, FALSE);
2118 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2121 /* TODO: more sensible tag mappings */
2124 const gchar *matroska_tagname;
2125 const gchar *gstreamer_tagname;
2129 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2130 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2131 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2132 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2133 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2134 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2135 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2136 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2137 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2138 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2139 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2140 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2141 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2142 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2143 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2145 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2147 guint64 simpletag_master;
2149 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2150 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2151 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2153 if (strcmp (tagname_gst, tag) == 0) {
2154 GValue src = { 0, };
2157 if (!gst_tag_list_copy_value (&src, list, tag))
2159 if ((dest = gst_value_serialize (&src))) {
2161 simpletag_master = gst_ebml_write_master_start (ebml,
2162 GST_MATROSKA_ID_SIMPLETAG);
2163 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2164 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2165 gst_ebml_write_master_finish (ebml, simpletag_master);
2168 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2170 g_value_unset (&src);
2178 * gst_matroska_mux_finish:
2179 * @mux: #GstMatroskaMux
2181 * Finish a new matroska file (write index etc...)
2184 gst_matroska_mux_finish (GstMatroskaMux * mux)
2186 GstEbmlWrite *ebml = mux->ebml_write;
2188 guint64 duration = 0;
2190 const GstTagList *tags;
2192 /* finish last cluster */
2194 gst_ebml_write_master_finish (ebml, mux->cluster);
2198 if (mux->index != NULL) {
2200 guint64 master, pointentry_master, trackpos_master;
2202 mux->cues_pos = ebml->pos;
2203 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2204 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2206 for (n = 0; n < mux->num_indexes; n++) {
2207 GstMatroskaIndex *idx = &mux->index[n];
2209 pointentry_master = gst_ebml_write_master_start (ebml,
2210 GST_MATROSKA_ID_POINTENTRY);
2211 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2212 idx->time / mux->time_scale);
2213 trackpos_master = gst_ebml_write_master_start (ebml,
2214 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2215 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2216 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2217 idx->pos - mux->segment_master);
2218 gst_ebml_write_master_finish (ebml, trackpos_master);
2219 gst_ebml_write_master_finish (ebml, pointentry_master);
2222 gst_ebml_write_master_finish (ebml, master);
2223 gst_ebml_write_flush_cache (ebml, FALSE);
2227 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2229 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2230 guint64 master_tags, master_tag;
2232 GST_DEBUG ("Writing tags");
2234 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2235 mux->tags_pos = ebml->pos;
2236 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2237 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2238 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2239 gst_ebml_write_master_finish (ebml, master_tag);
2240 gst_ebml_write_master_finish (ebml, master_tags);
2243 /* update seekhead. We know that:
2244 * - a seekhead contains 4 entries.
2245 * - order of entries is as above.
2246 * - a seekhead has a 4-byte header + 8-byte length
2247 * - each entry is 2-byte master, 2-byte ID pointer,
2248 * 2-byte length pointer, all 8/1-byte length, 4-
2249 * byte ID and 8-byte length pointer, where the
2250 * length pointer starts at 20.
2251 * - all entries are local to the segment (so pos - segment_master).
2252 * - so each entry is at 12 + 20 + num * 28. */
2253 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2254 mux->info_pos - mux->segment_master);
2255 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2256 mux->tracks_pos - mux->segment_master);
2257 if (mux->index != NULL) {
2258 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2259 mux->cues_pos - mux->segment_master);
2262 guint64 my_pos = ebml->pos;
2264 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2265 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2266 gst_ebml_write_seek (ebml, my_pos);
2269 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2270 mux->tags_pos - mux->segment_master);
2273 guint64 my_pos = ebml->pos;
2275 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2276 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2277 gst_ebml_write_seek (ebml, my_pos);
2280 /* update duration */
2281 /* first get the overall duration */
2282 /* a released track may have left a duration in here */
2283 duration = mux->duration;
2284 for (collected = mux->collect->data; collected;
2285 collected = g_slist_next (collected)) {
2286 GstMatroskaPad *collect_pad;
2287 GstClockTime min_duration; /* observed minimum duration */
2289 collect_pad = (GstMatroskaPad *) collected->data;
2291 GST_DEBUG_OBJECT (mux,
2292 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2293 " end ts %" GST_TIME_FORMAT, collect_pad,
2294 GST_TIME_ARGS (collect_pad->start_ts),
2295 GST_TIME_ARGS (collect_pad->end_ts));
2297 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2298 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2300 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2301 if (collect_pad->duration < min_duration)
2302 collect_pad->duration = min_duration;
2303 GST_DEBUG_OBJECT (collect_pad,
2304 "final track duration: %" GST_TIME_FORMAT,
2305 GST_TIME_ARGS (collect_pad->duration));
2308 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2309 duration < collect_pad->duration)
2310 duration = collect_pad->duration;
2312 if (duration != 0) {
2313 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2314 GST_TIME_ARGS (duration));
2315 pos = mux->ebml_write->pos;
2316 gst_ebml_write_seek (ebml, mux->duration_pos);
2317 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2318 gst_guint64_to_gdouble (duration) /
2319 gst_guint64_to_gdouble (mux->time_scale));
2320 gst_ebml_write_seek (ebml, pos);
2323 guint64 my_pos = ebml->pos;
2325 gst_ebml_write_seek (ebml, mux->duration_pos);
2326 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2327 gst_ebml_write_seek (ebml, my_pos);
2329 GST_DEBUG_OBJECT (mux, "finishing segment");
2330 /* finish segment - this also writes element length */
2331 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2336 * gst_matroska_mux_best_pad:
2337 * @mux: #GstMatroskaMux
2338 * @popped: True if at least one buffer was popped from #GstCollectPads
2340 * Find a pad with the oldest data
2341 * (data from this pad should be written first).
2343 * Returns: Selected pad.
2345 static GstMatroskaPad *
2346 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2349 GstMatroskaPad *best = NULL;
2352 for (collected = mux->collect->data; collected;
2353 collected = g_slist_next (collected)) {
2354 GstMatroskaPad *collect_pad;
2356 collect_pad = (GstMatroskaPad *) collected->data;
2357 /* fetch a new buffer if needed */
2358 if (collect_pad->buffer == NULL) {
2359 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2360 (GstCollectData *) collect_pad);
2362 if (collect_pad->buffer != NULL)
2366 /* if we have a buffer check if it is better then the current best one */
2367 if (collect_pad->buffer != NULL) {
2368 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2369 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2370 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2371 GST_BUFFER_TIMESTAMP (best->buffer))) {
2381 * gst_matroska_mux_buffer_header:
2382 * @track: Track context.
2383 * @relative_timestamp: relative timestamp of the buffer
2384 * @flags: Buffer flags.
2386 * Create a buffer containing buffer header.
2388 * Returns: New buffer.
2391 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2392 gint16 relative_timestamp, int flags)
2396 hdr = gst_buffer_new_and_alloc (4);
2397 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2398 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2399 /* time relative to clustertime */
2400 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2403 GST_BUFFER_DATA (hdr)[3] = flags;
2408 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2409 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2410 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2413 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2414 GstMatroskaPad * collect_pad, GstBuffer * buf)
2416 GstMatroskaTrackVideoContext *ctx =
2417 (GstMatroskaTrackVideoContext *) collect_pad->track;
2418 const guint8 *data = GST_BUFFER_DATA (buf);
2419 guint size = GST_BUFFER_SIZE (buf);
2421 guint32 next_parse_offset;
2422 GstBuffer *ret = NULL;
2423 gboolean is_muxing_unit = FALSE;
2425 if (GST_BUFFER_SIZE (buf) < 13) {
2426 gst_buffer_unref (buf);
2430 /* Check if this buffer contains a picture or end-of-sequence packet */
2431 while (size >= 13) {
2432 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2433 gst_buffer_unref (buf);
2437 parse_code = GST_READ_UINT8 (data + 4);
2438 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2439 if (ctx->dirac_unit) {
2440 gst_buffer_unref (ctx->dirac_unit);
2441 ctx->dirac_unit = NULL;
2443 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2444 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2445 is_muxing_unit = TRUE;
2449 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2451 if (G_UNLIKELY (next_parse_offset == 0))
2454 data += next_parse_offset;
2455 size -= next_parse_offset;
2458 if (ctx->dirac_unit)
2459 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2461 ctx->dirac_unit = gst_buffer_ref (buf);
2463 if (is_muxing_unit) {
2464 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2465 ctx->dirac_unit = NULL;
2466 gst_buffer_copy_metadata (ret, buf,
2467 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2468 GST_BUFFER_COPY_CAPS);
2469 gst_buffer_unref (buf);
2471 gst_buffer_unref (buf);
2479 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2483 GValue streamheader = { 0 };
2484 GValue bufval = { 0 };
2485 GstBuffer *streamheader_buffer;
2486 GstEbmlWrite *ebml = mux->ebml_write;
2488 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2489 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2490 caps = gst_caps_from_string ("video/webm");
2492 caps = gst_caps_from_string ("video/x-matroska");
2494 s = gst_caps_get_structure (caps, 0);
2495 g_value_init (&streamheader, GST_TYPE_ARRAY);
2496 g_value_init (&bufval, GST_TYPE_BUFFER);
2497 gst_value_set_buffer (&bufval, streamheader_buffer);
2498 gst_value_array_append_value (&streamheader, &bufval);
2499 g_value_unset (&bufval);
2500 gst_structure_set_value (s, "streamheader", &streamheader);
2501 g_value_unset (&streamheader);
2502 gst_caps_unref (ebml->caps);
2503 gst_buffer_unref (streamheader_buffer);
2508 * gst_matroska_mux_write_data:
2509 * @mux: #GstMatroskaMux
2510 * @collect_pad: #GstMatroskaPad with the data
2512 * Write collected data (called from gst_matroska_mux_collected).
2514 * Returns: Result of the gst_pad_push issued to write the data.
2516 static GstFlowReturn
2517 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2519 GstEbmlWrite *ebml = mux->ebml_write;
2520 GstBuffer *buf, *hdr;
2522 gboolean write_duration;
2523 gint16 relative_timestamp;
2524 gint64 relative_timestamp64;
2525 guint64 block_duration;
2526 gboolean is_video_keyframe = FALSE;
2529 buf = collect_pad->buffer;
2530 collect_pad->buffer = NULL;
2532 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2533 if (collect_pad->track->xiph_headers_to_skip > 0) {
2534 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2535 gst_buffer_unref (buf);
2536 --collect_pad->track->xiph_headers_to_skip;
2540 /* for dirac we have to queue up everything up to a picture unit */
2541 if (collect_pad->track->codec_id != NULL &&
2542 strcmp (collect_pad->track->codec_id,
2543 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2544 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2549 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2550 * this would wreak havoc with time stored in matroska file */
2551 /* TODO: maybe calculate a timestamp by using the previous timestamp
2552 * and default duration */
2553 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2554 GST_WARNING_OBJECT (collect_pad->collect.pad,
2555 "Invalid buffer timestamp; dropping buffer");
2556 gst_buffer_unref (buf);
2560 /* set the timestamp for outgoing buffers */
2561 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2563 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2564 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2565 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2566 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2567 is_video_keyframe = TRUE;
2571 /* start a new cluster at every keyframe or when we may be reaching the
2572 * limit of the relative timestamp */
2573 if (mux->cluster_time +
2574 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2575 || is_video_keyframe) {
2576 if (!mux->streamable)
2577 gst_ebml_write_master_finish (ebml, mux->cluster);
2578 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2579 mux->cluster_pos = ebml->pos;
2580 gst_ebml_write_set_cache (ebml, 0x20);
2582 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2583 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2584 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2586 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2587 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2589 gst_ebml_write_flush_cache (ebml, TRUE);
2590 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2591 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2592 mux->prev_cluster_size);
2597 mux->cluster_pos = ebml->pos;
2598 gst_ebml_write_set_cache (ebml, 0x20);
2599 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2600 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2601 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2602 gst_ebml_write_flush_cache (ebml, TRUE);
2603 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2606 /* update duration of this track */
2607 if (GST_BUFFER_DURATION_IS_VALID (buf))
2608 collect_pad->duration += GST_BUFFER_DURATION (buf);
2610 /* We currently write index entries for all video tracks or for the audio
2611 * track in a single-track audio file. This could be improved by keeping the
2612 * index only for the *first* video track. */
2614 /* TODO: index is useful for every track, should contain the number of
2615 * the block in the cluster which contains the timestamp, should also work
2616 * for files with multiple audio tracks.
2618 if (is_video_keyframe ||
2619 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2620 (mux->num_streams == 1))) {
2623 if (mux->min_index_interval != 0) {
2624 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2625 if (mux->index[last_idx].track == collect_pad->track->num)
2630 if (last_idx < 0 || mux->min_index_interval == 0 ||
2631 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2632 >= mux->min_index_interval)) {
2633 GstMatroskaIndex *idx;
2635 if (mux->num_indexes % 32 == 0) {
2636 mux->index = g_renew (GstMatroskaIndex, mux->index,
2637 mux->num_indexes + 32);
2639 idx = &mux->index[mux->num_indexes++];
2641 idx->pos = mux->cluster_pos;
2642 idx->time = GST_BUFFER_TIMESTAMP (buf);
2643 idx->track = collect_pad->track->num;
2647 /* Check if the duration differs from the default duration. */
2648 write_duration = FALSE;
2649 block_duration = GST_BUFFER_DURATION (buf);
2650 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2651 if (block_duration != collect_pad->track->default_duration) {
2652 write_duration = TRUE;
2656 /* write the block, for doctype v2 use SimpleBlock if possible
2657 * one slice (*breath*).
2658 * FIXME: Need to do correct lacing! */
2659 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2660 if (relative_timestamp64 >= 0) {
2661 /* round the timestamp */
2662 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2664 /* round the timestamp */
2665 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2667 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2669 if (mux->doctype_version > 1 && !write_duration) {
2671 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2674 gst_matroska_mux_create_buffer_header (collect_pad->track,
2675 relative_timestamp, flags);
2676 gst_ebml_write_set_cache (ebml, 0x40);
2677 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2678 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2679 gst_ebml_write_buffer (ebml, hdr);
2680 gst_ebml_write_flush_cache (ebml, FALSE);
2681 gst_ebml_write_buffer (ebml, buf);
2683 return gst_ebml_last_write_result (ebml);
2685 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2686 /* write and call order slightly unnatural,
2687 * but avoids seek and minizes pushing */
2688 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2690 gst_matroska_mux_create_buffer_header (collect_pad->track,
2691 relative_timestamp, 0);
2692 if (write_duration) {
2693 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2694 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2696 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2697 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2698 gst_ebml_write_buffer (ebml, hdr);
2699 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2700 gst_ebml_write_flush_cache (ebml, FALSE);
2701 gst_ebml_write_buffer (ebml, buf);
2702 return gst_ebml_last_write_result (ebml);
2708 * gst_matroska_mux_collected:
2709 * @pads: #GstCollectPads
2710 * @uuser_data: #GstMatroskaMux
2712 * Collectpads callback.
2714 * Returns: #GstFlowReturn
2716 static GstFlowReturn
2717 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2719 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2720 GstEbmlWrite *ebml = mux->ebml_write;
2721 GstMatroskaPad *best;
2725 GST_DEBUG_OBJECT (mux, "Collected pads");
2727 /* start with a header */
2728 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2729 if (mux->collect->data == NULL) {
2730 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2731 ("No input streams configured"));
2732 return GST_FLOW_ERROR;
2734 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2735 gst_ebml_start_streamheader (ebml);
2736 gst_matroska_mux_start (mux);
2737 gst_matroska_mux_stop_streamheader (mux);
2738 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2742 /* which stream to write from? */
2743 best = gst_matroska_mux_best_pad (mux, &popped);
2745 /* if there is no best pad, we have reached EOS */
2747 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2748 if (!mux->streamable) {
2749 gst_matroska_mux_finish (mux);
2751 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2753 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2754 ret = GST_FLOW_UNEXPECTED;
2757 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2758 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2759 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2760 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2762 /* make note of first and last encountered timestamps, so we can calculate
2763 * the actual duration later when we send an updated header on eos */
2764 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2765 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2766 GstClockTime end_ts = start_ts;
2768 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2769 end_ts += GST_BUFFER_DURATION (best->buffer);
2770 else if (best->track->default_duration)
2771 end_ts += best->track->default_duration;
2773 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2774 best->end_ts = end_ts;
2776 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2777 start_ts < best->start_ts))
2778 best->start_ts = start_ts;
2781 /* write one buffer */
2782 ret = gst_matroska_mux_write_data (mux, best);
2783 } while (ret == GST_FLOW_OK && !popped);
2790 * gst_matroska_mux_change_state:
2791 * @element: #GstMatroskaMux
2792 * @transition: State change transition.
2794 * Change the muxer state.
2796 * Returns: #GstStateChangeReturn
2798 static GstStateChangeReturn
2799 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2801 GstStateChangeReturn ret;
2802 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2804 switch (transition) {
2805 case GST_STATE_CHANGE_NULL_TO_READY:
2807 case GST_STATE_CHANGE_READY_TO_PAUSED:
2808 gst_collect_pads_start (mux->collect);
2810 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2812 case GST_STATE_CHANGE_PAUSED_TO_READY:
2813 gst_collect_pads_stop (mux->collect);
2819 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2821 switch (transition) {
2822 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2824 case GST_STATE_CHANGE_PAUSED_TO_READY:
2825 gst_matroska_mux_reset (GST_ELEMENT (mux));
2827 case GST_STATE_CHANGE_READY_TO_NULL:
2837 gst_matroska_mux_set_property (GObject * object,
2838 guint prop_id, const GValue * value, GParamSpec * pspec)
2840 GstMatroskaMux *mux;
2842 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2843 mux = GST_MATROSKA_MUX (object);
2846 case ARG_WRITING_APP:
2847 if (!g_value_get_string (value)) {
2848 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2851 g_free (mux->writing_app);
2852 mux->writing_app = g_value_dup_string (value);
2854 case ARG_DOCTYPE_VERSION:
2855 mux->doctype_version = g_value_get_int (value);
2857 case ARG_MIN_INDEX_INTERVAL:
2858 mux->min_index_interval = g_value_get_int64 (value);
2860 case ARG_STREAMABLE:
2861 mux->streamable = g_value_get_boolean (value);
2864 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2870 gst_matroska_mux_get_property (GObject * object,
2871 guint prop_id, GValue * value, GParamSpec * pspec)
2873 GstMatroskaMux *mux;
2875 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2876 mux = GST_MATROSKA_MUX (object);
2879 case ARG_WRITING_APP:
2880 g_value_set_string (value, mux->writing_app);
2882 case ARG_DOCTYPE_VERSION:
2883 g_value_set_int (value, mux->doctype_version);
2885 case ARG_MIN_INDEX_INTERVAL:
2886 g_value_set_int64 (value, mux->min_index_interval);
2888 case ARG_STREAMABLE:
2889 g_value_set_boolean (value, mux->streamable);
2892 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);