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));
300 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
301 g_param_spec_int ("version", "DocType version",
302 "This parameter determines what Matroska features can be used.",
303 1, 2, DEFAULT_DOCTYPE_VERSION, G_PARAM_READWRITE));
304 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
305 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
306 "entries", "An index entry is created every so many nanoseconds.",
307 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
308 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
309 g_param_spec_boolean ("streamable", "Determines whether output should "
310 "be streamable", "If set to true, the output should be as if it is "
311 "to be streamed and hence no indexes written or duration written.",
312 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 gstelement_class->change_state =
315 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
316 gstelement_class->request_new_pad =
317 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
318 gstelement_class->release_pad =
319 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
324 * gst_matroska_mux_init:
325 * @mux: #GstMatroskaMux that should be initialized.
326 * @g_class: Class of the muxer.
328 * Matroska muxer constructor.
331 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
333 GstPadTemplate *templ;
336 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
337 mux->srcpad = gst_pad_new_from_template (templ, "src");
339 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
340 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
342 mux->collect = gst_collect_pads_new ();
343 gst_collect_pads_set_function (mux->collect,
344 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
347 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
348 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
350 /* property defaults */
351 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
352 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
353 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
354 mux->streamable = DEFAULT_STREAMABLE;
356 /* initialize internal variables */
358 mux->num_streams = 0;
359 mux->num_a_streams = 0;
360 mux->num_t_streams = 0;
361 mux->num_v_streams = 0;
363 /* initialize remaining variables */
364 gst_matroska_mux_reset (GST_ELEMENT (mux));
369 * gst_matroska_mux_finalize:
370 * @object: #GstMatroskaMux that should be finalized.
372 * Finalize matroska muxer.
375 gst_matroska_mux_finalize (GObject * object)
377 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
379 gst_object_unref (mux->collect);
380 gst_object_unref (mux->ebml_write);
381 if (mux->writing_app)
382 g_free (mux->writing_app);
384 G_OBJECT_CLASS (parent_class)->finalize (object);
389 * gst_matroska_mux_create_uid:
391 * Generate new unused track UID.
393 * Returns: New track UID.
396 gst_matroska_mux_create_uid (void)
403 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
408 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
409 for (i = 0; i < used_uids->len; i++) {
410 if (g_array_index (used_uids, guint64, i) == uid) {
415 g_array_append_val (used_uids, uid);
418 G_UNLOCK (used_uids);
424 * gst_matroska_pad_reset:
425 * @collect_pad: the #GstMatroskaPad
427 * Reset and/or release resources of a matroska collect pad.
430 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
433 GstMatroskaTrackType type = 0;
435 /* free track information */
436 if (collect_pad->track != NULL) {
437 /* retrieve for optional later use */
438 name = collect_pad->track->name;
439 type = collect_pad->track->type;
440 /* extra for video */
441 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
442 GstMatroskaTrackVideoContext *ctx =
443 (GstMatroskaTrackVideoContext *) collect_pad->track;
445 if (ctx->dirac_unit) {
446 gst_buffer_unref (ctx->dirac_unit);
447 ctx->dirac_unit = NULL;
450 g_free (collect_pad->track->codec_id);
451 g_free (collect_pad->track->codec_name);
453 g_free (collect_pad->track->name);
454 g_free (collect_pad->track->language);
455 g_free (collect_pad->track->codec_priv);
456 g_free (collect_pad->track);
457 collect_pad->track = NULL;
460 /* free cached buffer */
461 if (collect_pad->buffer != NULL) {
462 gst_buffer_unref (collect_pad->buffer);
463 collect_pad->buffer = NULL;
466 if (!full && type != 0) {
467 GstMatroskaTrackContext *context;
469 /* create a fresh context */
471 case GST_MATROSKA_TRACK_TYPE_VIDEO:
472 context = (GstMatroskaTrackContext *)
473 g_new0 (GstMatroskaTrackVideoContext, 1);
475 case GST_MATROSKA_TRACK_TYPE_AUDIO:
476 context = (GstMatroskaTrackContext *)
477 g_new0 (GstMatroskaTrackAudioContext, 1);
479 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
480 context = (GstMatroskaTrackContext *)
481 g_new0 (GstMatroskaTrackSubtitleContext, 1);
484 g_assert_not_reached ();
488 context->type = type;
489 context->name = name;
490 /* TODO: check default values for the context */
491 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
492 collect_pad->track = context;
493 collect_pad->buffer = NULL;
494 collect_pad->duration = 0;
495 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
496 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
501 * gst_matroska_pad_free:
502 * @collect_pad: the #GstMatroskaPad
504 * Release resources of a matroska collect pad.
507 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
509 gst_matroska_pad_reset (collect_pad, TRUE);
514 * gst_matroska_mux_reset:
515 * @element: #GstMatroskaMux that should be reseted.
517 * Reset matroska muxer back to initial state.
520 gst_matroska_mux_reset (GstElement * element)
522 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
525 /* reset EBML write */
526 gst_ebml_write_reset (mux->ebml_write);
529 mux->state = GST_MATROSKA_MUX_STATE_START;
531 /* clean up existing streams */
533 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
534 GstMatroskaPad *collect_pad;
536 collect_pad = (GstMatroskaPad *) walk->data;
538 /* reset collect pad to pristine state */
539 gst_matroska_pad_reset (collect_pad, FALSE);
543 mux->num_indexes = 0;
548 mux->time_scale = GST_MSECOND;
549 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
554 mux->cluster_time = 0;
555 mux->cluster_pos = 0;
556 mux->prev_cluster_size = 0;
559 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
563 * gst_matroska_mux_handle_src_event:
564 * @pad: Pad which received the event.
565 * @event: Received event.
567 * handle events - copied from oggmux without understanding
569 * Returns: #TRUE on success.
572 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
576 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
580 /* disable seeking for now */
586 return gst_pad_event_default (pad, event);
590 * gst_matroska_mux_handle_sink_event:
591 * @pad: Pad which received the event.
592 * @event: Received event.
594 * handle events - informational ones like tags
596 * Returns: #TRUE on success.
599 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
601 GstMatroskaTrackContext *context;
602 GstMatroskaPad *collect_pad;
607 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
609 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
610 switch (GST_EVENT_TYPE (event)) {
614 GST_DEBUG_OBJECT (mux, "received tag event");
615 gst_event_parse_tag (event, &list);
617 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
618 g_assert (collect_pad);
619 context = collect_pad->track;
622 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
623 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
624 const gchar *lang_code;
626 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
628 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
629 context->language = g_strdup (lang_code);
631 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
636 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
637 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
640 case GST_EVENT_NEWSEGMENT:
641 /* We don't support NEWSEGMENT events */
643 gst_event_unref (event);
649 /* now GstCollectPads can take care of the rest, e.g. EOS */
651 ret = mux->collect_event (pad, event);
652 gst_object_unref (mux);
659 * gst_matroska_mux_video_pad_setcaps:
660 * @pad: Pad which got the caps.
663 * Setcaps function for video sink pad.
665 * Returns: #TRUE on success.
668 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
670 GstMatroskaTrackContext *context = NULL;
671 GstMatroskaTrackVideoContext *videocontext;
673 GstMatroskaPad *collect_pad;
674 GstStructure *structure;
675 const gchar *mimetype;
676 const GValue *value = NULL;
677 const GstBuffer *codec_buf = NULL;
678 gint width, height, pixel_width, pixel_height;
680 gboolean interlaced = FALSE;
682 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
685 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
686 g_assert (collect_pad);
687 context = collect_pad->track;
689 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
690 videocontext = (GstMatroskaTrackVideoContext *) context;
692 /* gst -> matroska ID'ing */
693 structure = gst_caps_get_structure (caps, 0);
695 mimetype = gst_structure_get_name (structure);
697 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
699 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
701 if (!strcmp (mimetype, "video/x-theora")) {
702 /* we'll extract the details later from the theora identification header */
706 /* get general properties */
707 /* spec says it is mandatory */
708 if (!gst_structure_get_int (structure, "width", &width) ||
709 !gst_structure_get_int (structure, "height", &height))
712 videocontext->pixel_width = width;
713 videocontext->pixel_height = height;
714 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
716 context->default_duration =
717 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
718 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
719 GST_TIME_ARGS (context->default_duration));
721 context->default_duration = 0;
723 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
724 &pixel_width, &pixel_height)) {
725 if (pixel_width > pixel_height) {
726 videocontext->display_width = width * pixel_width / pixel_height;
727 videocontext->display_height = height;
728 } else if (pixel_width < pixel_height) {
729 videocontext->display_width = width;
730 videocontext->display_height = height * pixel_height / pixel_width;
732 videocontext->display_width = 0;
733 videocontext->display_height = 0;
736 videocontext->display_width = 0;
737 videocontext->display_height = 0;
742 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
743 videocontext->fourcc = 0;
745 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
746 * data and other settings
750 /* extract codec_data, may turn out needed */
751 value = gst_structure_get_value (structure, "codec_data");
753 codec_buf = gst_value_get_buffer (value);
756 if (!strcmp (mimetype, "video/x-raw-yuv")) {
757 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
758 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
759 } else if (!strcmp (mimetype, "image/jpeg")) {
760 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
761 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
762 ||!strcmp (mimetype, "video/x-huffyuv")
763 || !strcmp (mimetype, "video/x-divx")
764 || !strcmp (mimetype, "video/x-dv")
765 || !strcmp (mimetype, "video/x-h263")
766 || !strcmp (mimetype, "video/x-msmpeg")
767 || !strcmp (mimetype, "video/x-wmv")) {
768 gst_riff_strf_vids *bih;
769 gint size = sizeof (gst_riff_strf_vids);
772 if (!strcmp (mimetype, "video/x-xvid"))
773 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
774 else if (!strcmp (mimetype, "video/x-huffyuv"))
775 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
776 else if (!strcmp (mimetype, "video/x-dv"))
777 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
778 else if (!strcmp (mimetype, "video/x-h263"))
779 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
780 else if (!strcmp (mimetype, "video/x-divx")) {
783 gst_structure_get_int (structure, "divxversion", &divxversion);
784 switch (divxversion) {
786 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
789 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
792 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
795 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
798 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
799 switch (msmpegversion) {
801 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
804 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
810 } else if (!strcmp (mimetype, "video/x-wmv")) {
813 if (gst_structure_get_fourcc (structure, "format", &format)) {
815 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
816 if (wmvversion == 2) {
817 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
818 } else if (wmvversion == 1) {
819 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
820 } else if (wmvversion == 3) {
821 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
829 bih = g_new0 (gst_riff_strf_vids, 1);
830 GST_WRITE_UINT32_LE (&bih->size, size);
831 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
832 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
833 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
834 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
835 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
836 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
837 videocontext->pixel_height * 3);
839 /* process codec private/initialization data, if any */
841 size += GST_BUFFER_SIZE (codec_buf);
842 bih = g_realloc (bih, size);
843 GST_WRITE_UINT32_LE (&bih->size, size);
844 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
845 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
848 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
849 context->codec_priv = (gpointer) bih;
850 context->codec_priv_size = size;
851 } else if (!strcmp (mimetype, "video/x-h264")) {
852 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
854 if (context->codec_priv != NULL) {
855 g_free (context->codec_priv);
856 context->codec_priv = NULL;
857 context->codec_priv_size = 0;
860 /* Create avcC header */
861 if (codec_buf != NULL) {
862 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
863 context->codec_priv = g_malloc0 (context->codec_priv_size);
864 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
865 context->codec_priv_size);
867 } else if (!strcmp (mimetype, "video/x-theora")) {
868 const GValue *streamheader;
870 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
872 if (context->codec_priv != NULL) {
873 g_free (context->codec_priv);
874 context->codec_priv = NULL;
875 context->codec_priv_size = 0;
878 streamheader = gst_structure_get_value (structure, "streamheader");
879 if (!theora_streamheader_to_codecdata (streamheader, context)) {
880 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
881 ("theora stream headers missing or malformed"));
884 } else if (!strcmp (mimetype, "video/x-dirac")) {
885 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
886 } else if (!strcmp (mimetype, "video/x-vp8")) {
887 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
888 } else if (!strcmp (mimetype, "video/mpeg")) {
891 gst_structure_get_int (structure, "mpegversion", &mpegversion);
892 switch (mpegversion) {
894 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
897 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
900 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
906 /* global headers may be in codec data */
907 if (codec_buf != NULL) {
908 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
909 context->codec_priv = g_malloc0 (context->codec_priv_size);
910 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
911 context->codec_priv_size);
913 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
915 /* can only make it here if preceding case verified it was version 3 */
916 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
917 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
919 const GValue *mdpr_data;
921 gst_structure_get_int (structure, "rmversion", &rmversion);
924 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
927 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
930 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
933 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
939 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
940 if (mdpr_data != NULL) {
941 guint8 *priv_data = NULL;
942 guint priv_data_size = 0;
944 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
946 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
947 priv_data = g_malloc0 (priv_data_size);
949 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
951 context->codec_priv = priv_data;
952 context->codec_priv_size = priv_data_size;
961 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
962 GST_PAD_NAME (pad), caps);
967 /* N > 0 to expect a particular number of headers, negative if the
968 number of headers is variable */
970 xiphN_streamheader_to_codecdata (const GValue * streamheader,
971 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
973 GstBuffer **buf = NULL;
976 guint bufi, i, offset, priv_data_size;
978 if (streamheader == NULL)
979 goto no_stream_headers;
981 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
984 bufarr = g_value_peek_pointer (streamheader);
985 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
987 if (N > 0 && bufarr->len != N)
990 context->xiph_headers_to_skip = bufarr->len;
992 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
993 for (i = 0; i < bufarr->len; i++) {
994 GValue *bufval = &g_array_index (bufarr, GValue, i);
996 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
998 goto wrong_content_type;
1001 buf[i] = g_value_peek_pointer (bufval);
1005 if (bufarr->len > 0) {
1006 for (i = 0; i < bufarr->len - 1; i++) {
1007 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1011 for (i = 0; i < bufarr->len; ++i) {
1012 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1015 priv_data = g_malloc0 (priv_data_size);
1017 priv_data[0] = bufarr->len - 1;
1020 if (bufarr->len > 0) {
1021 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1022 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1023 priv_data[offset++] = 0xff;
1025 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1029 for (i = 0; i < bufarr->len; ++i) {
1030 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1031 GST_BUFFER_SIZE (buf[i]));
1032 offset += GST_BUFFER_SIZE (buf[i]);
1035 context->codec_priv = priv_data;
1036 context->codec_priv_size = priv_data_size;
1039 *p_buf0 = gst_buffer_ref (buf[0]);
1048 GST_WARNING ("required streamheaders missing in sink caps!");
1053 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1054 G_VALUE_TYPE_NAME (streamheader));
1059 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1064 GST_WARNING ("streamheaders array does not contain GstBuffers");
1070 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1071 GstMatroskaTrackContext * context)
1073 GstBuffer *buf0 = NULL;
1075 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1078 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1079 GST_WARNING ("First vorbis header too small, ignoring");
1081 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1082 GstMatroskaTrackAudioContext *audiocontext;
1085 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1086 audiocontext = (GstMatroskaTrackAudioContext *) context;
1087 audiocontext->channels = GST_READ_UINT8 (hdr);
1088 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1093 gst_buffer_unref (buf0);
1099 theora_streamheader_to_codecdata (const GValue * streamheader,
1100 GstMatroskaTrackContext * context)
1102 GstBuffer *buf0 = NULL;
1104 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1107 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1108 GST_WARNING ("First theora header too small, ignoring");
1109 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1110 GST_WARNING ("First header not a theora identification header, ignoring");
1112 GstMatroskaTrackVideoContext *videocontext;
1113 guint fps_num, fps_denom, par_num, par_denom;
1116 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1118 videocontext = (GstMatroskaTrackVideoContext *) context;
1119 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1120 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1121 hdr += 3 + 3 + 1 + 1;
1122 fps_num = GST_READ_UINT32_BE (hdr);
1123 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1124 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1125 fps_denom, fps_num);
1127 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1128 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1129 if (par_num > 0 && par_num > 0) {
1130 if (par_num > par_denom) {
1131 videocontext->display_width =
1132 videocontext->pixel_width * par_num / par_denom;
1133 videocontext->display_height = videocontext->pixel_height;
1134 } else if (par_num < par_denom) {
1135 videocontext->display_width = videocontext->pixel_width;
1136 videocontext->display_height =
1137 videocontext->pixel_height * par_denom / par_num;
1139 videocontext->display_width = 0;
1140 videocontext->display_height = 0;
1143 videocontext->display_width = 0;
1144 videocontext->display_height = 0;
1150 gst_buffer_unref (buf0);
1156 kate_streamheader_to_codecdata (const GValue * streamheader,
1157 GstMatroskaTrackContext * context)
1159 GstBuffer *buf0 = NULL;
1161 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1164 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1165 GST_WARNING ("First kate header too small, ignoring");
1166 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1167 GST_WARNING ("First header not a kate identification header, ignoring");
1171 gst_buffer_unref (buf0);
1177 flac_streamheader_to_codecdata (const GValue * streamheader,
1178 GstMatroskaTrackContext * context)
1185 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1186 GST_WARNING ("No or invalid streamheader field in the caps");
1190 bufarr = g_value_peek_pointer (streamheader);
1191 if (bufarr->len < 2) {
1192 GST_WARNING ("Too few headers in streamheader field");
1196 context->xiph_headers_to_skip = bufarr->len + 1;
1198 bufval = &g_array_index (bufarr, GValue, 0);
1199 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1200 GST_WARNING ("streamheaders array does not contain GstBuffers");
1204 buffer = g_value_peek_pointer (bufval);
1206 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1207 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1208 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1209 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1210 GST_WARNING ("Invalid streamheader for FLAC");
1214 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1215 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1216 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1217 GST_BUFFER_SIZE (buffer) - 9);
1219 for (i = 1; i < bufarr->len; i++) {
1220 bufval = &g_array_index (bufarr, GValue, i);
1222 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1223 g_free (context->codec_priv);
1224 context->codec_priv = NULL;
1225 context->codec_priv_size = 0;
1226 GST_WARNING ("streamheaders array does not contain GstBuffers");
1230 buffer = g_value_peek_pointer (bufval);
1232 context->codec_priv =
1233 g_realloc (context->codec_priv,
1234 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1235 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1236 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1237 context->codec_priv_size =
1238 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1245 speex_streamheader_to_codecdata (const GValue * streamheader,
1246 GstMatroskaTrackContext * context)
1252 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1253 GST_WARNING ("No or invalid streamheader field in the caps");
1257 bufarr = g_value_peek_pointer (streamheader);
1258 if (bufarr->len != 2) {
1259 GST_WARNING ("Too few headers in streamheader field");
1263 context->xiph_headers_to_skip = bufarr->len + 1;
1265 bufval = &g_array_index (bufarr, GValue, 0);
1266 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1267 GST_WARNING ("streamheaders array does not contain GstBuffers");
1271 buffer = g_value_peek_pointer (bufval);
1273 if (GST_BUFFER_SIZE (buffer) < 80
1274 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1275 GST_WARNING ("Invalid streamheader for Speex");
1279 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1280 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1281 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1282 GST_BUFFER_SIZE (buffer));
1284 bufval = &g_array_index (bufarr, GValue, 1);
1286 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1287 g_free (context->codec_priv);
1288 context->codec_priv = NULL;
1289 context->codec_priv_size = 0;
1290 GST_WARNING ("streamheaders array does not contain GstBuffers");
1294 buffer = g_value_peek_pointer (bufval);
1296 context->codec_priv =
1297 g_realloc (context->codec_priv,
1298 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1299 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1300 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1301 context->codec_priv_size =
1302 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1307 static const gchar *
1308 aac_codec_data_to_codec_id (const GstBuffer * buf)
1310 const gchar *result;
1313 /* default to MAIN */
1316 if (GST_BUFFER_SIZE (buf) >= 2) {
1317 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1335 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1344 * gst_matroska_mux_audio_pad_setcaps:
1345 * @pad: Pad which got the caps.
1348 * Setcaps function for audio sink pad.
1350 * Returns: #TRUE on success.
1353 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1355 GstMatroskaTrackContext *context = NULL;
1356 GstMatroskaTrackAudioContext *audiocontext;
1357 GstMatroskaMux *mux;
1358 GstMatroskaPad *collect_pad;
1359 const gchar *mimetype;
1360 gint samplerate = 0, channels = 0;
1361 GstStructure *structure;
1362 const GValue *codec_data = NULL;
1363 const GstBuffer *buf = NULL;
1364 const gchar *stream_format = NULL;
1366 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1369 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1370 g_assert (collect_pad);
1371 context = collect_pad->track;
1373 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1374 audiocontext = (GstMatroskaTrackAudioContext *) context;
1376 structure = gst_caps_get_structure (caps, 0);
1377 mimetype = gst_structure_get_name (structure);
1380 gst_structure_get_int (structure, "rate", &samplerate);
1381 gst_structure_get_int (structure, "channels", &channels);
1383 audiocontext->samplerate = samplerate;
1384 audiocontext->channels = channels;
1385 audiocontext->bitdepth = 0;
1386 context->default_duration = 0;
1388 codec_data = gst_structure_get_value (structure, "codec_data");
1390 buf = gst_value_get_buffer (codec_data);
1392 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1393 * data and other settings
1397 if (!strcmp (mimetype, "audio/mpeg")) {
1398 gint mpegversion = 0;
1400 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1401 switch (mpegversion) {
1407 gst_structure_get_int (structure, "layer", &layer);
1409 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1410 GST_WARNING_OBJECT (mux,
1411 "Unable to determine MPEG audio version, assuming 1");
1417 else if (layer == 2)
1419 else if (version == 2)
1424 context->default_duration =
1425 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1429 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1432 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1435 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1444 stream_format = gst_structure_get_string (structure, "stream-format");
1445 /* check this is raw aac */
1446 if (stream_format) {
1447 if (strcmp (stream_format, "raw") != 0) {
1448 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1452 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1457 if (mpegversion == 2)
1459 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1460 aac_codec_data_to_codec_id (buf));
1461 else if (mpegversion == 4)
1463 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1464 aac_codec_data_to_codec_id (buf));
1466 g_assert_not_reached ();
1468 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1475 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1477 gint endianness = G_LITTLE_ENDIAN;
1478 gboolean signedness = TRUE;
1480 if (!gst_structure_get_int (structure, "width", &width) ||
1481 !gst_structure_get_int (structure, "depth", &depth) ||
1482 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1483 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1488 !gst_structure_get_int (structure, "endianness", &endianness)) {
1489 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1493 if (width != depth) {
1494 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1498 /* FIXME: where is this spec'ed out? (tpm) */
1499 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1500 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1504 audiocontext->bitdepth = depth;
1505 if (endianness == G_BIG_ENDIAN)
1506 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1508 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1510 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1513 if (!gst_structure_get_int (structure, "width", &width)) {
1514 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1518 audiocontext->bitdepth = width;
1519 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1521 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1522 const GValue *streamheader;
1524 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1526 if (context->codec_priv != NULL) {
1527 g_free (context->codec_priv);
1528 context->codec_priv = NULL;
1529 context->codec_priv_size = 0;
1532 streamheader = gst_structure_get_value (structure, "streamheader");
1533 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1534 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1535 ("vorbis stream headers missing or malformed"));
1538 } else if (!strcmp (mimetype, "audio/x-flac")) {
1539 const GValue *streamheader;
1541 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1542 if (context->codec_priv != NULL) {
1543 g_free (context->codec_priv);
1544 context->codec_priv = NULL;
1545 context->codec_priv_size = 0;
1548 streamheader = gst_structure_get_value (structure, "streamheader");
1549 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1550 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1551 ("flac stream headers missing or malformed"));
1554 } else if (!strcmp (mimetype, "audio/x-speex")) {
1555 const GValue *streamheader;
1557 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1558 if (context->codec_priv != NULL) {
1559 g_free (context->codec_priv);
1560 context->codec_priv = NULL;
1561 context->codec_priv_size = 0;
1564 streamheader = gst_structure_get_value (structure, "streamheader");
1565 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1566 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1567 ("speex stream headers missing or malformed"));
1570 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1571 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1572 } else if (!strcmp (mimetype, "audio/x-tta")) {
1575 /* TTA frame duration */
1576 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1578 gst_structure_get_int (structure, "width", &width);
1579 audiocontext->bitdepth = width;
1580 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1582 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1584 const GValue *mdpr_data;
1586 gst_structure_get_int (structure, "raversion", &raversion);
1587 switch (raversion) {
1589 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1592 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1595 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1601 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1602 if (mdpr_data != NULL) {
1603 guint8 *priv_data = NULL;
1604 guint priv_data_size = 0;
1606 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1608 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1609 priv_data = g_malloc0 (priv_data_size);
1611 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1613 context->codec_priv = priv_data;
1614 context->codec_priv_size = priv_data_size;
1617 } else if (!strcmp (mimetype, "audio/x-wma")) {
1619 guint codec_priv_size;
1626 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1627 || !gst_structure_get_int (structure, "block_align", &block_align)
1628 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1629 || samplerate == 0 || channels == 0) {
1630 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1631 "channels/rate on WMA caps");
1635 switch (wmaversion) {
1637 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1640 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1643 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1646 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1650 if (gst_structure_get_int (structure, "depth", &depth))
1651 audiocontext->bitdepth = depth;
1653 codec_priv_size = WAVEFORMATEX_SIZE;
1655 codec_priv_size += GST_BUFFER_SIZE (buf);
1657 /* serialize waveformatex structure */
1658 codec_priv = g_malloc0 (codec_priv_size);
1659 GST_WRITE_UINT16_LE (codec_priv, format);
1660 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1661 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1662 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1663 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1664 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1666 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1668 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1670 /* process codec private/initialization data, if any */
1672 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1673 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1676 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1677 context->codec_priv = (gpointer) codec_priv;
1678 context->codec_priv_size = codec_priv_size;
1686 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1687 GST_PAD_NAME (pad), caps);
1694 * gst_matroska_mux_subtitle_pad_setcaps:
1695 * @pad: Pad which got the caps.
1698 * Setcaps function for subtitle sink pad.
1700 * Returns: #TRUE on success.
1703 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1706 * Consider this as boilerplate code for now. There is
1707 * no single subtitle creation element in GStreamer,
1708 * neither do I know how subtitling works at all. */
1710 /* There is now (at least) one such alement (kateenc), and I'm going
1711 to handle it here and claim it works when it can be piped back
1712 through GStreamer and VLC */
1714 GstMatroskaTrackContext *context = NULL;
1715 GstMatroskaTrackSubtitleContext *scontext;
1716 GstMatroskaMux *mux;
1717 GstMatroskaPad *collect_pad;
1718 const gchar *mimetype;
1719 GstStructure *structure;
1721 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1724 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1725 g_assert (collect_pad);
1726 context = collect_pad->track;
1728 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1729 scontext = (GstMatroskaTrackSubtitleContext *) context;
1731 structure = gst_caps_get_structure (caps, 0);
1732 mimetype = gst_structure_get_name (structure);
1735 scontext->check_utf8 = 1;
1736 scontext->invalid_utf8 = 0;
1737 context->default_duration = 0;
1739 /* TODO: - other format than Kate */
1741 if (!strcmp (mimetype, "subtitle/x-kate")) {
1742 const GValue *streamheader;
1744 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1746 if (context->codec_priv != NULL) {
1747 g_free (context->codec_priv);
1748 context->codec_priv = NULL;
1749 context->codec_priv_size = 0;
1752 streamheader = gst_structure_get_value (structure, "streamheader");
1753 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1754 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1755 ("kate stream headers missing or malformed"));
1766 * gst_matroska_mux_request_new_pad:
1767 * @element: #GstMatroskaMux.
1768 * @templ: #GstPadTemplate.
1769 * @pad_name: New pad name.
1771 * Request pad function for sink templates.
1773 * Returns: New #GstPad.
1776 gst_matroska_mux_request_new_pad (GstElement * element,
1777 GstPadTemplate * templ, const gchar * pad_name)
1779 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1780 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1781 GstMatroskaPad *collect_pad;
1782 GstPad *newpad = NULL;
1784 GstPadSetCapsFunction setcapsfunc = NULL;
1785 GstMatroskaTrackContext *context = NULL;
1787 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1788 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1789 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1790 context = (GstMatroskaTrackContext *)
1791 g_new0 (GstMatroskaTrackAudioContext, 1);
1792 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1793 context->name = g_strdup ("Audio");
1794 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1795 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1796 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1797 context = (GstMatroskaTrackContext *)
1798 g_new0 (GstMatroskaTrackVideoContext, 1);
1799 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1800 context->name = g_strdup ("Video");
1801 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1802 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1803 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1804 context = (GstMatroskaTrackContext *)
1805 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1806 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1807 context->name = g_strdup ("Subtitle");
1809 GST_WARNING_OBJECT (mux, "This is not our template!");
1813 newpad = gst_pad_new_from_template (templ, name);
1815 collect_pad = (GstMatroskaPad *)
1816 gst_collect_pads_add_pad_full (mux->collect, newpad,
1817 sizeof (GstMatroskaPad),
1818 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1820 collect_pad->track = context;
1821 gst_matroska_pad_reset (collect_pad, FALSE);
1823 /* FIXME: hacked way to override/extend the event function of
1824 * GstCollectPads; because it sets its own event function giving the
1825 * element no access to events.
1826 * TODO GstCollectPads should really give its 'users' a clean chance to
1827 * properly handle events that are not meant for collectpads itself.
1828 * Perhaps a callback or so, though rejected (?) in #340060.
1829 * This would allow (clean) transcoding of info from demuxer/streams
1830 * to another muxer */
1831 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1832 gst_pad_set_event_function (newpad,
1833 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1835 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1836 gst_pad_set_active (newpad, TRUE);
1837 gst_element_add_pad (element, newpad);
1844 * gst_matroska_mux_release_pad:
1845 * @element: #GstMatroskaMux.
1846 * @pad: Pad to release.
1848 * Release a previously requested pad.
1851 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1853 GstMatroskaMux *mux;
1856 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1858 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1859 GstCollectData *cdata = (GstCollectData *) walk->data;
1860 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1862 if (cdata->pad == pad) {
1863 GstClockTime min_dur; /* observed minimum duration */
1865 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1866 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1867 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1868 if (collect_pad->duration < min_dur)
1869 collect_pad->duration = min_dur;
1872 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1873 mux->duration < collect_pad->duration)
1874 mux->duration = collect_pad->duration;
1880 gst_collect_pads_remove_pad (mux->collect, pad);
1881 if (gst_element_remove_pad (element, pad))
1887 * gst_matroska_mux_track_header:
1888 * @mux: #GstMatroskaMux
1889 * @context: Tack context.
1891 * Write a track header.
1894 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1895 GstMatroskaTrackContext * context)
1897 GstEbmlWrite *ebml = mux->ebml_write;
1900 /* TODO: check if everything necessary is written and check default values */
1902 /* track type goes before the type-specific stuff */
1903 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1904 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1906 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1907 gst_matroska_mux_create_uid ());
1908 if (context->default_duration) {
1909 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1910 context->default_duration);
1912 if (context->language) {
1913 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1917 /* type-specific stuff */
1918 switch (context->type) {
1919 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1920 GstMatroskaTrackVideoContext *videocontext =
1921 (GstMatroskaTrackVideoContext *) context;
1923 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1924 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1925 videocontext->pixel_width);
1926 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1927 videocontext->pixel_height);
1928 if (videocontext->display_width && videocontext->display_height) {
1929 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1930 videocontext->display_width);
1931 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1932 videocontext->display_height);
1934 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1935 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1936 if (videocontext->fourcc) {
1937 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1939 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1940 (gpointer) & fcc_le, 4);
1942 gst_ebml_write_master_finish (ebml, master);
1947 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1948 GstMatroskaTrackAudioContext *audiocontext =
1949 (GstMatroskaTrackAudioContext *) context;
1951 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1952 if (audiocontext->samplerate != 8000)
1953 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1954 audiocontext->samplerate);
1955 if (audiocontext->channels != 1)
1956 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1957 audiocontext->channels);
1958 if (audiocontext->bitdepth) {
1959 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1960 audiocontext->bitdepth);
1962 gst_ebml_write_master_finish (ebml, master);
1968 /* doesn't need type-specific data */
1972 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1973 if (context->codec_priv)
1974 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1975 context->codec_priv, context->codec_priv_size);
1976 /* FIXME: until we have a nice way of getting the codecname
1977 * out of the caps, I'm not going to enable this. Too much
1978 * (useless, double, boring) work... */
1979 /* TODO: Use value from tags if any */
1980 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1981 context->codec_name); */
1982 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1987 * gst_matroska_mux_start:
1988 * @mux: #GstMatroskaMux
1990 * Start a new matroska file (write headers etc...)
1993 gst_matroska_mux_start (GstMatroskaMux * mux)
1995 GstEbmlWrite *ebml = mux->ebml_write;
1996 const gchar *doctype;
1997 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1998 GST_MATROSKA_ID_TRACKS,
1999 GST_MATROSKA_ID_CUES,
2000 GST_MATROSKA_ID_TAGS,
2003 guint64 master, child;
2007 GstClockTime duration = 0;
2008 guint32 segment_uid[4];
2009 GTimeVal time = { 0, 0 };
2011 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2012 ebml->caps = gst_caps_from_string ("video/webm");
2014 ebml->caps = gst_caps_from_string ("video/x-matroska");
2016 /* we start with a EBML header */
2017 doctype = mux->doctype;
2018 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2019 doctype, mux->doctype_version);
2020 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2022 /* the rest of the header is cached */
2023 gst_ebml_write_set_cache (ebml, 0x1000);
2025 /* start a segment */
2027 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2028 mux->segment_master = ebml->pos;
2030 if (!mux->streamable) {
2031 /* seekhead (table of contents) - we set the positions later */
2032 mux->seekhead_pos = ebml->pos;
2033 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2034 for (i = 0; seekhead_id[i] != 0; i++) {
2035 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2037 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2038 gst_ebml_write_master_finish (ebml, child);
2040 gst_ebml_write_master_finish (ebml, master);
2044 mux->info_pos = ebml->pos;
2045 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2046 for (i = 0; i < 4; i++) {
2047 segment_uid[i] = g_random_int ();
2049 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2050 (guint8 *) segment_uid, 16);
2051 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2052 mux->duration_pos = ebml->pos;
2054 if (!mux->streamable) {
2055 for (collected = mux->collect->data; collected;
2056 collected = g_slist_next (collected)) {
2057 GstMatroskaPad *collect_pad;
2058 GstFormat format = GST_FORMAT_TIME;
2060 gint64 trackduration;
2062 collect_pad = (GstMatroskaPad *) collected->data;
2063 thepad = collect_pad->collect.pad;
2065 /* Query the total length of the track. */
2066 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2067 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2068 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2069 GST_TIME_ARGS (trackduration));
2070 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2071 duration = (GstClockTime) trackduration;
2075 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2076 gst_guint64_to_gdouble (duration) /
2077 gst_guint64_to_gdouble (mux->time_scale));
2079 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2080 "GStreamer plugin version " PACKAGE_VERSION);
2081 if (mux->writing_app && mux->writing_app[0]) {
2082 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2084 g_get_current_time (&time);
2085 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2086 gst_ebml_write_master_finish (ebml, master);
2089 mux->tracks_pos = ebml->pos;
2090 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2092 for (collected = mux->collect->data; collected;
2093 collected = g_slist_next (collected)) {
2094 GstMatroskaPad *collect_pad;
2097 collect_pad = (GstMatroskaPad *) collected->data;
2098 thepad = collect_pad->collect.pad;
2100 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2101 collect_pad->track->codec_id != 0) {
2102 collect_pad->track->num = tracknum++;
2103 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2104 gst_matroska_mux_track_header (mux, collect_pad->track);
2105 gst_ebml_write_master_finish (ebml, child);
2108 gst_ebml_write_master_finish (ebml, master);
2110 /* lastly, flush the cache */
2111 gst_ebml_write_flush_cache (ebml, FALSE);
2115 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2118 /* TODO: more sensible tag mappings */
2121 const gchar *matroska_tagname;
2122 const gchar *gstreamer_tagname;
2126 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2127 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2128 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2129 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2130 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2131 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2132 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2133 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2134 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2135 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2136 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2137 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2138 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2139 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2140 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2142 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2144 guint64 simpletag_master;
2146 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2147 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2148 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2150 if (strcmp (tagname_gst, tag) == 0) {
2151 GValue src = { 0, };
2154 if (!gst_tag_list_copy_value (&src, list, tag))
2156 if ((dest = gst_value_serialize (&src))) {
2158 simpletag_master = gst_ebml_write_master_start (ebml,
2159 GST_MATROSKA_ID_SIMPLETAG);
2160 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2161 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2162 gst_ebml_write_master_finish (ebml, simpletag_master);
2165 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2167 g_value_unset (&src);
2175 * gst_matroska_mux_finish:
2176 * @mux: #GstMatroskaMux
2178 * Finish a new matroska file (write index etc...)
2181 gst_matroska_mux_finish (GstMatroskaMux * mux)
2183 GstEbmlWrite *ebml = mux->ebml_write;
2185 guint64 duration = 0;
2187 const GstTagList *tags;
2189 /* finish last cluster */
2191 gst_ebml_write_master_finish (ebml, mux->cluster);
2195 if (mux->index != NULL && !mux->streamable) {
2197 guint64 master, pointentry_master, trackpos_master;
2199 mux->cues_pos = ebml->pos;
2200 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2201 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2203 for (n = 0; n < mux->num_indexes; n++) {
2204 GstMatroskaIndex *idx = &mux->index[n];
2206 pointentry_master = gst_ebml_write_master_start (ebml,
2207 GST_MATROSKA_ID_POINTENTRY);
2208 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2209 idx->time / mux->time_scale);
2210 trackpos_master = gst_ebml_write_master_start (ebml,
2211 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2212 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2213 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2214 idx->pos - mux->segment_master);
2215 gst_ebml_write_master_finish (ebml, trackpos_master);
2216 gst_ebml_write_master_finish (ebml, pointentry_master);
2219 gst_ebml_write_master_finish (ebml, master);
2220 gst_ebml_write_flush_cache (ebml, FALSE);
2224 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2226 if (tags != NULL && !gst_tag_list_is_empty (tags) && !mux->streamable) {
2227 guint64 master_tags, master_tag;
2229 GST_DEBUG ("Writing tags");
2231 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2232 mux->tags_pos = ebml->pos;
2233 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2234 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2235 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2236 gst_ebml_write_master_finish (ebml, master_tag);
2237 gst_ebml_write_master_finish (ebml, master_tags);
2240 /* update seekhead. We know that:
2241 * - a seekhead contains 4 entries.
2242 * - order of entries is as above.
2243 * - a seekhead has a 4-byte header + 8-byte length
2244 * - each entry is 2-byte master, 2-byte ID pointer,
2245 * 2-byte length pointer, all 8/1-byte length, 4-
2246 * byte ID and 8-byte length pointer, where the
2247 * length pointer starts at 20.
2248 * - all entries are local to the segment (so pos - segment_master).
2249 * - so each entry is at 12 + 20 + num * 28. */
2250 if (!mux->streamable) {
2251 GST_DEBUG_OBJECT (mux, "not streamable");
2252 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2253 mux->info_pos - mux->segment_master);
2254 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2255 mux->tracks_pos - mux->segment_master);
2256 if (mux->index != NULL) {
2257 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2258 mux->cues_pos - mux->segment_master);
2261 guint64 my_pos = ebml->pos;
2263 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2264 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2265 gst_ebml_write_seek (ebml, my_pos);
2268 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2269 mux->tags_pos - mux->segment_master);
2272 guint64 my_pos = ebml->pos;
2274 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2275 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2276 gst_ebml_write_seek (ebml, my_pos);
2279 /* update duration */
2280 /* first get the overall duration */
2281 /* a released track may have left a duration in here */
2282 duration = mux->duration;
2283 for (collected = mux->collect->data; collected;
2284 collected = g_slist_next (collected)) {
2285 GstMatroskaPad *collect_pad;
2286 GstClockTime min_duration; /* observed minimum duration */
2288 collect_pad = (GstMatroskaPad *) collected->data;
2290 GST_DEBUG_OBJECT (mux,
2291 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2292 " end ts %" GST_TIME_FORMAT, collect_pad,
2293 GST_TIME_ARGS (collect_pad->start_ts),
2294 GST_TIME_ARGS (collect_pad->end_ts));
2296 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2297 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2299 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2300 if (collect_pad->duration < min_duration)
2301 collect_pad->duration = min_duration;
2302 GST_DEBUG_OBJECT (collect_pad,
2303 "final track duration: %" GST_TIME_FORMAT,
2304 GST_TIME_ARGS (collect_pad->duration));
2307 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2308 duration < collect_pad->duration)
2309 duration = collect_pad->duration;
2311 if (duration != 0) {
2312 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2313 GST_TIME_ARGS (duration));
2314 pos = mux->ebml_write->pos;
2315 gst_ebml_write_seek (ebml, mux->duration_pos);
2316 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2317 gst_guint64_to_gdouble (duration) /
2318 gst_guint64_to_gdouble (mux->time_scale));
2319 gst_ebml_write_seek (ebml, pos);
2322 guint64 my_pos = ebml->pos;
2324 gst_ebml_write_seek (ebml, mux->duration_pos);
2325 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2326 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);
2507 * gst_matroska_mux_write_data:
2508 * @mux: #GstMatroskaMux
2509 * @collect_pad: #GstMatroskaPad with the data
2511 * Write collected data (called from gst_matroska_mux_collected).
2513 * Returns: Result of the gst_pad_push issued to write the data.
2515 static GstFlowReturn
2516 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2518 GstEbmlWrite *ebml = mux->ebml_write;
2519 GstBuffer *buf, *hdr;
2521 gboolean write_duration;
2522 gint16 relative_timestamp;
2523 gint64 relative_timestamp64;
2524 guint64 block_duration;
2525 gboolean is_video_keyframe = FALSE;
2528 buf = collect_pad->buffer;
2529 collect_pad->buffer = NULL;
2531 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2532 if (collect_pad->track->xiph_headers_to_skip > 0) {
2533 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2534 gst_buffer_unref (buf);
2535 --collect_pad->track->xiph_headers_to_skip;
2539 /* for dirac we have to queue up everything up to a picture unit */
2540 if (collect_pad->track->codec_id != NULL &&
2541 strcmp (collect_pad->track->codec_id,
2542 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2543 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2548 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2549 * this would wreak havoc with time stored in matroska file */
2550 /* TODO: maybe calculate a timestamp by using the previous timestamp
2551 * and default duration */
2552 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2553 GST_WARNING_OBJECT (collect_pad->collect.pad,
2554 "Invalid buffer timestamp; dropping buffer");
2555 gst_buffer_unref (buf);
2559 /* set the timestamp for outgoing buffers */
2560 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2562 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2563 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2564 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2565 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2566 is_video_keyframe = TRUE;
2570 /* start a new cluster at every keyframe or when we may be reaching the
2571 * limit of the relative timestamp */
2572 if (mux->cluster_time +
2573 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2574 || is_video_keyframe) {
2575 if (!mux->streamable)
2576 gst_ebml_write_master_finish (ebml, mux->cluster);
2577 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2578 mux->cluster_pos = ebml->pos;
2579 gst_ebml_write_set_cache (ebml, 0x20);
2581 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2582 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2583 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2585 GST_WARNING_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2586 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2588 gst_ebml_write_flush_cache (ebml, TRUE);
2589 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2590 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2591 mux->prev_cluster_size);
2596 mux->cluster_pos = ebml->pos;
2597 gst_ebml_write_set_cache (ebml, 0x20);
2598 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2599 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2600 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2601 gst_ebml_write_flush_cache (ebml, TRUE);
2602 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2605 /* update duration of this track */
2606 if (GST_BUFFER_DURATION_IS_VALID (buf))
2607 collect_pad->duration += GST_BUFFER_DURATION (buf);
2609 /* We currently write index entries for all video tracks or for the audio
2610 * track in a single-track audio file. This could be improved by keeping the
2611 * index only for the *first* video track. */
2613 /* TODO: index is useful for every track, should contain the number of
2614 * the block in the cluster which contains the timestamp, should also work
2615 * for files with multiple audio tracks.
2617 if (is_video_keyframe ||
2618 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2619 (mux->num_streams == 1))) {
2622 if (mux->min_index_interval != 0) {
2623 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2624 if (mux->index[last_idx].track == collect_pad->track->num)
2629 if (last_idx < 0 || mux->min_index_interval == 0 ||
2630 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2631 >= mux->min_index_interval)) {
2632 GstMatroskaIndex *idx;
2634 if (mux->num_indexes % 32 == 0) {
2635 mux->index = g_renew (GstMatroskaIndex, mux->index,
2636 mux->num_indexes + 32);
2638 idx = &mux->index[mux->num_indexes++];
2640 idx->pos = mux->cluster_pos;
2641 idx->time = GST_BUFFER_TIMESTAMP (buf);
2642 idx->track = collect_pad->track->num;
2646 /* Check if the duration differs from the default duration. */
2647 write_duration = FALSE;
2648 block_duration = GST_BUFFER_DURATION (buf);
2649 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2650 if (block_duration != collect_pad->track->default_duration) {
2651 write_duration = TRUE;
2655 /* write the block, for doctype v2 use SimpleBlock if possible
2656 * one slice (*breath*).
2657 * FIXME: Need to do correct lacing! */
2658 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2659 if (relative_timestamp64 >= 0) {
2660 /* round the timestamp */
2661 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2663 /* round the timestamp */
2664 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2666 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2668 if (mux->doctype_version > 1 && !write_duration) {
2670 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2673 gst_matroska_mux_create_buffer_header (collect_pad->track,
2674 relative_timestamp, flags);
2675 gst_ebml_write_set_cache (ebml, 0x40);
2676 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2677 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2678 gst_ebml_write_buffer (ebml, hdr);
2679 gst_ebml_write_flush_cache (ebml, FALSE);
2680 gst_ebml_write_buffer (ebml, buf);
2682 return gst_ebml_last_write_result (ebml);
2684 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2685 /* write and call order slightly unnatural,
2686 * but avoids seek and minizes pushing */
2687 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2689 gst_matroska_mux_create_buffer_header (collect_pad->track,
2690 relative_timestamp, 0);
2691 if (write_duration) {
2692 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2693 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2695 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2696 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2697 gst_ebml_write_buffer (ebml, hdr);
2698 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2699 gst_ebml_write_flush_cache (ebml, FALSE);
2700 gst_ebml_write_buffer (ebml, buf);
2701 return gst_ebml_last_write_result (ebml);
2707 * gst_matroska_mux_collected:
2708 * @pads: #GstCollectPads
2709 * @uuser_data: #GstMatroskaMux
2711 * Collectpads callback.
2713 * Returns: #GstFlowReturn
2715 static GstFlowReturn
2716 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2718 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2719 GstEbmlWrite *ebml = mux->ebml_write;
2720 GstMatroskaPad *best;
2724 GST_DEBUG_OBJECT (mux, "Collected pads");
2726 /* start with a header */
2727 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2728 if (mux->collect->data == NULL) {
2729 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2730 ("No input streams configured"));
2731 return GST_FLOW_ERROR;
2733 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2734 gst_ebml_start_streamheader (ebml);
2735 gst_matroska_mux_start (mux);
2736 gst_matroska_mux_stop_streamheader (mux);
2737 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2741 /* which stream to write from? */
2742 best = gst_matroska_mux_best_pad (mux, &popped);
2744 /* if there is no best pad, we have reached EOS */
2746 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2747 gst_matroska_mux_finish (mux);
2748 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2749 ret = GST_FLOW_UNEXPECTED;
2752 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2753 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2754 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2755 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2757 /* make note of first and last encountered timestamps, so we can calculate
2758 * the actual duration later when we send an updated header on eos */
2759 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2760 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2761 GstClockTime end_ts = start_ts;
2763 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2764 end_ts += GST_BUFFER_DURATION (best->buffer);
2765 else if (best->track->default_duration)
2766 end_ts += best->track->default_duration;
2768 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2769 best->end_ts = end_ts;
2771 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2772 start_ts < best->start_ts))
2773 best->start_ts = start_ts;
2776 /* write one buffer */
2777 ret = gst_matroska_mux_write_data (mux, best);
2778 } while (ret == GST_FLOW_OK && !popped);
2785 * gst_matroska_mux_change_state:
2786 * @element: #GstMatroskaMux
2787 * @transition: State change transition.
2789 * Change the muxer state.
2791 * Returns: #GstStateChangeReturn
2793 static GstStateChangeReturn
2794 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2796 GstStateChangeReturn ret;
2797 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2799 switch (transition) {
2800 case GST_STATE_CHANGE_NULL_TO_READY:
2802 case GST_STATE_CHANGE_READY_TO_PAUSED:
2803 gst_collect_pads_start (mux->collect);
2805 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2807 case GST_STATE_CHANGE_PAUSED_TO_READY:
2808 gst_collect_pads_stop (mux->collect);
2814 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2816 switch (transition) {
2817 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2819 case GST_STATE_CHANGE_PAUSED_TO_READY:
2820 gst_matroska_mux_reset (GST_ELEMENT (mux));
2822 case GST_STATE_CHANGE_READY_TO_NULL:
2832 gst_matroska_mux_set_property (GObject * object,
2833 guint prop_id, const GValue * value, GParamSpec * pspec)
2835 GstMatroskaMux *mux;
2837 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2838 mux = GST_MATROSKA_MUX (object);
2841 case ARG_WRITING_APP:
2842 if (!g_value_get_string (value)) {
2843 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2846 g_free (mux->writing_app);
2847 mux->writing_app = g_value_dup_string (value);
2849 case ARG_DOCTYPE_VERSION:
2850 mux->doctype_version = g_value_get_int (value);
2852 case ARG_MIN_INDEX_INTERVAL:
2853 mux->min_index_interval = g_value_get_int64 (value);
2855 case ARG_STREAMABLE:
2856 mux->streamable = g_value_get_boolean (value);
2859 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2865 gst_matroska_mux_get_property (GObject * object,
2866 guint prop_id, GValue * value, GParamSpec * pspec)
2868 GstMatroskaMux *mux;
2870 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2871 mux = GST_MATROSKA_MUX (object);
2874 case ARG_WRITING_APP:
2875 g_value_set_string (value, mux->writing_app);
2877 case ARG_DOCTYPE_VERSION:
2878 g_value_set_int (value, mux->doctype_version);
2880 case ARG_MIN_INDEX_INTERVAL:
2881 g_value_set_int64 (value, mux->min_index_interval);
2883 case ARG_STREAMABLE:
2884 g_value_set_boolean (value, mux->streamable);
2887 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);