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_INDEXED TRUE
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_INDEXED,
309 g_param_spec_boolean ("indexed", "Determines whether output should be "
310 "indexed", "If set to false, the output should be as if it is to "
311 "be streamed and hence no indexes written or duration written.",
312 DEFAULT_INDEXED, 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->indexed = DEFAULT_INDEXED;
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;
553 mux->cluster_time = 0;
554 mux->cluster_pos = 0;
555 mux->prev_cluster_size = 0;
558 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
562 * gst_matroska_mux_handle_src_event:
563 * @pad: Pad which received the event.
564 * @event: Received event.
566 * handle events - copied from oggmux without understanding
568 * Returns: #TRUE on success.
571 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
575 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
579 /* disable seeking for now */
585 return gst_pad_event_default (pad, event);
589 * gst_matroska_mux_handle_sink_event:
590 * @pad: Pad which received the event.
591 * @event: Received event.
593 * handle events - informational ones like tags
595 * Returns: #TRUE on success.
598 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
600 GstMatroskaTrackContext *context;
601 GstMatroskaPad *collect_pad;
606 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
608 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
609 switch (GST_EVENT_TYPE (event)) {
613 GST_DEBUG_OBJECT (mux, "received tag event");
614 gst_event_parse_tag (event, &list);
616 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
617 g_assert (collect_pad);
618 context = collect_pad->track;
621 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
622 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
623 const gchar *lang_code;
625 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
627 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
628 context->language = g_strdup (lang_code);
630 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
635 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
636 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
639 case GST_EVENT_NEWSEGMENT:
640 /* We don't support NEWSEGMENT events */
642 gst_event_unref (event);
648 /* now GstCollectPads can take care of the rest, e.g. EOS */
650 ret = mux->collect_event (pad, event);
651 gst_object_unref (mux);
658 * gst_matroska_mux_video_pad_setcaps:
659 * @pad: Pad which got the caps.
662 * Setcaps function for video sink pad.
664 * Returns: #TRUE on success.
667 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
669 GstMatroskaTrackContext *context = NULL;
670 GstMatroskaTrackVideoContext *videocontext;
672 GstMatroskaPad *collect_pad;
673 GstStructure *structure;
674 const gchar *mimetype;
675 const GValue *value = NULL;
676 const GstBuffer *codec_buf = NULL;
677 gint width, height, pixel_width, pixel_height;
679 gboolean interlaced = FALSE;
681 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
684 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
685 g_assert (collect_pad);
686 context = collect_pad->track;
688 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
689 videocontext = (GstMatroskaTrackVideoContext *) context;
691 /* gst -> matroska ID'ing */
692 structure = gst_caps_get_structure (caps, 0);
694 mimetype = gst_structure_get_name (structure);
696 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
698 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
700 if (!strcmp (mimetype, "video/x-theora")) {
701 /* we'll extract the details later from the theora identification header */
705 /* get general properties */
706 /* spec says it is mandatory */
707 if (!gst_structure_get_int (structure, "width", &width) ||
708 !gst_structure_get_int (structure, "height", &height))
711 videocontext->pixel_width = width;
712 videocontext->pixel_height = height;
713 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
715 context->default_duration =
716 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
717 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
718 GST_TIME_ARGS (context->default_duration));
720 context->default_duration = 0;
722 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
723 &pixel_width, &pixel_height)) {
724 if (pixel_width > pixel_height) {
725 videocontext->display_width = width * pixel_width / pixel_height;
726 videocontext->display_height = height;
727 } else if (pixel_width < pixel_height) {
728 videocontext->display_width = width;
729 videocontext->display_height = height * pixel_height / pixel_width;
731 videocontext->display_width = 0;
732 videocontext->display_height = 0;
735 videocontext->display_width = 0;
736 videocontext->display_height = 0;
741 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
742 videocontext->fourcc = 0;
744 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
745 * data and other settings
749 /* extract codec_data, may turn out needed */
750 value = gst_structure_get_value (structure, "codec_data");
752 codec_buf = gst_value_get_buffer (value);
755 if (!strcmp (mimetype, "video/x-raw-yuv")) {
756 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
757 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
758 } else if (!strcmp (mimetype, "image/jpeg")) {
759 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
760 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
761 ||!strcmp (mimetype, "video/x-huffyuv")
762 || !strcmp (mimetype, "video/x-divx")
763 || !strcmp (mimetype, "video/x-dv")
764 || !strcmp (mimetype, "video/x-h263")
765 || !strcmp (mimetype, "video/x-msmpeg")
766 || !strcmp (mimetype, "video/x-wmv")) {
767 gst_riff_strf_vids *bih;
768 gint size = sizeof (gst_riff_strf_vids);
771 if (!strcmp (mimetype, "video/x-xvid"))
772 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
773 else if (!strcmp (mimetype, "video/x-huffyuv"))
774 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
775 else if (!strcmp (mimetype, "video/x-dv"))
776 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
777 else if (!strcmp (mimetype, "video/x-h263"))
778 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
779 else if (!strcmp (mimetype, "video/x-divx")) {
782 gst_structure_get_int (structure, "divxversion", &divxversion);
783 switch (divxversion) {
785 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
788 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
791 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
794 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
797 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
798 switch (msmpegversion) {
800 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
803 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
809 } else if (!strcmp (mimetype, "video/x-wmv")) {
812 if (gst_structure_get_fourcc (structure, "format", &format)) {
814 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
815 if (wmvversion == 2) {
816 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
817 } else if (wmvversion == 1) {
818 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
819 } else if (wmvversion == 3) {
820 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
828 bih = g_new0 (gst_riff_strf_vids, 1);
829 GST_WRITE_UINT32_LE (&bih->size, size);
830 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
831 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
832 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
833 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
834 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
835 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
836 videocontext->pixel_height * 3);
838 /* process codec private/initialization data, if any */
840 size += GST_BUFFER_SIZE (codec_buf);
841 bih = g_realloc (bih, size);
842 GST_WRITE_UINT32_LE (&bih->size, size);
843 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
844 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
847 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
848 context->codec_priv = (gpointer) bih;
849 context->codec_priv_size = size;
850 } else if (!strcmp (mimetype, "video/x-h264")) {
851 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
853 if (context->codec_priv != NULL) {
854 g_free (context->codec_priv);
855 context->codec_priv = NULL;
856 context->codec_priv_size = 0;
859 /* Create avcC header */
860 if (codec_buf != NULL) {
861 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
862 context->codec_priv = g_malloc0 (context->codec_priv_size);
863 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
864 context->codec_priv_size);
866 } else if (!strcmp (mimetype, "video/x-theora")) {
867 const GValue *streamheader;
869 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
871 if (context->codec_priv != NULL) {
872 g_free (context->codec_priv);
873 context->codec_priv = NULL;
874 context->codec_priv_size = 0;
877 streamheader = gst_structure_get_value (structure, "streamheader");
878 if (!theora_streamheader_to_codecdata (streamheader, context)) {
879 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
880 ("theora stream headers missing or malformed"));
883 } else if (!strcmp (mimetype, "video/x-dirac")) {
884 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
885 } else if (!strcmp (mimetype, "video/x-vp8")) {
886 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
887 } else if (!strcmp (mimetype, "video/mpeg")) {
890 gst_structure_get_int (structure, "mpegversion", &mpegversion);
891 switch (mpegversion) {
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
896 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
905 /* global headers may be in codec data */
906 if (codec_buf != NULL) {
907 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
908 context->codec_priv = g_malloc0 (context->codec_priv_size);
909 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
910 context->codec_priv_size);
912 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
914 /* can only make it here if preceding case verified it was version 3 */
915 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
916 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
918 const GValue *mdpr_data;
920 gst_structure_get_int (structure, "rmversion", &rmversion);
923 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
926 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
929 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
932 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
938 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
939 if (mdpr_data != NULL) {
940 guint8 *priv_data = NULL;
941 guint priv_data_size = 0;
943 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
945 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
946 priv_data = g_malloc0 (priv_data_size);
948 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
950 context->codec_priv = priv_data;
951 context->codec_priv_size = priv_data_size;
960 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
961 GST_PAD_NAME (pad), caps);
966 /* N > 0 to expect a particular number of headers, negative if the
967 number of headers is variable */
969 xiphN_streamheader_to_codecdata (const GValue * streamheader,
970 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
972 GstBuffer **buf = NULL;
975 guint bufi, i, offset, priv_data_size;
977 if (streamheader == NULL)
978 goto no_stream_headers;
980 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
983 bufarr = g_value_peek_pointer (streamheader);
984 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
986 if (N > 0 && bufarr->len != N)
989 context->xiph_headers_to_skip = bufarr->len;
991 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
992 for (i = 0; i < bufarr->len; i++) {
993 GValue *bufval = &g_array_index (bufarr, GValue, i);
995 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
997 goto wrong_content_type;
1000 buf[i] = g_value_peek_pointer (bufval);
1004 if (bufarr->len > 0) {
1005 for (i = 0; i < bufarr->len - 1; i++) {
1006 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1010 for (i = 0; i < bufarr->len; ++i) {
1011 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1014 priv_data = g_malloc0 (priv_data_size);
1016 priv_data[0] = bufarr->len - 1;
1019 if (bufarr->len > 0) {
1020 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1021 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1022 priv_data[offset++] = 0xff;
1024 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1028 for (i = 0; i < bufarr->len; ++i) {
1029 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1030 GST_BUFFER_SIZE (buf[i]));
1031 offset += GST_BUFFER_SIZE (buf[i]);
1034 context->codec_priv = priv_data;
1035 context->codec_priv_size = priv_data_size;
1038 *p_buf0 = gst_buffer_ref (buf[0]);
1047 GST_WARNING ("required streamheaders missing in sink caps!");
1052 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1053 G_VALUE_TYPE_NAME (streamheader));
1058 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1063 GST_WARNING ("streamheaders array does not contain GstBuffers");
1069 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1070 GstMatroskaTrackContext * context)
1072 GstBuffer *buf0 = NULL;
1074 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1077 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1078 GST_WARNING ("First vorbis header too small, ignoring");
1080 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1081 GstMatroskaTrackAudioContext *audiocontext;
1084 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1085 audiocontext = (GstMatroskaTrackAudioContext *) context;
1086 audiocontext->channels = GST_READ_UINT8 (hdr);
1087 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1092 gst_buffer_unref (buf0);
1098 theora_streamheader_to_codecdata (const GValue * streamheader,
1099 GstMatroskaTrackContext * context)
1101 GstBuffer *buf0 = NULL;
1103 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1106 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1107 GST_WARNING ("First theora header too small, ignoring");
1108 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1109 GST_WARNING ("First header not a theora identification header, ignoring");
1111 GstMatroskaTrackVideoContext *videocontext;
1112 guint fps_num, fps_denom, par_num, par_denom;
1115 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1117 videocontext = (GstMatroskaTrackVideoContext *) context;
1118 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1119 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1120 hdr += 3 + 3 + 1 + 1;
1121 fps_num = GST_READ_UINT32_BE (hdr);
1122 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1123 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1124 fps_denom, fps_num);
1126 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1127 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1128 if (par_num > 0 && par_num > 0) {
1129 if (par_num > par_denom) {
1130 videocontext->display_width =
1131 videocontext->pixel_width * par_num / par_denom;
1132 videocontext->display_height = videocontext->pixel_height;
1133 } else if (par_num < par_denom) {
1134 videocontext->display_width = videocontext->pixel_width;
1135 videocontext->display_height =
1136 videocontext->pixel_height * par_denom / par_num;
1138 videocontext->display_width = 0;
1139 videocontext->display_height = 0;
1142 videocontext->display_width = 0;
1143 videocontext->display_height = 0;
1149 gst_buffer_unref (buf0);
1155 kate_streamheader_to_codecdata (const GValue * streamheader,
1156 GstMatroskaTrackContext * context)
1158 GstBuffer *buf0 = NULL;
1160 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1163 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1164 GST_WARNING ("First kate header too small, ignoring");
1165 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1166 GST_WARNING ("First header not a kate identification header, ignoring");
1170 gst_buffer_unref (buf0);
1176 flac_streamheader_to_codecdata (const GValue * streamheader,
1177 GstMatroskaTrackContext * context)
1184 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1185 GST_WARNING ("No or invalid streamheader field in the caps");
1189 bufarr = g_value_peek_pointer (streamheader);
1190 if (bufarr->len < 2) {
1191 GST_WARNING ("Too few headers in streamheader field");
1195 context->xiph_headers_to_skip = bufarr->len + 1;
1197 bufval = &g_array_index (bufarr, GValue, 0);
1198 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1199 GST_WARNING ("streamheaders array does not contain GstBuffers");
1203 buffer = g_value_peek_pointer (bufval);
1205 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1206 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1207 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1208 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1209 GST_WARNING ("Invalid streamheader for FLAC");
1213 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1214 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1215 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1216 GST_BUFFER_SIZE (buffer) - 9);
1218 for (i = 1; i < bufarr->len; i++) {
1219 bufval = &g_array_index (bufarr, GValue, i);
1221 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1222 g_free (context->codec_priv);
1223 context->codec_priv = NULL;
1224 context->codec_priv_size = 0;
1225 GST_WARNING ("streamheaders array does not contain GstBuffers");
1229 buffer = g_value_peek_pointer (bufval);
1231 context->codec_priv =
1232 g_realloc (context->codec_priv,
1233 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1234 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1235 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1236 context->codec_priv_size =
1237 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1244 speex_streamheader_to_codecdata (const GValue * streamheader,
1245 GstMatroskaTrackContext * context)
1251 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1252 GST_WARNING ("No or invalid streamheader field in the caps");
1256 bufarr = g_value_peek_pointer (streamheader);
1257 if (bufarr->len != 2) {
1258 GST_WARNING ("Too few headers in streamheader field");
1262 context->xiph_headers_to_skip = bufarr->len + 1;
1264 bufval = &g_array_index (bufarr, GValue, 0);
1265 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1266 GST_WARNING ("streamheaders array does not contain GstBuffers");
1270 buffer = g_value_peek_pointer (bufval);
1272 if (GST_BUFFER_SIZE (buffer) < 80
1273 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1274 GST_WARNING ("Invalid streamheader for Speex");
1278 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1279 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1280 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1281 GST_BUFFER_SIZE (buffer));
1283 bufval = &g_array_index (bufarr, GValue, 1);
1285 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1286 g_free (context->codec_priv);
1287 context->codec_priv = NULL;
1288 context->codec_priv_size = 0;
1289 GST_WARNING ("streamheaders array does not contain GstBuffers");
1293 buffer = g_value_peek_pointer (bufval);
1295 context->codec_priv =
1296 g_realloc (context->codec_priv,
1297 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1298 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1299 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1300 context->codec_priv_size =
1301 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1306 static const gchar *
1307 aac_codec_data_to_codec_id (const GstBuffer * buf)
1309 const gchar *result;
1312 /* default to MAIN */
1315 if (GST_BUFFER_SIZE (buf) >= 2) {
1316 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1334 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1343 * gst_matroska_mux_audio_pad_setcaps:
1344 * @pad: Pad which got the caps.
1347 * Setcaps function for audio sink pad.
1349 * Returns: #TRUE on success.
1352 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1354 GstMatroskaTrackContext *context = NULL;
1355 GstMatroskaTrackAudioContext *audiocontext;
1356 GstMatroskaMux *mux;
1357 GstMatroskaPad *collect_pad;
1358 const gchar *mimetype;
1359 gint samplerate = 0, channels = 0;
1360 GstStructure *structure;
1361 const GValue *codec_data = NULL;
1362 const GstBuffer *buf = NULL;
1363 const gchar *stream_format = NULL;
1365 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1368 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1369 g_assert (collect_pad);
1370 context = collect_pad->track;
1372 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1373 audiocontext = (GstMatroskaTrackAudioContext *) context;
1375 structure = gst_caps_get_structure (caps, 0);
1376 mimetype = gst_structure_get_name (structure);
1379 gst_structure_get_int (structure, "rate", &samplerate);
1380 gst_structure_get_int (structure, "channels", &channels);
1382 audiocontext->samplerate = samplerate;
1383 audiocontext->channels = channels;
1384 audiocontext->bitdepth = 0;
1385 context->default_duration = 0;
1387 codec_data = gst_structure_get_value (structure, "codec_data");
1389 buf = gst_value_get_buffer (codec_data);
1391 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1392 * data and other settings
1396 if (!strcmp (mimetype, "audio/mpeg")) {
1397 gint mpegversion = 0;
1399 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1400 switch (mpegversion) {
1406 gst_structure_get_int (structure, "layer", &layer);
1408 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1409 GST_WARNING_OBJECT (mux,
1410 "Unable to determine MPEG audio version, assuming 1");
1416 else if (layer == 2)
1418 else if (version == 2)
1423 context->default_duration =
1424 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1428 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1431 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1434 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1443 stream_format = gst_structure_get_string (structure, "stream-format");
1444 /* check this is raw aac */
1445 if (stream_format) {
1446 if (strcmp (stream_format, "raw") != 0) {
1447 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1451 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1456 if (mpegversion == 2)
1458 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1459 aac_codec_data_to_codec_id (buf));
1460 else if (mpegversion == 4)
1462 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1463 aac_codec_data_to_codec_id (buf));
1465 g_assert_not_reached ();
1467 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1474 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1476 gint endianness = G_LITTLE_ENDIAN;
1477 gboolean signedness = TRUE;
1479 if (!gst_structure_get_int (structure, "width", &width) ||
1480 !gst_structure_get_int (structure, "depth", &depth) ||
1481 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1482 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1487 !gst_structure_get_int (structure, "endianness", &endianness)) {
1488 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1492 if (width != depth) {
1493 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1497 /* FIXME: where is this spec'ed out? (tpm) */
1498 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1499 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1503 audiocontext->bitdepth = depth;
1504 if (endianness == G_BIG_ENDIAN)
1505 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1507 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1509 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1512 if (!gst_structure_get_int (structure, "width", &width)) {
1513 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1517 audiocontext->bitdepth = width;
1518 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1520 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1521 const GValue *streamheader;
1523 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1525 if (context->codec_priv != NULL) {
1526 g_free (context->codec_priv);
1527 context->codec_priv = NULL;
1528 context->codec_priv_size = 0;
1531 streamheader = gst_structure_get_value (structure, "streamheader");
1532 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1533 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1534 ("vorbis stream headers missing or malformed"));
1537 } else if (!strcmp (mimetype, "audio/x-flac")) {
1538 const GValue *streamheader;
1540 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1541 if (context->codec_priv != NULL) {
1542 g_free (context->codec_priv);
1543 context->codec_priv = NULL;
1544 context->codec_priv_size = 0;
1547 streamheader = gst_structure_get_value (structure, "streamheader");
1548 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1549 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1550 ("flac stream headers missing or malformed"));
1553 } else if (!strcmp (mimetype, "audio/x-speex")) {
1554 const GValue *streamheader;
1556 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1557 if (context->codec_priv != NULL) {
1558 g_free (context->codec_priv);
1559 context->codec_priv = NULL;
1560 context->codec_priv_size = 0;
1563 streamheader = gst_structure_get_value (structure, "streamheader");
1564 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1565 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1566 ("speex stream headers missing or malformed"));
1569 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1570 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1571 } else if (!strcmp (mimetype, "audio/x-tta")) {
1574 /* TTA frame duration */
1575 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1577 gst_structure_get_int (structure, "width", &width);
1578 audiocontext->bitdepth = width;
1579 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1581 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1583 const GValue *mdpr_data;
1585 gst_structure_get_int (structure, "raversion", &raversion);
1586 switch (raversion) {
1588 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1591 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1594 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1600 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1601 if (mdpr_data != NULL) {
1602 guint8 *priv_data = NULL;
1603 guint priv_data_size = 0;
1605 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1607 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1608 priv_data = g_malloc0 (priv_data_size);
1610 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1612 context->codec_priv = priv_data;
1613 context->codec_priv_size = priv_data_size;
1616 } else if (!strcmp (mimetype, "audio/x-wma")) {
1618 guint codec_priv_size;
1625 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1626 || !gst_structure_get_int (structure, "block_align", &block_align)
1627 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1628 || samplerate == 0 || channels == 0) {
1629 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1630 "channels/rate on WMA caps");
1634 switch (wmaversion) {
1636 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1639 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1642 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1645 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1649 if (gst_structure_get_int (structure, "depth", &depth))
1650 audiocontext->bitdepth = depth;
1652 codec_priv_size = WAVEFORMATEX_SIZE;
1654 codec_priv_size += GST_BUFFER_SIZE (buf);
1656 /* serialize waveformatex structure */
1657 codec_priv = g_malloc0 (codec_priv_size);
1658 GST_WRITE_UINT16_LE (codec_priv, format);
1659 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1660 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1661 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1662 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1663 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1665 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1667 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1669 /* process codec private/initialization data, if any */
1671 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1672 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1675 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1676 context->codec_priv = (gpointer) codec_priv;
1677 context->codec_priv_size = codec_priv_size;
1685 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1686 GST_PAD_NAME (pad), caps);
1693 * gst_matroska_mux_subtitle_pad_setcaps:
1694 * @pad: Pad which got the caps.
1697 * Setcaps function for subtitle sink pad.
1699 * Returns: #TRUE on success.
1702 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1705 * Consider this as boilerplate code for now. There is
1706 * no single subtitle creation element in GStreamer,
1707 * neither do I know how subtitling works at all. */
1709 /* There is now (at least) one such alement (kateenc), and I'm going
1710 to handle it here and claim it works when it can be piped back
1711 through GStreamer and VLC */
1713 GstMatroskaTrackContext *context = NULL;
1714 GstMatroskaTrackSubtitleContext *scontext;
1715 GstMatroskaMux *mux;
1716 GstMatroskaPad *collect_pad;
1717 const gchar *mimetype;
1718 GstStructure *structure;
1720 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1723 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1724 g_assert (collect_pad);
1725 context = collect_pad->track;
1727 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1728 scontext = (GstMatroskaTrackSubtitleContext *) context;
1730 structure = gst_caps_get_structure (caps, 0);
1731 mimetype = gst_structure_get_name (structure);
1734 scontext->check_utf8 = 1;
1735 scontext->invalid_utf8 = 0;
1736 context->default_duration = 0;
1738 /* TODO: - other format than Kate */
1740 if (!strcmp (mimetype, "subtitle/x-kate")) {
1741 const GValue *streamheader;
1743 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1745 if (context->codec_priv != NULL) {
1746 g_free (context->codec_priv);
1747 context->codec_priv = NULL;
1748 context->codec_priv_size = 0;
1751 streamheader = gst_structure_get_value (structure, "streamheader");
1752 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1753 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1754 ("kate stream headers missing or malformed"));
1765 * gst_matroska_mux_request_new_pad:
1766 * @element: #GstMatroskaMux.
1767 * @templ: #GstPadTemplate.
1768 * @pad_name: New pad name.
1770 * Request pad function for sink templates.
1772 * Returns: New #GstPad.
1775 gst_matroska_mux_request_new_pad (GstElement * element,
1776 GstPadTemplate * templ, const gchar * pad_name)
1778 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1779 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1780 GstMatroskaPad *collect_pad;
1781 GstPad *newpad = NULL;
1783 GstPadSetCapsFunction setcapsfunc = NULL;
1784 GstMatroskaTrackContext *context = NULL;
1786 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1787 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1788 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1789 context = (GstMatroskaTrackContext *)
1790 g_new0 (GstMatroskaTrackAudioContext, 1);
1791 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1792 context->name = g_strdup ("Audio");
1793 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1794 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1795 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1796 context = (GstMatroskaTrackContext *)
1797 g_new0 (GstMatroskaTrackVideoContext, 1);
1798 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1799 context->name = g_strdup ("Video");
1800 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1801 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1802 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1803 context = (GstMatroskaTrackContext *)
1804 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1805 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1806 context->name = g_strdup ("Subtitle");
1808 GST_WARNING_OBJECT (mux, "This is not our template!");
1812 newpad = gst_pad_new_from_template (templ, name);
1814 collect_pad = (GstMatroskaPad *)
1815 gst_collect_pads_add_pad_full (mux->collect, newpad,
1816 sizeof (GstMatroskaPad),
1817 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1819 collect_pad->track = context;
1820 gst_matroska_pad_reset (collect_pad, FALSE);
1822 /* FIXME: hacked way to override/extend the event function of
1823 * GstCollectPads; because it sets its own event function giving the
1824 * element no access to events.
1825 * TODO GstCollectPads should really give its 'users' a clean chance to
1826 * properly handle events that are not meant for collectpads itself.
1827 * Perhaps a callback or so, though rejected (?) in #340060.
1828 * This would allow (clean) transcoding of info from demuxer/streams
1829 * to another muxer */
1830 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1831 gst_pad_set_event_function (newpad,
1832 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1834 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1835 gst_pad_set_active (newpad, TRUE);
1836 gst_element_add_pad (element, newpad);
1843 * gst_matroska_mux_release_pad:
1844 * @element: #GstMatroskaMux.
1845 * @pad: Pad to release.
1847 * Release a previously requested pad.
1850 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1852 GstMatroskaMux *mux;
1855 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1857 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1858 GstCollectData *cdata = (GstCollectData *) walk->data;
1859 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1861 if (cdata->pad == pad) {
1862 GstClockTime min_dur; /* observed minimum duration */
1864 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1865 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1866 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1867 if (collect_pad->duration < min_dur)
1868 collect_pad->duration = min_dur;
1871 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1872 mux->duration < collect_pad->duration)
1873 mux->duration = collect_pad->duration;
1879 gst_collect_pads_remove_pad (mux->collect, pad);
1880 if (gst_element_remove_pad (element, pad))
1886 * gst_matroska_mux_track_header:
1887 * @mux: #GstMatroskaMux
1888 * @context: Tack context.
1890 * Write a track header.
1893 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1894 GstMatroskaTrackContext * context)
1896 GstEbmlWrite *ebml = mux->ebml_write;
1899 /* TODO: check if everything necessary is written and check default values */
1901 /* track type goes before the type-specific stuff */
1902 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1903 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1905 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1906 gst_matroska_mux_create_uid ());
1907 if (context->default_duration) {
1908 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1909 context->default_duration);
1911 if (context->language) {
1912 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1916 /* type-specific stuff */
1917 switch (context->type) {
1918 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1919 GstMatroskaTrackVideoContext *videocontext =
1920 (GstMatroskaTrackVideoContext *) context;
1922 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1923 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1924 videocontext->pixel_width);
1925 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1926 videocontext->pixel_height);
1927 if (videocontext->display_width && videocontext->display_height) {
1928 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1929 videocontext->display_width);
1930 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1931 videocontext->display_height);
1933 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1934 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1935 if (videocontext->fourcc) {
1936 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1938 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1939 (gpointer) & fcc_le, 4);
1941 gst_ebml_write_master_finish (ebml, master);
1946 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1947 GstMatroskaTrackAudioContext *audiocontext =
1948 (GstMatroskaTrackAudioContext *) context;
1950 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1951 if (audiocontext->samplerate != 8000)
1952 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1953 audiocontext->samplerate);
1954 if (audiocontext->channels != 1)
1955 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1956 audiocontext->channels);
1957 if (audiocontext->bitdepth) {
1958 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1959 audiocontext->bitdepth);
1961 gst_ebml_write_master_finish (ebml, master);
1967 /* doesn't need type-specific data */
1971 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1972 if (context->codec_priv)
1973 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1974 context->codec_priv, context->codec_priv_size);
1975 /* FIXME: until we have a nice way of getting the codecname
1976 * out of the caps, I'm not going to enable this. Too much
1977 * (useless, double, boring) work... */
1978 /* TODO: Use value from tags if any */
1979 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1980 context->codec_name); */
1981 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1986 * gst_matroska_mux_start:
1987 * @mux: #GstMatroskaMux
1989 * Start a new matroska file (write headers etc...)
1992 gst_matroska_mux_start (GstMatroskaMux * mux)
1994 GstEbmlWrite *ebml = mux->ebml_write;
1995 const gchar *doctype;
1996 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1997 GST_MATROSKA_ID_TRACKS,
1998 GST_MATROSKA_ID_CUES,
1999 GST_MATROSKA_ID_TAGS,
2002 guint64 master, child;
2006 GstClockTime duration = 0;
2007 guint32 segment_uid[4];
2008 GTimeVal time = { 0, 0 };
2010 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2011 ebml->caps = gst_caps_from_string ("video/webm");
2013 ebml->caps = gst_caps_from_string ("video/x-matroska");
2015 /* we start with a EBML header */
2016 doctype = mux->doctype;
2017 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2018 doctype, mux->doctype_version);
2019 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2021 /* the rest of the header is cached */
2022 gst_ebml_write_set_cache (ebml, 0x1000);
2024 /* start a segment */
2026 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2027 mux->segment_master = ebml->pos;
2030 /* seekhead (table of contents) - we set the positions later */
2031 mux->seekhead_pos = ebml->pos;
2032 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2033 for (i = 0; seekhead_id[i] != 0; i++) {
2034 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2035 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2037 gst_ebml_write_master_finish (ebml, child);
2039 gst_ebml_write_master_finish (ebml, master);
2043 mux->info_pos = ebml->pos;
2044 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2045 for (i = 0; i < 4; i++) {
2046 segment_uid[i] = g_random_int ();
2048 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2049 (guint8 *) segment_uid, 16);
2050 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2051 mux->duration_pos = ebml->pos;
2054 for (collected = mux->collect->data; collected;
2055 collected = g_slist_next (collected)) {
2056 GstMatroskaPad *collect_pad;
2057 GstFormat format = GST_FORMAT_TIME;
2059 gint64 trackduration;
2061 collect_pad = (GstMatroskaPad *) collected->data;
2062 thepad = collect_pad->collect.pad;
2064 /* Query the total length of the track. */
2065 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2066 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2067 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2068 GST_TIME_ARGS (trackduration));
2069 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2070 duration = (GstClockTime) trackduration;
2074 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2075 gst_guint64_to_gdouble (duration) /
2076 gst_guint64_to_gdouble (mux->time_scale));
2078 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2079 "GStreamer plugin version " PACKAGE_VERSION);
2080 if (mux->writing_app && mux->writing_app[0]) {
2081 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2083 g_get_current_time (&time);
2084 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2085 gst_ebml_write_master_finish (ebml, master);
2088 mux->tracks_pos = ebml->pos;
2089 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2091 for (collected = mux->collect->data; collected;
2092 collected = g_slist_next (collected)) {
2093 GstMatroskaPad *collect_pad;
2096 collect_pad = (GstMatroskaPad *) collected->data;
2097 thepad = collect_pad->collect.pad;
2099 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2100 collect_pad->track->codec_id != 0) {
2101 collect_pad->track->num = tracknum++;
2102 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2103 gst_matroska_mux_track_header (mux, collect_pad->track);
2104 gst_ebml_write_master_finish (ebml, child);
2107 gst_ebml_write_master_finish (ebml, master);
2109 /* lastly, flush the cache */
2110 gst_ebml_write_flush_cache (ebml, FALSE);
2114 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2117 /* TODO: more sensible tag mappings */
2120 const gchar *matroska_tagname;
2121 const gchar *gstreamer_tagname;
2125 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2126 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2127 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2128 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2129 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2130 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2131 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2132 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2133 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2134 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2135 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2136 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2137 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2138 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2139 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2141 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2143 guint64 simpletag_master;
2145 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2146 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2147 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2149 if (strcmp (tagname_gst, tag) == 0) {
2150 GValue src = { 0, };
2153 if (!gst_tag_list_copy_value (&src, list, tag))
2155 if ((dest = gst_value_serialize (&src))) {
2157 simpletag_master = gst_ebml_write_master_start (ebml,
2158 GST_MATROSKA_ID_SIMPLETAG);
2159 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2160 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2161 gst_ebml_write_master_finish (ebml, simpletag_master);
2164 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2166 g_value_unset (&src);
2174 * gst_matroska_mux_finish:
2175 * @mux: #GstMatroskaMux
2177 * Finish a new matroska file (write index etc...)
2180 gst_matroska_mux_finish (GstMatroskaMux * mux)
2182 GstEbmlWrite *ebml = mux->ebml_write;
2184 guint64 duration = 0;
2186 const GstTagList *tags;
2188 /* finish last cluster */
2190 gst_ebml_write_master_finish (ebml, mux->cluster);
2194 if (mux->index != NULL && mux->indexed) {
2196 guint64 master, pointentry_master, trackpos_master;
2198 mux->cues_pos = ebml->pos;
2199 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2200 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2202 for (n = 0; n < mux->num_indexes; n++) {
2203 GstMatroskaIndex *idx = &mux->index[n];
2205 pointentry_master = gst_ebml_write_master_start (ebml,
2206 GST_MATROSKA_ID_POINTENTRY);
2207 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2208 idx->time / mux->time_scale);
2209 trackpos_master = gst_ebml_write_master_start (ebml,
2210 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2211 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2212 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2213 idx->pos - mux->segment_master);
2214 gst_ebml_write_master_finish (ebml, trackpos_master);
2215 gst_ebml_write_master_finish (ebml, pointentry_master);
2218 gst_ebml_write_master_finish (ebml, master);
2219 gst_ebml_write_flush_cache (ebml, FALSE);
2223 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2225 if (tags != NULL && !gst_tag_list_is_empty (tags) && mux->indexed) {
2226 guint64 master_tags, master_tag;
2228 GST_DEBUG ("Writing tags");
2230 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2231 mux->tags_pos = ebml->pos;
2232 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2233 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2234 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2235 gst_ebml_write_master_finish (ebml, master_tag);
2236 gst_ebml_write_master_finish (ebml, master_tags);
2239 /* update seekhead. We know that:
2240 * - a seekhead contains 4 entries.
2241 * - order of entries is as above.
2242 * - a seekhead has a 4-byte header + 8-byte length
2243 * - each entry is 2-byte master, 2-byte ID pointer,
2244 * 2-byte length pointer, all 8/1-byte length, 4-
2245 * byte ID and 8-byte length pointer, where the
2246 * length pointer starts at 20.
2247 * - all entries are local to the segment (so pos - segment_master).
2248 * - so each entry is at 12 + 20 + num * 28. */
2250 GST_DEBUG_OBJECT (mux, "not live");
2251 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2252 mux->info_pos - mux->segment_master);
2253 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2254 mux->tracks_pos - mux->segment_master);
2255 if (mux->index != NULL) {
2256 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2257 mux->cues_pos - mux->segment_master);
2260 guint64 my_pos = ebml->pos;
2262 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2263 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2264 gst_ebml_write_seek (ebml, my_pos);
2267 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2268 mux->tags_pos - mux->segment_master);
2271 guint64 my_pos = ebml->pos;
2273 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2274 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2275 gst_ebml_write_seek (ebml, my_pos);
2278 /* update duration */
2279 /* first get the overall duration */
2280 /* a released track may have left a duration in here */
2281 duration = mux->duration;
2282 for (collected = mux->collect->data; collected;
2283 collected = g_slist_next (collected)) {
2284 GstMatroskaPad *collect_pad;
2285 GstClockTime min_duration; /* observed minimum duration */
2287 collect_pad = (GstMatroskaPad *) collected->data;
2289 GST_DEBUG_OBJECT (mux,
2290 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2291 " end ts %" GST_TIME_FORMAT, collect_pad,
2292 GST_TIME_ARGS (collect_pad->start_ts),
2293 GST_TIME_ARGS (collect_pad->end_ts));
2295 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2296 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2298 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2299 if (collect_pad->duration < min_duration)
2300 collect_pad->duration = min_duration;
2301 GST_DEBUG_OBJECT (collect_pad,
2302 "final track duration: %" GST_TIME_FORMAT,
2303 GST_TIME_ARGS (collect_pad->duration));
2306 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2307 duration < collect_pad->duration)
2308 duration = collect_pad->duration;
2310 if (duration != 0) {
2311 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2312 GST_TIME_ARGS (duration));
2313 pos = mux->ebml_write->pos;
2314 gst_ebml_write_seek (ebml, mux->duration_pos);
2315 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2316 gst_guint64_to_gdouble (duration) /
2317 gst_guint64_to_gdouble (mux->time_scale));
2318 gst_ebml_write_seek (ebml, pos);
2321 guint64 my_pos = ebml->pos;
2323 gst_ebml_write_seek (ebml, mux->duration_pos);
2324 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2325 gst_ebml_write_seek (ebml, my_pos);
2328 GST_DEBUG_OBJECT (mux, "finishing segment");
2329 /* finish segment - this also writes element length */
2330 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2335 * gst_matroska_mux_best_pad:
2336 * @mux: #GstMatroskaMux
2337 * @popped: True if at least one buffer was popped from #GstCollectPads
2339 * Find a pad with the oldest data
2340 * (data from this pad should be written first).
2342 * Returns: Selected pad.
2344 static GstMatroskaPad *
2345 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2348 GstMatroskaPad *best = NULL;
2351 for (collected = mux->collect->data; collected;
2352 collected = g_slist_next (collected)) {
2353 GstMatroskaPad *collect_pad;
2355 collect_pad = (GstMatroskaPad *) collected->data;
2356 /* fetch a new buffer if needed */
2357 if (collect_pad->buffer == NULL) {
2358 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2359 (GstCollectData *) collect_pad);
2361 if (collect_pad->buffer != NULL)
2365 /* if we have a buffer check if it is better then the current best one */
2366 if (collect_pad->buffer != NULL) {
2367 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2368 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2369 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2370 GST_BUFFER_TIMESTAMP (best->buffer))) {
2380 * gst_matroska_mux_buffer_header:
2381 * @track: Track context.
2382 * @relative_timestamp: relative timestamp of the buffer
2383 * @flags: Buffer flags.
2385 * Create a buffer containing buffer header.
2387 * Returns: New buffer.
2390 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2391 gint16 relative_timestamp, int flags)
2395 hdr = gst_buffer_new_and_alloc (4);
2396 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2397 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2398 /* time relative to clustertime */
2399 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2402 GST_BUFFER_DATA (hdr)[3] = flags;
2407 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2408 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2409 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2412 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2413 GstMatroskaPad * collect_pad, GstBuffer * buf)
2415 GstMatroskaTrackVideoContext *ctx =
2416 (GstMatroskaTrackVideoContext *) collect_pad->track;
2417 const guint8 *data = GST_BUFFER_DATA (buf);
2418 guint size = GST_BUFFER_SIZE (buf);
2420 guint32 next_parse_offset;
2421 GstBuffer *ret = NULL;
2422 gboolean is_muxing_unit = FALSE;
2424 if (GST_BUFFER_SIZE (buf) < 13) {
2425 gst_buffer_unref (buf);
2429 /* Check if this buffer contains a picture or end-of-sequence packet */
2430 while (size >= 13) {
2431 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2432 gst_buffer_unref (buf);
2436 parse_code = GST_READ_UINT8 (data + 4);
2437 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2438 if (ctx->dirac_unit) {
2439 gst_buffer_unref (ctx->dirac_unit);
2440 ctx->dirac_unit = NULL;
2442 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2443 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2444 is_muxing_unit = TRUE;
2448 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2450 if (G_UNLIKELY (next_parse_offset == 0))
2453 data += next_parse_offset;
2454 size -= next_parse_offset;
2457 if (ctx->dirac_unit)
2458 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2460 ctx->dirac_unit = gst_buffer_ref (buf);
2462 if (is_muxing_unit) {
2463 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2464 ctx->dirac_unit = NULL;
2465 gst_buffer_copy_metadata (ret, buf,
2466 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2467 GST_BUFFER_COPY_CAPS);
2468 gst_buffer_unref (buf);
2470 gst_buffer_unref (buf);
2478 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
2482 GValue streamheader = { 0 };
2483 GValue bufval = { 0 };
2484 GstBuffer *streamheader_buffer;
2485 GstEbmlWrite *ebml = mux->ebml_write;
2487 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
2488 if (!strcmp (mux->doctype, GST_MATROSKA_DOCTYPE_WEBM)) {
2489 caps = gst_caps_from_string ("video/webm");
2491 caps = gst_caps_from_string ("video/x-matroska");
2493 s = gst_caps_get_structure (caps, 0);
2494 g_value_init (&streamheader, GST_TYPE_ARRAY);
2495 g_value_init (&bufval, GST_TYPE_BUFFER);
2496 gst_value_set_buffer (&bufval, streamheader_buffer);
2497 gst_value_array_append_value (&streamheader, &bufval);
2498 g_value_unset (&bufval);
2499 gst_structure_set_value (s, "streamheader", &streamheader);
2500 g_value_unset (&streamheader);
2501 gst_caps_unref (ebml->caps);
2506 * gst_matroska_mux_write_data:
2507 * @mux: #GstMatroskaMux
2508 * @collect_pad: #GstMatroskaPad with the data
2510 * Write collected data (called from gst_matroska_mux_collected).
2512 * Returns: Result of the gst_pad_push issued to write the data.
2514 static GstFlowReturn
2515 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2517 GstEbmlWrite *ebml = mux->ebml_write;
2518 GstBuffer *buf, *hdr;
2520 gboolean write_duration;
2521 gint16 relative_timestamp;
2522 gint64 relative_timestamp64;
2523 guint64 block_duration;
2524 gboolean is_video_keyframe = FALSE;
2527 buf = collect_pad->buffer;
2528 collect_pad->buffer = NULL;
2530 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2531 if (collect_pad->track->xiph_headers_to_skip > 0) {
2532 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2533 gst_buffer_unref (buf);
2534 --collect_pad->track->xiph_headers_to_skip;
2538 /* for dirac we have to queue up everything up to a picture unit */
2539 if (collect_pad->track->codec_id != NULL &&
2540 strcmp (collect_pad->track->codec_id,
2541 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2542 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2547 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2548 * this would wreak havoc with time stored in matroska file */
2549 /* TODO: maybe calculate a timestamp by using the previous timestamp
2550 * and default duration */
2551 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2552 GST_WARNING_OBJECT (collect_pad->collect.pad,
2553 "Invalid buffer timestamp; dropping buffer");
2554 gst_buffer_unref (buf);
2558 /* set the timestamp for outgoing buffers */
2559 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2561 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2562 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2563 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2564 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2565 is_video_keyframe = TRUE;
2569 /* start a new cluster every two seconds or at keyframe */
2570 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2571 || is_video_keyframe) {
2573 gst_ebml_write_master_finish (ebml, mux->cluster);
2574 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2575 mux->cluster_pos = ebml->pos;
2576 gst_ebml_write_set_cache (ebml, 0x20);
2578 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2579 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2580 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2582 GST_WARNING_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
2583 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1,
2585 gst_ebml_write_flush_cache (ebml, TRUE);
2586 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2587 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2588 mux->prev_cluster_size);
2593 mux->cluster_pos = ebml->pos;
2594 gst_ebml_write_set_cache (ebml, 0x20);
2595 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2596 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2597 gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buf), 1, mux->time_scale));
2598 gst_ebml_write_flush_cache (ebml, TRUE);
2599 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2602 /* update duration of this track */
2603 if (GST_BUFFER_DURATION_IS_VALID (buf))
2604 collect_pad->duration += GST_BUFFER_DURATION (buf);
2606 /* We currently write index entries for all video tracks or for the audio
2607 * track in a single-track audio file. This could be improved by keeping the
2608 * index only for the *first* video track. */
2610 /* TODO: index is useful for every track, should contain the number of
2611 * the block in the cluster which contains the timestamp, should also work
2612 * for files with multiple audio tracks.
2614 if (is_video_keyframe ||
2615 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2616 (mux->num_streams == 1))) {
2619 if (mux->min_index_interval != 0) {
2620 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2621 if (mux->index[last_idx].track == collect_pad->track->num)
2626 if (last_idx < 0 || mux->min_index_interval == 0 ||
2627 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2628 >= mux->min_index_interval)) {
2629 GstMatroskaIndex *idx;
2631 if (mux->num_indexes % 32 == 0) {
2632 mux->index = g_renew (GstMatroskaIndex, mux->index,
2633 mux->num_indexes + 32);
2635 idx = &mux->index[mux->num_indexes++];
2637 idx->pos = mux->cluster_pos;
2638 idx->time = GST_BUFFER_TIMESTAMP (buf);
2639 idx->track = collect_pad->track->num;
2643 /* Check if the duration differs from the default duration. */
2644 write_duration = FALSE;
2645 block_duration = GST_BUFFER_DURATION (buf);
2646 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2647 if (block_duration != collect_pad->track->default_duration) {
2648 write_duration = TRUE;
2652 /* write the block, for doctype v2 use SimpleBlock if possible
2653 * one slice (*breath*).
2654 * FIXME: Need to do correct lacing! */
2655 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2656 if (relative_timestamp64 >= 0) {
2657 /* round the timestamp */
2658 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
2660 /* round the timestamp */
2661 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
2663 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
2665 if (mux->doctype_version > 1 && !write_duration) {
2667 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2670 gst_matroska_mux_create_buffer_header (collect_pad->track,
2671 relative_timestamp, flags);
2672 gst_ebml_write_set_cache (ebml, 0x40);
2673 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2674 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2675 gst_ebml_write_buffer (ebml, hdr);
2676 gst_ebml_write_flush_cache (ebml, FALSE);
2677 gst_ebml_write_buffer (ebml, buf);
2679 return gst_ebml_last_write_result (ebml);
2681 gst_ebml_write_set_cache (ebml, GST_BUFFER_SIZE (buf) * 2);
2682 /* write and call order slightly unnatural,
2683 * but avoids seek and minizes pushing */
2684 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2686 gst_matroska_mux_create_buffer_header (collect_pad->track,
2687 relative_timestamp, 0);
2688 if (write_duration) {
2689 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2690 gst_util_uint64_scale (block_duration, 1, mux->time_scale));
2692 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2693 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2694 gst_ebml_write_buffer (ebml, hdr);
2695 gst_ebml_write_master_finish_full (ebml, blockgroup, GST_BUFFER_SIZE (buf));
2696 gst_ebml_write_flush_cache (ebml, FALSE);
2697 gst_ebml_write_buffer (ebml, buf);
2698 return gst_ebml_last_write_result (ebml);
2704 * gst_matroska_mux_collected:
2705 * @pads: #GstCollectPads
2706 * @uuser_data: #GstMatroskaMux
2708 * Collectpads callback.
2710 * Returns: #GstFlowReturn
2712 static GstFlowReturn
2713 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2715 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2716 GstEbmlWrite *ebml = mux->ebml_write;
2717 GstMatroskaPad *best;
2721 GST_DEBUG_OBJECT (mux, "Collected pads");
2723 /* start with a header */
2724 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2725 if (mux->collect->data == NULL) {
2726 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2727 ("No input streams configured"));
2728 return GST_FLOW_ERROR;
2730 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2731 gst_ebml_start_streamheader (ebml);
2732 gst_matroska_mux_start (mux);
2733 gst_matroska_mux_stop_streamheader (mux);
2734 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2738 /* which stream to write from? */
2739 best = gst_matroska_mux_best_pad (mux, &popped);
2741 /* if there is no best pad, we have reached EOS */
2743 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2744 gst_matroska_mux_finish (mux);
2745 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2746 ret = GST_FLOW_UNEXPECTED;
2749 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2750 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2751 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2752 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2754 /* make note of first and last encountered timestamps, so we can calculate
2755 * the actual duration later when we send an updated header on eos */
2756 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2757 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2758 GstClockTime end_ts = start_ts;
2760 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2761 end_ts += GST_BUFFER_DURATION (best->buffer);
2762 else if (best->track->default_duration)
2763 end_ts += best->track->default_duration;
2765 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2766 best->end_ts = end_ts;
2768 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2769 start_ts < best->start_ts))
2770 best->start_ts = start_ts;
2773 /* write one buffer */
2774 ret = gst_matroska_mux_write_data (mux, best);
2775 } while (ret == GST_FLOW_OK && !popped);
2782 * gst_matroska_mux_change_state:
2783 * @element: #GstMatroskaMux
2784 * @transition: State change transition.
2786 * Change the muxer state.
2788 * Returns: #GstStateChangeReturn
2790 static GstStateChangeReturn
2791 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2793 GstStateChangeReturn ret;
2794 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2796 switch (transition) {
2797 case GST_STATE_CHANGE_NULL_TO_READY:
2799 case GST_STATE_CHANGE_READY_TO_PAUSED:
2800 gst_collect_pads_start (mux->collect);
2802 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2804 case GST_STATE_CHANGE_PAUSED_TO_READY:
2805 gst_collect_pads_stop (mux->collect);
2811 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2813 switch (transition) {
2814 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2816 case GST_STATE_CHANGE_PAUSED_TO_READY:
2817 gst_matroska_mux_reset (GST_ELEMENT (mux));
2819 case GST_STATE_CHANGE_READY_TO_NULL:
2829 gst_matroska_mux_set_property (GObject * object,
2830 guint prop_id, const GValue * value, GParamSpec * pspec)
2832 GstMatroskaMux *mux;
2834 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2835 mux = GST_MATROSKA_MUX (object);
2838 case ARG_WRITING_APP:
2839 if (!g_value_get_string (value)) {
2840 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2843 g_free (mux->writing_app);
2844 mux->writing_app = g_value_dup_string (value);
2846 case ARG_DOCTYPE_VERSION:
2847 mux->doctype_version = g_value_get_int (value);
2849 case ARG_MIN_INDEX_INTERVAL:
2850 mux->min_index_interval = g_value_get_int64 (value);
2853 mux->indexed = g_value_get_boolean (value);
2856 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2862 gst_matroska_mux_get_property (GObject * object,
2863 guint prop_id, GValue * value, GParamSpec * pspec)
2865 GstMatroskaMux *mux;
2867 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2868 mux = GST_MATROSKA_MUX (object);
2871 case ARG_WRITING_APP:
2872 g_value_set_string (value, mux->writing_app);
2874 case ARG_DOCTYPE_VERSION:
2875 g_value_set_int (value, mux->doctype_version);
2877 case ARG_MIN_INDEX_INTERVAL:
2878 g_value_set_int64 (value, mux->min_index_interval);
2881 g_value_set_boolean (value, mux->indexed);
2884 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);