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 "; "
162 COMMON_AUDIO_CAPS "; "
164 COMMON_AUDIO_CAPS "; "
168 "signed = (boolean) false, "
169 COMMON_AUDIO_CAPS ";"
173 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
174 "signed = (boolean) true, "
175 COMMON_AUDIO_CAPS ";"
179 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
180 "signed = (boolean) true, "
181 COMMON_AUDIO_CAPS ";"
185 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
186 "signed = (boolean) true, "
187 COMMON_AUDIO_CAPS ";"
188 "audio/x-raw-float, "
189 "width = (int) [ 32, 64 ], "
190 "endianness = (int) LITTLE_ENDIAN, "
191 COMMON_AUDIO_CAPS ";"
193 "width = (int) { 8, 16, 24 }, "
194 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
195 "audio/x-pn-realaudio, "
196 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
197 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
198 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
202 static GstStaticPadTemplate subtitlesink_templ =
203 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
206 GST_STATIC_CAPS ("subtitle/x-kate"));
208 static GArray *used_uids;
209 G_LOCK_DEFINE_STATIC (used_uids);
211 static void gst_matroska_mux_add_interfaces (GType type);
213 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
214 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
216 /* Matroska muxer destructor */
217 static void gst_matroska_mux_finalize (GObject * object);
219 /* Pads collected callback */
221 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
224 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
226 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
227 GstPadTemplate * templ, const gchar * name);
228 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
230 /* gst internal change state handler */
231 static GstStateChangeReturn
232 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
234 /* gobject bla bla */
235 static void gst_matroska_mux_set_property (GObject * object,
236 guint prop_id, const GValue * value, GParamSpec * pspec);
237 static void gst_matroska_mux_get_property (GObject * object,
238 guint prop_id, GValue * value, GParamSpec * pspec);
241 static void gst_matroska_mux_reset (GstElement * element);
244 static guint64 gst_matroska_mux_create_uid ();
246 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
247 GstMatroskaTrackContext * context);
248 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
249 GstMatroskaTrackContext * context);
250 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
251 GstMatroskaTrackContext * context);
252 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
253 GstMatroskaTrackContext * context);
254 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
255 GstMatroskaTrackContext * context);
258 gst_matroska_mux_add_interfaces (GType type)
260 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
262 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
266 gst_matroska_mux_base_init (gpointer g_class)
271 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
273 GObjectClass *gobject_class;
274 GstElementClass *gstelement_class;
276 gobject_class = (GObjectClass *) klass;
277 gstelement_class = (GstElementClass *) klass;
279 gst_element_class_add_pad_template (gstelement_class,
280 gst_static_pad_template_get (&videosink_templ));
281 gst_element_class_add_pad_template (gstelement_class,
282 gst_static_pad_template_get (&audiosink_templ));
283 gst_element_class_add_pad_template (gstelement_class,
284 gst_static_pad_template_get (&subtitlesink_templ));
285 gst_element_class_add_pad_template (gstelement_class,
286 gst_static_pad_template_get (&src_templ));
287 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
289 "Muxes video/audio/subtitle streams into a matroska stream",
290 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
292 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
295 gobject_class->finalize = gst_matroska_mux_finalize;
297 gobject_class->get_property = gst_matroska_mux_get_property;
298 gobject_class->set_property = gst_matroska_mux_set_property;
300 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
301 g_param_spec_string ("writing-app", "Writing application.",
302 "The name the application that creates the matroska file.",
303 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
304 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
305 g_param_spec_int ("version", "DocType version",
306 "This parameter determines what Matroska features can be used.",
307 1, 2, DEFAULT_DOCTYPE_VERSION,
308 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
309 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
310 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
311 "entries", "An index entry is created every so many nanoseconds.",
312 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
313 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314 g_object_class_install_property (gobject_class, ARG_STREAMABLE,
315 g_param_spec_boolean ("streamable", "Determines whether output should "
316 "be streamable", "If set to true, the output should be as if it is "
317 "to be streamed and hence no indexes written or duration written.",
319 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
321 gstelement_class->change_state =
322 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
323 gstelement_class->request_new_pad =
324 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
325 gstelement_class->release_pad =
326 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
331 * gst_matroska_mux_init:
332 * @mux: #GstMatroskaMux that should be initialized.
333 * @g_class: Class of the muxer.
335 * Matroska muxer constructor.
338 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
340 GstPadTemplate *templ;
343 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
344 mux->srcpad = gst_pad_new_from_template (templ, "src");
346 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
347 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
349 mux->collect = gst_collect_pads_new ();
350 gst_collect_pads_set_function (mux->collect,
351 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
354 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
355 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
357 /* property defaults */
358 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
359 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
360 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
361 mux->streamable = DEFAULT_STREAMABLE;
363 /* initialize internal variables */
365 mux->num_streams = 0;
366 mux->num_a_streams = 0;
367 mux->num_t_streams = 0;
368 mux->num_v_streams = 0;
370 /* initialize remaining variables */
371 gst_matroska_mux_reset (GST_ELEMENT (mux));
376 * gst_matroska_mux_finalize:
377 * @object: #GstMatroskaMux that should be finalized.
379 * Finalize matroska muxer.
382 gst_matroska_mux_finalize (GObject * object)
384 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
386 gst_object_unref (mux->collect);
387 gst_object_unref (mux->ebml_write);
388 if (mux->writing_app)
389 g_free (mux->writing_app);
391 G_OBJECT_CLASS (parent_class)->finalize (object);
396 * gst_matroska_mux_create_uid:
398 * Generate new unused track UID.
400 * Returns: New track UID.
403 gst_matroska_mux_create_uid (void)
410 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
415 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
416 for (i = 0; i < used_uids->len; i++) {
417 if (g_array_index (used_uids, guint64, i) == uid) {
422 g_array_append_val (used_uids, uid);
425 G_UNLOCK (used_uids);
431 * gst_matroska_pad_reset:
432 * @collect_pad: the #GstMatroskaPad
434 * Reset and/or release resources of a matroska collect pad.
437 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
440 GstMatroskaTrackType type = 0;
442 /* free track information */
443 if (collect_pad->track != NULL) {
444 /* retrieve for optional later use */
445 name = collect_pad->track->name;
446 type = collect_pad->track->type;
447 /* extra for video */
448 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
449 GstMatroskaTrackVideoContext *ctx =
450 (GstMatroskaTrackVideoContext *) collect_pad->track;
452 if (ctx->dirac_unit) {
453 gst_buffer_unref (ctx->dirac_unit);
454 ctx->dirac_unit = NULL;
457 g_free (collect_pad->track->codec_id);
458 g_free (collect_pad->track->codec_name);
460 g_free (collect_pad->track->name);
461 g_free (collect_pad->track->language);
462 g_free (collect_pad->track->codec_priv);
463 g_free (collect_pad->track);
464 collect_pad->track = NULL;
467 /* free cached buffer */
468 if (collect_pad->buffer != NULL) {
469 gst_buffer_unref (collect_pad->buffer);
470 collect_pad->buffer = NULL;
473 if (!full && type != 0) {
474 GstMatroskaTrackContext *context;
476 /* create a fresh context */
478 case GST_MATROSKA_TRACK_TYPE_VIDEO:
479 context = (GstMatroskaTrackContext *)
480 g_new0 (GstMatroskaTrackVideoContext, 1);
482 case GST_MATROSKA_TRACK_TYPE_AUDIO:
483 context = (GstMatroskaTrackContext *)
484 g_new0 (GstMatroskaTrackAudioContext, 1);
486 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
487 context = (GstMatroskaTrackContext *)
488 g_new0 (GstMatroskaTrackSubtitleContext, 1);
491 g_assert_not_reached ();
495 context->type = type;
496 context->name = name;
497 /* TODO: check default values for the context */
498 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
499 collect_pad->track = context;
500 collect_pad->buffer = NULL;
501 collect_pad->duration = 0;
502 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
503 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
508 * gst_matroska_pad_free:
509 * @collect_pad: the #GstMatroskaPad
511 * Release resources of a matroska collect pad.
514 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
516 gst_matroska_pad_reset (collect_pad, TRUE);
521 * gst_matroska_mux_reset:
522 * @element: #GstMatroskaMux that should be reseted.
524 * Reset matroska muxer back to initial state.
527 gst_matroska_mux_reset (GstElement * element)
529 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
532 /* reset EBML write */
533 gst_ebml_write_reset (mux->ebml_write);
536 mux->state = GST_MATROSKA_MUX_STATE_START;
538 /* clean up existing streams */
540 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
541 GstMatroskaPad *collect_pad;
543 collect_pad = (GstMatroskaPad *) walk->data;
545 /* reset collect pad to pristine state */
546 gst_matroska_pad_reset (collect_pad, FALSE);
550 mux->num_indexes = 0;
555 mux->time_scale = GST_MSECOND;
556 mux->max_cluster_duration = G_MAXINT16 * mux->time_scale;
561 mux->cluster_time = 0;
562 mux->cluster_pos = 0;
563 mux->prev_cluster_size = 0;
566 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
570 * gst_matroska_mux_handle_src_event:
571 * @pad: Pad which received the event.
572 * @event: Received event.
574 * handle events - copied from oggmux without understanding
576 * Returns: #TRUE on success.
579 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
583 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
587 /* disable seeking for now */
593 return gst_pad_event_default (pad, event);
597 * gst_matroska_mux_handle_sink_event:
598 * @pad: Pad which received the event.
599 * @event: Received event.
601 * handle events - informational ones like tags
603 * Returns: #TRUE on success.
606 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
608 GstMatroskaTrackContext *context;
609 GstMatroskaPad *collect_pad;
614 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
616 switch (GST_EVENT_TYPE (event)) {
620 GST_DEBUG_OBJECT (mux, "received tag event");
621 gst_event_parse_tag (event, &list);
623 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
624 g_assert (collect_pad);
625 context = collect_pad->track;
628 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
629 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
630 const gchar *lang_code;
632 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
634 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
635 context->language = g_strdup (lang_code);
637 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
642 /* FIXME: what about stream-specific tags? */
643 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
644 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
646 /* handled this, don't want collectpads to forward it downstream */
648 gst_event_unref (event);
651 case GST_EVENT_NEWSEGMENT:
652 /* We don't support NEWSEGMENT events */
654 gst_event_unref (event);
660 /* now GstCollectPads can take care of the rest, e.g. EOS */
662 ret = mux->collect_event (pad, event);
664 gst_object_unref (mux);
671 * gst_matroska_mux_video_pad_setcaps:
672 * @pad: Pad which got the caps.
675 * Setcaps function for video sink pad.
677 * Returns: #TRUE on success.
680 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
682 GstMatroskaTrackContext *context = NULL;
683 GstMatroskaTrackVideoContext *videocontext;
685 GstMatroskaPad *collect_pad;
686 GstStructure *structure;
687 const gchar *mimetype;
688 const GValue *value = NULL;
689 const GstBuffer *codec_buf = NULL;
690 gint width, height, pixel_width, pixel_height;
692 gboolean interlaced = FALSE;
694 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
697 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
698 g_assert (collect_pad);
699 context = collect_pad->track;
701 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
702 videocontext = (GstMatroskaTrackVideoContext *) context;
704 /* gst -> matroska ID'ing */
705 structure = gst_caps_get_structure (caps, 0);
707 mimetype = gst_structure_get_name (structure);
709 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
711 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
713 if (!strcmp (mimetype, "video/x-theora")) {
714 /* we'll extract the details later from the theora identification header */
718 /* get general properties */
719 /* spec says it is mandatory */
720 if (!gst_structure_get_int (structure, "width", &width) ||
721 !gst_structure_get_int (structure, "height", &height))
724 videocontext->pixel_width = width;
725 videocontext->pixel_height = height;
726 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
728 context->default_duration =
729 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
730 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
731 GST_TIME_ARGS (context->default_duration));
733 context->default_duration = 0;
735 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
736 &pixel_width, &pixel_height)) {
737 if (pixel_width > pixel_height) {
738 videocontext->display_width = width * pixel_width / pixel_height;
739 videocontext->display_height = height;
740 } else if (pixel_width < pixel_height) {
741 videocontext->display_width = width;
742 videocontext->display_height = height * pixel_height / pixel_width;
744 videocontext->display_width = 0;
745 videocontext->display_height = 0;
748 videocontext->display_width = 0;
749 videocontext->display_height = 0;
754 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
755 videocontext->fourcc = 0;
757 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
758 * data and other settings
762 /* extract codec_data, may turn out needed */
763 value = gst_structure_get_value (structure, "codec_data");
765 codec_buf = gst_value_get_buffer (value);
768 if (!strcmp (mimetype, "video/x-raw-yuv")) {
769 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
770 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
771 } else if (!strcmp (mimetype, "image/jpeg")) {
772 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
773 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
774 ||!strcmp (mimetype, "video/x-huffyuv")
775 || !strcmp (mimetype, "video/x-divx")
776 || !strcmp (mimetype, "video/x-dv")
777 || !strcmp (mimetype, "video/x-h263")
778 || !strcmp (mimetype, "video/x-msmpeg")
779 || !strcmp (mimetype, "video/x-wmv")) {
780 gst_riff_strf_vids *bih;
781 gint size = sizeof (gst_riff_strf_vids);
784 if (!strcmp (mimetype, "video/x-xvid"))
785 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
786 else if (!strcmp (mimetype, "video/x-huffyuv"))
787 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
788 else if (!strcmp (mimetype, "video/x-dv"))
789 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
790 else if (!strcmp (mimetype, "video/x-h263"))
791 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
792 else if (!strcmp (mimetype, "video/x-divx")) {
795 gst_structure_get_int (structure, "divxversion", &divxversion);
796 switch (divxversion) {
798 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
801 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
804 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
807 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
810 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
811 switch (msmpegversion) {
813 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
816 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
822 } else if (!strcmp (mimetype, "video/x-wmv")) {
825 if (gst_structure_get_fourcc (structure, "format", &format)) {
827 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
828 if (wmvversion == 2) {
829 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
830 } else if (wmvversion == 1) {
831 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
832 } else if (wmvversion == 3) {
833 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
841 bih = g_new0 (gst_riff_strf_vids, 1);
842 GST_WRITE_UINT32_LE (&bih->size, size);
843 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
844 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
845 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
846 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
847 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
848 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
849 videocontext->pixel_height * 3);
851 /* process codec private/initialization data, if any */
853 size += GST_BUFFER_SIZE (codec_buf);
854 bih = g_realloc (bih, size);
855 GST_WRITE_UINT32_LE (&bih->size, size);
856 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
857 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
860 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
861 context->codec_priv = (gpointer) bih;
862 context->codec_priv_size = size;
863 } else if (!strcmp (mimetype, "video/x-h264")) {
864 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
866 if (context->codec_priv != NULL) {
867 g_free (context->codec_priv);
868 context->codec_priv = NULL;
869 context->codec_priv_size = 0;
872 /* Create avcC header */
873 if (codec_buf != NULL) {
874 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
875 context->codec_priv = g_malloc0 (context->codec_priv_size);
876 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
877 context->codec_priv_size);
879 } else if (!strcmp (mimetype, "video/x-theora")) {
880 const GValue *streamheader;
882 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
884 if (context->codec_priv != NULL) {
885 g_free (context->codec_priv);
886 context->codec_priv = NULL;
887 context->codec_priv_size = 0;
890 streamheader = gst_structure_get_value (structure, "streamheader");
891 if (!theora_streamheader_to_codecdata (streamheader, context)) {
892 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
893 ("theora stream headers missing or malformed"));
896 } else if (!strcmp (mimetype, "video/x-dirac")) {
897 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
898 } else if (!strcmp (mimetype, "video/x-vp8")) {
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
900 } else if (!strcmp (mimetype, "video/mpeg")) {
903 gst_structure_get_int (structure, "mpegversion", &mpegversion);
904 switch (mpegversion) {
906 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
909 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
912 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
918 /* global headers may be in codec data */
919 if (codec_buf != NULL) {
920 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
921 context->codec_priv = g_malloc0 (context->codec_priv_size);
922 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
923 context->codec_priv_size);
925 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
927 /* can only make it here if preceding case verified it was version 3 */
928 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
929 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
931 const GValue *mdpr_data;
933 gst_structure_get_int (structure, "rmversion", &rmversion);
936 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
939 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
942 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
945 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
951 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
952 if (mdpr_data != NULL) {
953 guint8 *priv_data = NULL;
954 guint priv_data_size = 0;
956 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
958 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
959 priv_data = g_malloc0 (priv_data_size);
961 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
963 context->codec_priv = priv_data;
964 context->codec_priv_size = priv_data_size;
973 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
974 GST_PAD_NAME (pad), caps);
979 /* N > 0 to expect a particular number of headers, negative if the
980 number of headers is variable */
982 xiphN_streamheader_to_codecdata (const GValue * streamheader,
983 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
985 GstBuffer **buf = NULL;
988 guint bufi, i, offset, priv_data_size;
990 if (streamheader == NULL)
991 goto no_stream_headers;
993 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
996 bufarr = g_value_peek_pointer (streamheader);
997 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
999 if (N > 0 && bufarr->len != N)
1002 context->xiph_headers_to_skip = bufarr->len;
1004 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1005 for (i = 0; i < bufarr->len; i++) {
1006 GValue *bufval = &g_array_index (bufarr, GValue, i);
1008 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1010 goto wrong_content_type;
1013 buf[i] = g_value_peek_pointer (bufval);
1017 if (bufarr->len > 0) {
1018 for (i = 0; i < bufarr->len - 1; i++) {
1019 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1023 for (i = 0; i < bufarr->len; ++i) {
1024 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1027 priv_data = g_malloc0 (priv_data_size);
1029 priv_data[0] = bufarr->len - 1;
1032 if (bufarr->len > 0) {
1033 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1034 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1035 priv_data[offset++] = 0xff;
1037 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1041 for (i = 0; i < bufarr->len; ++i) {
1042 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1043 GST_BUFFER_SIZE (buf[i]));
1044 offset += GST_BUFFER_SIZE (buf[i]);
1047 context->codec_priv = priv_data;
1048 context->codec_priv_size = priv_data_size;
1051 *p_buf0 = gst_buffer_ref (buf[0]);
1060 GST_WARNING ("required streamheaders missing in sink caps!");
1065 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1066 G_VALUE_TYPE_NAME (streamheader));
1071 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1076 GST_WARNING ("streamheaders array does not contain GstBuffers");
1082 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1083 GstMatroskaTrackContext * context)
1085 GstBuffer *buf0 = NULL;
1087 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1090 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1091 GST_WARNING ("First vorbis header too small, ignoring");
1093 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1094 GstMatroskaTrackAudioContext *audiocontext;
1097 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1098 audiocontext = (GstMatroskaTrackAudioContext *) context;
1099 audiocontext->channels = GST_READ_UINT8 (hdr);
1100 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1105 gst_buffer_unref (buf0);
1111 theora_streamheader_to_codecdata (const GValue * streamheader,
1112 GstMatroskaTrackContext * context)
1114 GstBuffer *buf0 = NULL;
1116 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1119 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1120 GST_WARNING ("First theora header too small, ignoring");
1121 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1122 GST_WARNING ("First header not a theora identification header, ignoring");
1124 GstMatroskaTrackVideoContext *videocontext;
1125 guint fps_num, fps_denom, par_num, par_denom;
1128 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1130 videocontext = (GstMatroskaTrackVideoContext *) context;
1131 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1132 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1133 hdr += 3 + 3 + 1 + 1;
1134 fps_num = GST_READ_UINT32_BE (hdr);
1135 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1136 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1137 fps_denom, fps_num);
1139 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1140 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1141 if (par_num > 0 && par_num > 0) {
1142 if (par_num > par_denom) {
1143 videocontext->display_width =
1144 videocontext->pixel_width * par_num / par_denom;
1145 videocontext->display_height = videocontext->pixel_height;
1146 } else if (par_num < par_denom) {
1147 videocontext->display_width = videocontext->pixel_width;
1148 videocontext->display_height =
1149 videocontext->pixel_height * par_denom / par_num;
1151 videocontext->display_width = 0;
1152 videocontext->display_height = 0;
1155 videocontext->display_width = 0;
1156 videocontext->display_height = 0;
1162 gst_buffer_unref (buf0);
1168 kate_streamheader_to_codecdata (const GValue * streamheader,
1169 GstMatroskaTrackContext * context)
1171 GstBuffer *buf0 = NULL;
1173 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1176 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1177 GST_WARNING ("First kate header too small, ignoring");
1178 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1179 GST_WARNING ("First header not a kate identification header, ignoring");
1183 gst_buffer_unref (buf0);
1189 flac_streamheader_to_codecdata (const GValue * streamheader,
1190 GstMatroskaTrackContext * context)
1197 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1198 GST_WARNING ("No or invalid streamheader field in the caps");
1202 bufarr = g_value_peek_pointer (streamheader);
1203 if (bufarr->len < 2) {
1204 GST_WARNING ("Too few headers in streamheader field");
1208 context->xiph_headers_to_skip = bufarr->len + 1;
1210 bufval = &g_array_index (bufarr, GValue, 0);
1211 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1212 GST_WARNING ("streamheaders array does not contain GstBuffers");
1216 buffer = g_value_peek_pointer (bufval);
1218 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1219 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1220 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1221 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1222 GST_WARNING ("Invalid streamheader for FLAC");
1226 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1227 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1228 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1229 GST_BUFFER_SIZE (buffer) - 9);
1231 for (i = 1; i < bufarr->len; i++) {
1232 bufval = &g_array_index (bufarr, GValue, i);
1234 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1235 g_free (context->codec_priv);
1236 context->codec_priv = NULL;
1237 context->codec_priv_size = 0;
1238 GST_WARNING ("streamheaders array does not contain GstBuffers");
1242 buffer = g_value_peek_pointer (bufval);
1244 context->codec_priv =
1245 g_realloc (context->codec_priv,
1246 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1247 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1248 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1249 context->codec_priv_size =
1250 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1257 speex_streamheader_to_codecdata (const GValue * streamheader,
1258 GstMatroskaTrackContext * context)
1264 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1265 GST_WARNING ("No or invalid streamheader field in the caps");
1269 bufarr = g_value_peek_pointer (streamheader);
1270 if (bufarr->len != 2) {
1271 GST_WARNING ("Too few headers in streamheader field");
1275 context->xiph_headers_to_skip = bufarr->len + 1;
1277 bufval = &g_array_index (bufarr, GValue, 0);
1278 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1279 GST_WARNING ("streamheaders array does not contain GstBuffers");
1283 buffer = g_value_peek_pointer (bufval);
1285 if (GST_BUFFER_SIZE (buffer) < 80
1286 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1287 GST_WARNING ("Invalid streamheader for Speex");
1291 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1292 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1293 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1294 GST_BUFFER_SIZE (buffer));
1296 bufval = &g_array_index (bufarr, GValue, 1);
1298 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1299 g_free (context->codec_priv);
1300 context->codec_priv = NULL;
1301 context->codec_priv_size = 0;
1302 GST_WARNING ("streamheaders array does not contain GstBuffers");
1306 buffer = g_value_peek_pointer (bufval);
1308 context->codec_priv =
1309 g_realloc (context->codec_priv,
1310 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1311 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1312 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1313 context->codec_priv_size =
1314 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1319 static const gchar *
1320 aac_codec_data_to_codec_id (const GstBuffer * buf)
1322 const gchar *result;
1325 /* default to MAIN */
1328 if (GST_BUFFER_SIZE (buf) >= 2) {
1329 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1347 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1356 * gst_matroska_mux_audio_pad_setcaps:
1357 * @pad: Pad which got the caps.
1360 * Setcaps function for audio sink pad.
1362 * Returns: #TRUE on success.
1365 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1367 GstMatroskaTrackContext *context = NULL;
1368 GstMatroskaTrackAudioContext *audiocontext;
1369 GstMatroskaMux *mux;
1370 GstMatroskaPad *collect_pad;
1371 const gchar *mimetype;
1372 gint samplerate = 0, channels = 0;
1373 GstStructure *structure;
1374 const GValue *codec_data = NULL;
1375 const GstBuffer *buf = NULL;
1376 const gchar *stream_format = NULL;
1378 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1381 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1382 g_assert (collect_pad);
1383 context = collect_pad->track;
1385 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1386 audiocontext = (GstMatroskaTrackAudioContext *) context;
1388 structure = gst_caps_get_structure (caps, 0);
1389 mimetype = gst_structure_get_name (structure);
1392 gst_structure_get_int (structure, "rate", &samplerate);
1393 gst_structure_get_int (structure, "channels", &channels);
1395 audiocontext->samplerate = samplerate;
1396 audiocontext->channels = channels;
1397 audiocontext->bitdepth = 0;
1398 context->default_duration = 0;
1400 codec_data = gst_structure_get_value (structure, "codec_data");
1402 buf = gst_value_get_buffer (codec_data);
1404 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1405 * data and other settings
1409 if (!strcmp (mimetype, "audio/mpeg")) {
1410 gint mpegversion = 0;
1412 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1413 switch (mpegversion) {
1419 gst_structure_get_int (structure, "layer", &layer);
1421 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1422 GST_WARNING_OBJECT (mux,
1423 "Unable to determine MPEG audio version, assuming 1");
1429 else if (layer == 2)
1431 else if (version == 2)
1436 context->default_duration =
1437 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1441 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1444 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1447 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1456 stream_format = gst_structure_get_string (structure, "stream-format");
1457 /* check this is raw aac */
1458 if (stream_format) {
1459 if (strcmp (stream_format, "raw") != 0) {
1460 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1464 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1469 if (mpegversion == 2)
1471 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1472 aac_codec_data_to_codec_id (buf));
1473 else if (mpegversion == 4)
1475 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1476 aac_codec_data_to_codec_id (buf));
1478 g_assert_not_reached ();
1480 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1487 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1489 gint endianness = G_LITTLE_ENDIAN;
1490 gboolean signedness = TRUE;
1492 if (!gst_structure_get_int (structure, "width", &width) ||
1493 !gst_structure_get_int (structure, "depth", &depth) ||
1494 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1495 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1500 !gst_structure_get_int (structure, "endianness", &endianness)) {
1501 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1505 if (width != depth) {
1506 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1510 /* FIXME: where is this spec'ed out? (tpm) */
1511 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1512 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1516 audiocontext->bitdepth = depth;
1517 if (endianness == G_BIG_ENDIAN)
1518 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1520 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1522 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1525 if (!gst_structure_get_int (structure, "width", &width)) {
1526 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1530 audiocontext->bitdepth = width;
1531 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1533 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1534 const GValue *streamheader;
1536 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1538 if (context->codec_priv != NULL) {
1539 g_free (context->codec_priv);
1540 context->codec_priv = NULL;
1541 context->codec_priv_size = 0;
1544 streamheader = gst_structure_get_value (structure, "streamheader");
1545 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1546 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1547 ("vorbis stream headers missing or malformed"));
1550 } else if (!strcmp (mimetype, "audio/x-flac")) {
1551 const GValue *streamheader;
1553 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1554 if (context->codec_priv != NULL) {
1555 g_free (context->codec_priv);
1556 context->codec_priv = NULL;
1557 context->codec_priv_size = 0;
1560 streamheader = gst_structure_get_value (structure, "streamheader");
1561 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1562 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1563 ("flac stream headers missing or malformed"));
1566 } else if (!strcmp (mimetype, "audio/x-speex")) {
1567 const GValue *streamheader;
1569 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1570 if (context->codec_priv != NULL) {
1571 g_free (context->codec_priv);
1572 context->codec_priv = NULL;
1573 context->codec_priv_size = 0;
1576 streamheader = gst_structure_get_value (structure, "streamheader");
1577 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1578 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1579 ("speex stream headers missing or malformed"));
1582 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1583 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1584 } else if (!strcmp (mimetype, "audio/x-eac3")) {
1585 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
1586 } else if (!strcmp (mimetype, "audio/x-dts")) {
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_DTS);
1588 } else if (!strcmp (mimetype, "audio/x-tta")) {
1591 /* TTA frame duration */
1592 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1594 gst_structure_get_int (structure, "width", &width);
1595 audiocontext->bitdepth = width;
1596 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1598 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1600 const GValue *mdpr_data;
1602 gst_structure_get_int (structure, "raversion", &raversion);
1603 switch (raversion) {
1605 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1608 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1611 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1617 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1618 if (mdpr_data != NULL) {
1619 guint8 *priv_data = NULL;
1620 guint priv_data_size = 0;
1622 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1624 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1625 priv_data = g_malloc0 (priv_data_size);
1627 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1629 context->codec_priv = priv_data;
1630 context->codec_priv_size = priv_data_size;
1633 } else if (!strcmp (mimetype, "audio/x-wma")) {
1635 guint codec_priv_size;
1642 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1643 || !gst_structure_get_int (structure, "block_align", &block_align)
1644 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1645 || samplerate == 0 || channels == 0) {
1646 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1647 "channels/rate on WMA caps");
1651 switch (wmaversion) {
1653 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1656 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1659 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1662 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1666 if (gst_structure_get_int (structure, "depth", &depth))
1667 audiocontext->bitdepth = depth;
1669 codec_priv_size = WAVEFORMATEX_SIZE;
1671 codec_priv_size += GST_BUFFER_SIZE (buf);
1673 /* serialize waveformatex structure */
1674 codec_priv = g_malloc0 (codec_priv_size);
1675 GST_WRITE_UINT16_LE (codec_priv, format);
1676 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1677 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1678 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1679 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1680 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1682 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1684 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1686 /* process codec private/initialization data, if any */
1688 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1689 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1692 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1693 context->codec_priv = (gpointer) codec_priv;
1694 context->codec_priv_size = codec_priv_size;
1702 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1703 GST_PAD_NAME (pad), caps);
1710 * gst_matroska_mux_subtitle_pad_setcaps:
1711 * @pad: Pad which got the caps.
1714 * Setcaps function for subtitle sink pad.
1716 * Returns: #TRUE on success.
1719 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1722 * Consider this as boilerplate code for now. There is
1723 * no single subtitle creation element in GStreamer,
1724 * neither do I know how subtitling works at all. */
1726 /* There is now (at least) one such alement (kateenc), and I'm going
1727 to handle it here and claim it works when it can be piped back
1728 through GStreamer and VLC */
1730 GstMatroskaTrackContext *context = NULL;
1731 GstMatroskaTrackSubtitleContext *scontext;
1732 GstMatroskaMux *mux;
1733 GstMatroskaPad *collect_pad;
1734 const gchar *mimetype;
1735 GstStructure *structure;
1737 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1740 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1741 g_assert (collect_pad);
1742 context = collect_pad->track;
1744 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1745 scontext = (GstMatroskaTrackSubtitleContext *) context;
1747 structure = gst_caps_get_structure (caps, 0);
1748 mimetype = gst_structure_get_name (structure);
1751 scontext->check_utf8 = 1;
1752 scontext->invalid_utf8 = 0;
1753 context->default_duration = 0;
1755 /* TODO: - other format than Kate */
1757 if (!strcmp (mimetype, "subtitle/x-kate")) {
1758 const GValue *streamheader;
1760 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1762 if (context->codec_priv != NULL) {
1763 g_free (context->codec_priv);
1764 context->codec_priv = NULL;
1765 context->codec_priv_size = 0;
1768 streamheader = gst_structure_get_value (structure, "streamheader");
1769 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1770 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1771 ("kate stream headers missing or malformed"));
1782 * gst_matroska_mux_request_new_pad:
1783 * @element: #GstMatroskaMux.
1784 * @templ: #GstPadTemplate.
1785 * @pad_name: New pad name.
1787 * Request pad function for sink templates.
1789 * Returns: New #GstPad.
1792 gst_matroska_mux_request_new_pad (GstElement * element,
1793 GstPadTemplate * templ, const gchar * pad_name)
1795 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1796 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1797 GstMatroskaPad *collect_pad;
1798 GstPad *newpad = NULL;
1800 GstPadSetCapsFunction setcapsfunc = NULL;
1801 GstMatroskaTrackContext *context = NULL;
1803 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1804 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1805 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1806 context = (GstMatroskaTrackContext *)
1807 g_new0 (GstMatroskaTrackAudioContext, 1);
1808 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1809 context->name = g_strdup ("Audio");
1810 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1811 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1812 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1813 context = (GstMatroskaTrackContext *)
1814 g_new0 (GstMatroskaTrackVideoContext, 1);
1815 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1816 context->name = g_strdup ("Video");
1817 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1818 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1819 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1820 context = (GstMatroskaTrackContext *)
1821 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1822 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1823 context->name = g_strdup ("Subtitle");
1825 GST_WARNING_OBJECT (mux, "This is not our template!");
1829 newpad = gst_pad_new_from_template (templ, name);
1831 collect_pad = (GstMatroskaPad *)
1832 gst_collect_pads_add_pad_full (mux->collect, newpad,
1833 sizeof (GstMatroskaPad),
1834 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1836 collect_pad->track = context;
1837 gst_matroska_pad_reset (collect_pad, FALSE);
1839 /* FIXME: hacked way to override/extend the event function of
1840 * GstCollectPads; because it sets its own event function giving the
1841 * element no access to events.
1842 * TODO GstCollectPads should really give its 'users' a clean chance to
1843 * properly handle events that are not meant for collectpads itself.
1844 * Perhaps a callback or so, though rejected (?) in #340060.
1845 * This would allow (clean) transcoding of info from demuxer/streams
1846 * to another muxer */
1847 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1848 gst_pad_set_event_function (newpad,
1849 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1851 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1852 gst_pad_set_active (newpad, TRUE);
1853 gst_element_add_pad (element, newpad);
1860 * gst_matroska_mux_release_pad:
1861 * @element: #GstMatroskaMux.
1862 * @pad: Pad to release.
1864 * Release a previously requested pad.
1867 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1869 GstMatroskaMux *mux;
1872 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1874 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1875 GstCollectData *cdata = (GstCollectData *) walk->data;
1876 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1878 if (cdata->pad == pad) {
1879 GstClockTime min_dur; /* observed minimum duration */
1881 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1882 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1883 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1884 if (collect_pad->duration < min_dur)
1885 collect_pad->duration = min_dur;
1888 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1889 mux->duration < collect_pad->duration)
1890 mux->duration = collect_pad->duration;
1896 gst_collect_pads_remove_pad (mux->collect, pad);
1897 if (gst_element_remove_pad (element, pad))
1903 * gst_matroska_mux_track_header:
1904 * @mux: #GstMatroskaMux
1905 * @context: Tack context.
1907 * Write a track header.
1910 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1911 GstMatroskaTrackContext * context)
1913 GstEbmlWrite *ebml = mux->ebml_write;
1916 /* TODO: check if everything necessary is written and check default values */
1918 /* track type goes before the type-specific stuff */
1919 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1920 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1923 gst_matroska_mux_create_uid ());
1924 if (context->default_duration) {
1925 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1926 context->default_duration);
1928 if (context->language) {
1929 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1933 /* type-specific stuff */
1934 switch (context->type) {
1935 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1936 GstMatroskaTrackVideoContext *videocontext =
1937 (GstMatroskaTrackVideoContext *) context;
1939 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1940 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1941 videocontext->pixel_width);
1942 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1943 videocontext->pixel_height);
1944 if (videocontext->display_width && videocontext->display_height) {
1945 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1946 videocontext->display_width);
1947 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1948 videocontext->display_height);
1950 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1951 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1952 if (videocontext->fourcc) {
1953 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1955 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1956 (gpointer) & fcc_le, 4);
1958 gst_ebml_write_master_finish (ebml, master);
1963 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1964 GstMatroskaTrackAudioContext *audiocontext =
1965 (GstMatroskaTrackAudioContext *) context;
1967 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1968 if (audiocontext->samplerate != 8000)
1969 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1970 audiocontext->samplerate);
1971 if (audiocontext->channels != 1)
1972 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1973 audiocontext->channels);
1974 if (audiocontext->bitdepth) {
1975 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1976 audiocontext->bitdepth);
1978 gst_ebml_write_master_finish (ebml, master);
1984 /* doesn't need type-specific data */
1988 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1989 if (context->codec_priv)
1990 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1991 context->codec_priv, context->codec_priv_size);
1992 /* FIXME: until we have a nice way of getting the codecname
1993 * out of the caps, I'm not going to enable this. Too much
1994 * (useless, double, boring) work... */
1995 /* TODO: Use value from tags if any */
1996 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1997 context->codec_name); */
1998 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2003 * gst_matroska_mux_start:
2004 * @mux: #GstMatroskaMux
2006 * Start a new matroska file (write headers etc...)
2009 gst_matroska_mux_start (GstMatroskaMux * mux)
2011 GstEbmlWrite *ebml = mux->ebml_write;
2012 const gchar *doctype;
2013 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2014 GST_MATROSKA_ID_TRACKS,
2015 GST_MATROSKA_ID_CUES,
2016 GST_MATROSKA_ID_TAGS,
2019 guint64 master, child;
2023 GstClockTime duration = 0;
2024 guint32 segment_uid[4];
2025 GTimeVal time = { 0, 0 };
2027 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2028 ebml->caps = gst_caps_from_string ("video/webm");
2030 ebml->caps = gst_caps_from_string ("video/x-matroska");
2032 /* we start with a EBML header */
2033 doctype = mux->doctype;
2034 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2035 doctype, mux->doctype_version);
2036 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2038 /* the rest of the header is cached */
2039 gst_ebml_write_set_cache (ebml, 0x1000);
2041 /* start a segment */
2043 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2044 mux->segment_master = ebml->pos;
2046 if (!mux->streamable) {
2047 /* seekhead (table of contents) - we set the positions later */
2048 mux->seekhead_pos = ebml->pos;
2049 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2050 for (i = 0; seekhead_id[i] != 0; i++) {
2051 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2052 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2053 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2054 gst_ebml_write_master_finish (ebml, child);
2056 gst_ebml_write_master_finish (ebml, master);
2060 mux->info_pos = ebml->pos;
2061 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2062 for (i = 0; i < 4; i++) {
2063 segment_uid[i] = g_random_int ();
2065 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2066 (guint8 *) segment_uid, 16);
2067 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2068 mux->duration_pos = ebml->pos;
2070 if (!mux->streamable) {
2071 for (collected = mux->collect->data; collected;
2072 collected = g_slist_next (collected)) {
2073 GstMatroskaPad *collect_pad;
2074 GstFormat format = GST_FORMAT_TIME;
2076 gint64 trackduration;
2078 collect_pad = (GstMatroskaPad *) collected->data;
2079 thepad = collect_pad->collect.pad;
2081 /* Query the total length of the track. */
2082 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2083 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2084 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2085 GST_TIME_ARGS (trackduration));
2086 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2087 duration = (GstClockTime) trackduration;
2091 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2092 gst_guint64_to_gdouble (duration) /
2093 gst_guint64_to_gdouble (mux->time_scale));
2095 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2096 "GStreamer plugin version " PACKAGE_VERSION);
2097 if (mux->writing_app && mux->writing_app[0]) {
2098 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2100 g_get_current_time (&time);
2101 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2102 gst_ebml_write_master_finish (ebml, master);
2105 mux->tracks_pos = ebml->pos;
2106 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2108 for (collected = mux->collect->data; collected;
2109 collected = g_slist_next (collected)) {
2110 GstMatroskaPad *collect_pad;
2113 collect_pad = (GstMatroskaPad *) collected->data;
2114 thepad = collect_pad->collect.pad;
2116 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2117 collect_pad->track->codec_id != 0) {
2118 collect_pad->track->num = tracknum++;
2119 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2120 gst_matroska_mux_track_header (mux, collect_pad->track);
2121 gst_ebml_write_master_finish (ebml, child);
2124 gst_ebml_write_master_finish (ebml, master);
2126 /* lastly, flush the cache */
2127 gst_ebml_write_flush_cache (ebml, FALSE, 0);
2131 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2134 /* TODO: more sensible tag mappings */
2137 const gchar *matroska_tagname;
2138 const gchar *gstreamer_tagname;
2142 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2143 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2144 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2145 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2146 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2147 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2148 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2149 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2150 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2151 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2152 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2153 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2154 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2155 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2156 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2158 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2160 guint64 simpletag_master;
2162 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2163 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2164 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2166 if (strcmp (tagname_gst, tag) == 0) {
2167 GValue src = { 0, };
2170 if (!gst_tag_list_copy_value (&src, list, tag))
2172 if ((dest = gst_value_serialize (&src))) {
2174 simpletag_master = gst_ebml_write_master_start (ebml,
2175 GST_MATROSKA_ID_SIMPLETAG);
2176 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2177 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2178 gst_ebml_write_master_finish (ebml, simpletag_master);
2181 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2183 g_value_unset (&src);
2191 * gst_matroska_mux_finish:
2192 * @mux: #GstMatroskaMux
2194 * Finish a new matroska file (write index etc...)
2197 gst_matroska_mux_finish (GstMatroskaMux * mux)
2199 GstEbmlWrite *ebml = mux->ebml_write;
2201 guint64 duration = 0;
2203 const GstTagList *tags;
2205 /* finish last cluster */
2207 gst_ebml_write_master_finish (ebml, mux->cluster);
2211 if (mux->index != NULL) {
2213 guint64 master, pointentry_master, trackpos_master;
2215 mux->cues_pos = ebml->pos;
2216 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2217 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2219 for (n = 0; n < mux->num_indexes; n++) {
2220 GstMatroskaIndex *idx = &mux->index[n];
2222 pointentry_master = gst_ebml_write_master_start (ebml,
2223 GST_MATROSKA_ID_POINTENTRY);
2224 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2225 idx->time / mux->time_scale);
2226 trackpos_master = gst_ebml_write_master_start (ebml,
2227 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2228 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2229 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2230 idx->pos - mux->segment_master);
2231 gst_ebml_write_master_finish (ebml, trackpos_master);
2232 gst_ebml_write_master_finish (ebml, pointentry_master);
2235 gst_ebml_write_master_finish (ebml, master);
2236 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
2240 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2242 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2243 guint64 master_tags, master_tag;
2245 GST_DEBUG ("Writing tags");
2247 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2248 mux->tags_pos = ebml->pos;
2249 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2250 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2251 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2252 gst_ebml_write_master_finish (ebml, master_tag);
2253 gst_ebml_write_master_finish (ebml, master_tags);
2256 /* update seekhead. We know that:
2257 * - a seekhead contains 4 entries.
2258 * - order of entries is as above.
2259 * - a seekhead has a 4-byte header + 8-byte length
2260 * - each entry is 2-byte master, 2-byte ID pointer,
2261 * 2-byte length pointer, all 8/1-byte length, 4-
2262 * byte ID and 8-byte length pointer, where the
2263 * length pointer starts at 20.
2264 * - all entries are local to the segment (so pos - segment_master).
2265 * - so each entry is at 12 + 20 + num * 28. */
2266 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2267 mux->info_pos - mux->segment_master);
2268 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2269 mux->tracks_pos - mux->segment_master);
2270 if (mux->index != NULL) {
2271 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2272 mux->cues_pos - mux->segment_master);
2275 guint64 my_pos = ebml->pos;
2277 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2278 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2279 gst_ebml_write_seek (ebml, my_pos);
2282 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2283 mux->tags_pos - mux->segment_master);
2286 guint64 my_pos = ebml->pos;
2288 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2289 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2290 gst_ebml_write_seek (ebml, my_pos);
2293 /* update duration */
2294 /* first get the overall duration */
2295 /* a released track may have left a duration in here */
2296 duration = mux->duration;
2297 for (collected = mux->collect->data; collected;
2298 collected = g_slist_next (collected)) {
2299 GstMatroskaPad *collect_pad;
2300 GstClockTime min_duration; /* observed minimum duration */
2302 collect_pad = (GstMatroskaPad *) collected->data;
2304 GST_DEBUG_OBJECT (mux,
2305 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2306 " end ts %" GST_TIME_FORMAT, collect_pad,
2307 GST_TIME_ARGS (collect_pad->start_ts),
2308 GST_TIME_ARGS (collect_pad->end_ts));
2310 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2311 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2313 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2314 if (collect_pad->duration < min_duration)
2315 collect_pad->duration = min_duration;
2316 GST_DEBUG_OBJECT (collect_pad,
2317 "final track duration: %" GST_TIME_FORMAT,
2318 GST_TIME_ARGS (collect_pad->duration));
2321 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2322 duration < collect_pad->duration)
2323 duration = collect_pad->duration;
2325 if (duration != 0) {
2326 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2327 GST_TIME_ARGS (duration));
2328 pos = mux->ebml_write->pos;
2329 gst_ebml_write_seek (ebml, mux->duration_pos);
2330 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2331 gst_guint64_to_gdouble (duration) /
2332 gst_guint64_to_gdouble (mux->time_scale));
2333 gst_ebml_write_seek (ebml, pos);
2336 guint64 my_pos = ebml->pos;
2338 gst_ebml_write_seek (ebml, mux->duration_pos);
2339 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2340 gst_ebml_write_seek (ebml, my_pos);
2342 GST_DEBUG_OBJECT (mux, "finishing segment");
2343 /* finish segment - this also writes element length */
2344 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2349 * gst_matroska_mux_best_pad:
2350 * @mux: #GstMatroskaMux
2351 * @popped: True if at least one buffer was popped from #GstCollectPads
2353 * Find a pad with the oldest data
2354 * (data from this pad should be written first).
2356 * Returns: Selected pad.
2358 static GstMatroskaPad *
2359 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2362 GstMatroskaPad *best = NULL;
2365 for (collected = mux->collect->data; collected;
2366 collected = g_slist_next (collected)) {
2367 GstMatroskaPad *collect_pad;
2369 collect_pad = (GstMatroskaPad *) collected->data;
2370 /* fetch a new buffer if needed */
2371 if (collect_pad->buffer == NULL) {
2372 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2373 (GstCollectData *) collect_pad);
2375 if (collect_pad->buffer != NULL)
2379 /* if we have a buffer check if it is better then the current best one */
2380 if (collect_pad->buffer != NULL) {
2381 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2382 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2383 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2384 GST_BUFFER_TIMESTAMP (best->buffer))) {
2394 * gst_matroska_mux_buffer_header:
2395 * @track: Track context.
2396 * @relative_timestamp: relative timestamp of the buffer
2397 * @flags: Buffer flags.
2399 * Create a buffer containing buffer header.
2401 * Returns: New buffer.
2404 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2405 gint16 relative_timestamp, int flags)
2409 hdr = gst_buffer_new_and_alloc (4);
2410 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2411 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2412 /* time relative to clustertime */
2413 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2416 GST_BUFFER_DATA (hdr)[3] = flags;
2421 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2422 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2423 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2426 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2427 GstMatroskaPad * collect_pad, GstBuffer * buf)
2429 GstMatroskaTrackVideoContext *ctx =
2430 (GstMatroskaTrackVideoContext *) collect_pad->track;
2431 const guint8 *data = GST_BUFFER_DATA (buf);
2432 guint size = GST_BUFFER_SIZE (buf);
2434 guint32 next_parse_offset;
2435 GstBuffer *ret = NULL;
2436 gboolean is_muxing_unit = FALSE;
2438 if (GST_BUFFER_SIZE (buf) < 13) {
2439 gst_buffer_unref (buf);
2443 /* Check if this buffer contains a picture or end-of-sequence packet */
2444 while (size >= 13) {
2445 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2446 gst_buffer_unref (buf);
2450 parse_code = GST_READ_UINT8 (data + 4);
2451 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2452 if (ctx->dirac_unit) {
2453 gst_buffer_unref (ctx->dirac_unit);
2454 ctx->dirac_unit = NULL;
2456 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2457 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2458 is_muxing_unit = TRUE;
2462 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2464 if (G_UNLIKELY (next_parse_offset == 0))
2467 data += next_parse_offset;
2468 size -= next_parse_offset;
2471 if (ctx->dirac_unit)
2472 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2474 ctx->dirac_unit = gst_buffer_ref (buf);
2476 if (is_muxing_unit) {
2477 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2478 ctx->dirac_unit = NULL;
2479 gst_buffer_copy_metadata (ret, buf,
2480 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2481 GST_BUFFER_COPY_CAPS);
2482 gst_buffer_unref (buf);
2484 gst_buffer_unref (buf);
2492 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2496 GValue streamheader = { 0 };
2497 GValue bufval = { 0 };
2498 GstBuffer *streamheader_buffer;
2499 GstEbmlWrite *ebml = mux->ebml_write;
2501 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2502 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2503 caps = gst_caps_new_simple ("video/webm", NULL);
2505 caps = gst_caps_new_simple ("video/x-matroska", NULL);
2507 s = gst_caps_get_structure (caps, 0);
2508 g_value_init (&streamheader, GST_TYPE_ARRAY);
2509 g_value_init (&bufval, GST_TYPE_BUFFER);
2510 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_IN_CAPS);
2511 gst_value_set_buffer (&bufval, streamheader_buffer);
2512 gst_value_array_append_value (&streamheader, &bufval);
2513 g_value_unset (&bufval);
2514 gst_structure_set_value (s, "streamheader", &streamheader);
2515 g_value_unset (&streamheader);
2516 gst_caps_replace (&ebml->caps, caps);
2517 gst_buffer_unref (streamheader_buffer);
2518 gst_caps_unref (caps);
2522 * gst_matroska_mux_write_data:
2523 * @mux: #GstMatroskaMux
2524 * @collect_pad: #GstMatroskaPad with the data
2526 * Write collected data (called from gst_matroska_mux_collected).
2528 * Returns: Result of the gst_pad_push issued to write the data.
2530 static GstFlowReturn
2531 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2533 GstEbmlWrite *ebml = mux->ebml_write;
2534 GstBuffer *buf, *hdr;
2536 gboolean write_duration;
2537 gint16 relative_timestamp;
2538 gint64 relative_timestamp64;
2539 guint64 block_duration;
2540 gboolean is_video_keyframe = FALSE;
2543 buf = collect_pad->buffer;
2544 collect_pad->buffer = NULL;
2546 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2547 if (collect_pad->track->xiph_headers_to_skip > 0) {
2548 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2549 gst_buffer_unref (buf);
2550 --collect_pad->track->xiph_headers_to_skip;
2554 /* for dirac we have to queue up everything up to a picture unit */
2555 if (collect_pad->track->codec_id != NULL &&
2556 strcmp (collect_pad->track->codec_id,
2557 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2558 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2563 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2564 * this would wreak havoc with time stored in matroska file */
2565 /* TODO: maybe calculate a timestamp by using the previous timestamp
2566 * and default duration */
2567 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2568 GST_WARNING_OBJECT (collect_pad->collect.pad,
2569 "Invalid buffer timestamp; dropping buffer");
2570 gst_buffer_unref (buf);
2574 /* set the timestamp for outgoing buffers */
2575 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2577 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2578 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2579 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2580 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2581 is_video_keyframe = TRUE;
2585 /* start a new cluster at every keyframe or when we may be reaching the
2586 * limit of the relative timestamp */
2587 if (mux->cluster_time +
2588 mux->max_cluster_duration < GST_BUFFER_TIMESTAMP (buf)
2589 || is_video_keyframe) {
2590 if (!mux->streamable)
2591 gst_ebml_write_master_finish (ebml, mux->cluster);
2592 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2593 mux->cluster_pos = ebml->pos;
2594 gst_ebml_write_set_cache (ebml, 0x20);
2596 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2597 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2598 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2600 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2601 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2603 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2604 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2605 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2606 mux->prev_cluster_size);
2611 mux->cluster_pos = ebml->pos;
2612 gst_ebml_write_set_cache (ebml, 0x20);
2613 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2614 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2615 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2616 gst_ebml_write_flush_cache (ebml, TRUE, GST_BUFFER_TIMESTAMP (buf));
2617 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2620 /* update duration of this track */
2621 if (GST_BUFFER_DURATION_IS_VALID (buf))
2622 collect_pad->duration += GST_BUFFER_DURATION (buf);
2624 /* We currently write index entries for all video tracks or for the audio
2625 * track in a single-track audio file. This could be improved by keeping the
2626 * index only for the *first* video track. */
2628 /* TODO: index is useful for every track, should contain the number of
2629 * the block in the cluster which contains the timestamp, should also work
2630 * for files with multiple audio tracks.
2632 if (is_video_keyframe ||
2633 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2634 (mux->num_streams == 1))) {
2637 if (mux->min_index_interval != 0) {
2638 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2639 if (mux->index[last_idx].track == collect_pad->track->num)
2644 if (last_idx < 0 || mux->min_index_interval == 0 ||
2645 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2646 >= mux->min_index_interval)) {
2647 GstMatroskaIndex *idx;
2649 if (mux->num_indexes % 32 == 0) {
2650 mux->index = g_renew (GstMatroskaIndex, mux->index,
2651 mux->num_indexes + 32);
2653 idx = &mux->index[mux->num_indexes++];
2655 idx->pos = mux->cluster_pos;
2656 idx->time = GST_BUFFER_TIMESTAMP (buf);
2657 idx->track = collect_pad->track->num;
2661 /* Check if the duration differs from the default duration. */
2662 write_duration = FALSE;
2663 block_duration = GST_BUFFER_DURATION (buf);
2664 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2665 if (block_duration != collect_pad->track->default_duration) {
2666 write_duration = TRUE;
2670 /* write the block, for doctype v2 use SimpleBlock if possible
2671 * one slice (*breath*).
2672 * FIXME: Need to do correct lacing! */
2673 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2674 if (relative_timestamp64 >= 0) {
2675 /* round the timestamp */
2676 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2678 /* round the timestamp */
2679 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2681 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2683 if (mux->doctype_version > 1 && !write_duration) {
2685 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2688 gst_matroska_mux_create_buffer_header (collect_pad->track,
2689 relative_timestamp, flags);
2690 gst_ebml_write_set_cache (ebml, 0x40);
2691 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2692 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2693 gst_ebml_write_buffer (ebml, hdr);
2694 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2695 gst_ebml_write_buffer (ebml, buf);
2697 return gst_ebml_last_write_result (ebml);
2699 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2700 /* write and call order slightly unnatural,
2701 * but avoids seek and minizes pushing */
2702 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2704 gst_matroska_mux_create_buffer_header (collect_pad->track,
2705 relative_timestamp, 0);
2706 if (write_duration) {
2707 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2708 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2710 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2711 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2712 gst_ebml_write_buffer (ebml, hdr);
2713 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2714 gst_ebml_write_flush_cache (ebml, FALSE, GST_BUFFER_TIMESTAMP (buf));
2715 gst_ebml_write_buffer (ebml, buf);
2717 return gst_ebml_last_write_result (ebml);
2723 * gst_matroska_mux_collected:
2724 * @pads: #GstCollectPads
2725 * @uuser_data: #GstMatroskaMux
2727 * Collectpads callback.
2729 * Returns: #GstFlowReturn
2731 static GstFlowReturn
2732 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2734 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2735 GstEbmlWrite *ebml = mux->ebml_write;
2736 GstMatroskaPad *best;
2740 GST_DEBUG_OBJECT (mux, "Collected pads");
2742 /* start with a header */
2743 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2744 if (mux->collect->data == NULL) {
2745 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2746 ("No input streams configured"));
2747 return GST_FLOW_ERROR;
2749 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2750 gst_ebml_start_streamheader (ebml);
2751 gst_matroska_mux_start (mux);
2752 gst_matroska_mux_stop_streamheader (mux);
2753 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2757 /* which stream to write from? */
2758 best = gst_matroska_mux_best_pad (mux, &popped);
2760 /* if there is no best pad, we have reached EOS */
2762 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2763 if (!mux->streamable) {
2764 gst_matroska_mux_finish (mux);
2766 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
2768 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2769 ret = GST_FLOW_UNEXPECTED;
2772 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2773 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2774 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2775 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2777 /* make note of first and last encountered timestamps, so we can calculate
2778 * the actual duration later when we send an updated header on eos */
2779 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2780 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2781 GstClockTime end_ts = start_ts;
2783 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2784 end_ts += GST_BUFFER_DURATION (best->buffer);
2785 else if (best->track->default_duration)
2786 end_ts += best->track->default_duration;
2788 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2789 best->end_ts = end_ts;
2791 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2792 start_ts < best->start_ts))
2793 best->start_ts = start_ts;
2796 /* write one buffer */
2797 ret = gst_matroska_mux_write_data (mux, best);
2798 } while (ret == GST_FLOW_OK && !popped);
2805 * gst_matroska_mux_change_state:
2806 * @element: #GstMatroskaMux
2807 * @transition: State change transition.
2809 * Change the muxer state.
2811 * Returns: #GstStateChangeReturn
2813 static GstStateChangeReturn
2814 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2816 GstStateChangeReturn ret;
2817 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2819 switch (transition) {
2820 case GST_STATE_CHANGE_NULL_TO_READY:
2822 case GST_STATE_CHANGE_READY_TO_PAUSED:
2823 gst_collect_pads_start (mux->collect);
2825 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2827 case GST_STATE_CHANGE_PAUSED_TO_READY:
2828 gst_collect_pads_stop (mux->collect);
2834 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2836 switch (transition) {
2837 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2839 case GST_STATE_CHANGE_PAUSED_TO_READY:
2840 gst_matroska_mux_reset (GST_ELEMENT (mux));
2842 case GST_STATE_CHANGE_READY_TO_NULL:
2852 gst_matroska_mux_set_property (GObject * object,
2853 guint prop_id, const GValue * value, GParamSpec * pspec)
2855 GstMatroskaMux *mux;
2857 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2858 mux = GST_MATROSKA_MUX (object);
2861 case ARG_WRITING_APP:
2862 if (!g_value_get_string (value)) {
2863 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2866 g_free (mux->writing_app);
2867 mux->writing_app = g_value_dup_string (value);
2869 case ARG_DOCTYPE_VERSION:
2870 mux->doctype_version = g_value_get_int (value);
2872 case ARG_MIN_INDEX_INTERVAL:
2873 mux->min_index_interval = g_value_get_int64 (value);
2875 case ARG_STREAMABLE:
2876 mux->streamable = g_value_get_boolean (value);
2879 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2885 gst_matroska_mux_get_property (GObject * object,
2886 guint prop_id, GValue * value, GParamSpec * pspec)
2888 GstMatroskaMux *mux;
2890 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2891 mux = GST_MATROSKA_MUX (object);
2894 case ARG_WRITING_APP:
2895 g_value_set_string (value, mux->writing_app);
2897 case ARG_DOCTYPE_VERSION:
2898 g_value_set_int (value, mux->doctype_version);
2900 case ARG_MIN_INDEX_INTERVAL:
2901 g_value_set_int64 (value, mux->min_index_interval);
2903 case ARG_STREAMABLE:
2904 g_value_set_boolean (value, mux->streamable);
2907 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);