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
68 #define DEFAULT_DOCTYPE_VERSION 2
69 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
70 #define DEFAULT_MIN_INDEX_INTERVAL 0
72 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
73 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
75 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
78 GST_STATIC_CAPS ("video/x-matroska")
81 #define COMMON_VIDEO_CAPS \
82 "width = (int) [ 16, 4096 ], " \
83 "height = (int) [ 16, 4096 ], " \
84 "framerate = (fraction) [ 0, MAX ]"
86 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ] "
91 * * require codec data, etc as needed
94 static GstStaticPadTemplate videosink_templ =
95 GST_STATIC_PAD_TEMPLATE ("video_%d",
98 GST_STATIC_CAPS ("video/mpeg, "
99 "mpegversion = (int) { 1, 2, 4 }, "
100 "systemstream = (boolean) false, "
101 COMMON_VIDEO_CAPS "; "
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_NO_FRAMERATE "; "
120 COMMON_VIDEO_CAPS "; "
121 "video/x-pn-realvideo, "
122 "rmversion = (int) [1, 4], "
123 COMMON_VIDEO_CAPS "; "
125 COMMON_VIDEO_CAPS "; "
127 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
128 COMMON_VIDEO_CAPS "; "
129 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
132 #define COMMON_AUDIO_CAPS \
133 "channels = (int) [ 1, MAX ], " \
134 "rate = (int) [ 1, MAX ]"
137 * * require codec data, etc as needed
139 static GstStaticPadTemplate audiosink_templ =
140 GST_STATIC_PAD_TEMPLATE ("audio_%d",
143 GST_STATIC_CAPS ("audio/mpeg, "
144 "mpegversion = (int) 1, "
145 "layer = (int) [ 1, 3 ], "
146 "stream-format = (string) { raw }, "
147 COMMON_AUDIO_CAPS "; "
149 "mpegversion = (int) { 2, 4 }, "
150 COMMON_AUDIO_CAPS "; "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
162 "signed = (boolean) false, "
163 COMMON_AUDIO_CAPS ";"
167 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
168 "signed = (boolean) true, "
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 ";"
182 "audio/x-raw-float, "
183 "width = (int) [ 32, 64 ], "
184 "endianness = (int) LITTLE_ENDIAN, "
185 COMMON_AUDIO_CAPS ";"
187 "width = (int) { 8, 16, 24 }, "
188 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
189 "audio/x-pn-realaudio, "
190 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
191 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
192 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
196 static GstStaticPadTemplate subtitlesink_templ =
197 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
200 GST_STATIC_CAPS_ANY);
202 static GArray *used_uids;
203 G_LOCK_DEFINE_STATIC (used_uids);
205 static void gst_matroska_mux_add_interfaces (GType type);
207 GType gst_matroska_mux_get_type (void);
208 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
209 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
211 /* Matroska muxer destructor */
212 static void gst_matroska_mux_finalize (GObject * object);
214 /* Pads collected callback */
216 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
219 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
221 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
222 GstPadTemplate * templ, const gchar * name);
223 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
225 /* gst internal change state handler */
226 static GstStateChangeReturn
227 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
229 /* gobject bla bla */
230 static void gst_matroska_mux_set_property (GObject * object,
231 guint prop_id, const GValue * value, GParamSpec * pspec);
232 static void gst_matroska_mux_get_property (GObject * object,
233 guint prop_id, GValue * value, GParamSpec * pspec);
236 static void gst_matroska_mux_reset (GstElement * element);
239 static guint64 gst_matroska_mux_create_uid ();
241 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
243 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
253 gst_matroska_mux_add_interfaces (GType type)
255 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
257 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
261 gst_matroska_mux_base_init (gpointer g_class)
266 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
268 GObjectClass *gobject_class;
269 GstElementClass *gstelement_class;
271 gobject_class = (GObjectClass *) klass;
272 gstelement_class = (GstElementClass *) klass;
274 gst_element_class_add_pad_template (gstelement_class,
275 gst_static_pad_template_get (&videosink_templ));
276 gst_element_class_add_pad_template (gstelement_class,
277 gst_static_pad_template_get (&audiosink_templ));
278 gst_element_class_add_pad_template (gstelement_class,
279 gst_static_pad_template_get (&subtitlesink_templ));
280 gst_element_class_add_pad_template (gstelement_class,
281 gst_static_pad_template_get (&src_templ));
282 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
284 "Muxes video/audio/subtitle streams into a matroska stream",
285 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
287 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
290 gobject_class->finalize = gst_matroska_mux_finalize;
292 gobject_class->get_property = gst_matroska_mux_get_property;
293 gobject_class->set_property = gst_matroska_mux_set_property;
295 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
296 g_param_spec_string ("writing-app", "Writing application.",
297 "The name the application that creates the matroska file.",
298 NULL, G_PARAM_READWRITE));
299 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
300 g_param_spec_int ("version", "DocType version",
301 "This parameter determines what Matroska features can be used.",
302 1, 2, DEFAULT_DOCTYPE_VERSION, G_PARAM_READWRITE));
303 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
304 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
305 "entries", "An index entry is created every so many nanoseconds.",
306 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
308 gstelement_class->change_state =
309 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
310 gstelement_class->request_new_pad =
311 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
312 gstelement_class->release_pad =
313 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
318 * gst_matroska_mux_init:
319 * @mux: #GstMatroskaMux that should be initialized.
320 * @g_class: Class of the muxer.
322 * Matroska muxer constructor.
325 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
327 GstPadTemplate *templ;
330 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
331 mux->srcpad = gst_pad_new_from_template (templ, "src");
332 g_object_unref (templ);
334 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
335 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
337 mux->collect = gst_collect_pads_new ();
338 gst_collect_pads_set_function (mux->collect,
339 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
342 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
344 /* property defaults */
345 mux->doctype = "matroska";
346 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
347 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
348 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
350 /* initialize internal variables */
352 mux->num_streams = 0;
353 mux->num_a_streams = 0;
354 mux->num_t_streams = 0;
355 mux->num_v_streams = 0;
357 /* initialize remaining variables */
358 gst_matroska_mux_reset (GST_ELEMENT (mux));
363 * gst_matroska_mux_finalize:
364 * @object: #GstMatroskaMux that should be finalized.
366 * Finalize matroska muxer.
369 gst_matroska_mux_finalize (GObject * object)
371 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
373 gst_object_unref (mux->collect);
374 gst_object_unref (mux->ebml_write);
375 if (mux->writing_app)
376 g_free (mux->writing_app);
378 G_OBJECT_CLASS (parent_class)->finalize (object);
383 * gst_matroska_mux_create_uid:
385 * Generate new unused track UID.
387 * Returns: New track UID.
390 gst_matroska_mux_create_uid (void)
397 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
402 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
403 for (i = 0; i < used_uids->len; i++) {
404 if (g_array_index (used_uids, guint64, i) == uid) {
409 g_array_append_val (used_uids, uid);
412 G_UNLOCK (used_uids);
418 * gst_matroska_pad_reset:
419 * @collect_pad: the #GstMatroskaPad
421 * Reset and/or release resources of a matroska collect pad.
424 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
427 GstMatroskaTrackType type = 0;
429 /* free track information */
430 if (collect_pad->track != NULL) {
431 /* retrieve for optional later use */
432 name = collect_pad->track->name;
433 type = collect_pad->track->type;
434 /* extra for video */
435 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
436 GstMatroskaTrackVideoContext *ctx =
437 (GstMatroskaTrackVideoContext *) collect_pad->track;
439 if (ctx->dirac_unit) {
440 gst_buffer_unref (ctx->dirac_unit);
441 ctx->dirac_unit = NULL;
444 g_free (collect_pad->track->codec_id);
445 g_free (collect_pad->track->codec_name);
447 g_free (collect_pad->track->name);
448 g_free (collect_pad->track->language);
449 g_free (collect_pad->track->codec_priv);
450 g_free (collect_pad->track);
451 collect_pad->track = NULL;
454 /* free cached buffer */
455 if (collect_pad->buffer != NULL) {
456 gst_buffer_unref (collect_pad->buffer);
457 collect_pad->buffer = NULL;
460 if (!full && type != 0) {
461 GstMatroskaTrackContext *context;
463 /* create a fresh context */
465 case GST_MATROSKA_TRACK_TYPE_VIDEO:
466 context = (GstMatroskaTrackContext *)
467 g_new0 (GstMatroskaTrackVideoContext, 1);
469 case GST_MATROSKA_TRACK_TYPE_AUDIO:
470 context = (GstMatroskaTrackContext *)
471 g_new0 (GstMatroskaTrackAudioContext, 1);
473 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
474 context = (GstMatroskaTrackContext *)
475 g_new0 (GstMatroskaTrackSubtitleContext, 1);
478 g_assert_not_reached ();
482 context->type = type;
483 context->name = name;
484 /* TODO: check default values for the context */
485 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
486 collect_pad->track = context;
487 collect_pad->buffer = NULL;
488 collect_pad->duration = 0;
489 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
490 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
495 * gst_matroska_pad_free:
496 * @collect_pad: the #GstMatroskaPad
498 * Release resources of a matroska collect pad.
501 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
503 gst_matroska_pad_reset (collect_pad, TRUE);
508 * gst_matroska_mux_reset:
509 * @element: #GstMatroskaMux that should be reseted.
511 * Reset matroska muxer back to initial state.
514 gst_matroska_mux_reset (GstElement * element)
516 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
519 /* reset EBML write */
520 gst_ebml_write_reset (mux->ebml_write);
523 mux->state = GST_MATROSKA_MUX_STATE_START;
525 /* clean up existing streams */
527 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
528 GstMatroskaPad *collect_pad;
530 collect_pad = (GstMatroskaPad *) walk->data;
532 /* reset collect pad to pristine state */
533 gst_matroska_pad_reset (collect_pad, FALSE);
537 mux->num_indexes = 0;
542 mux->time_scale = GST_MSECOND;
547 mux->cluster_time = 0;
548 mux->cluster_pos = 0;
549 mux->prev_cluster_size = 0;
552 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
556 * gst_matroska_mux_handle_src_event:
557 * @pad: Pad which received the event.
558 * @event: Received event.
560 * handle events - copied from oggmux without understanding
562 * Returns: #TRUE on success.
565 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
569 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
573 /* disable seeking for now */
579 return gst_pad_event_default (pad, event);
583 * gst_matroska_mux_handle_sink_event:
584 * @pad: Pad which received the event.
585 * @event: Received event.
587 * handle events - informational ones like tags
589 * Returns: #TRUE on success.
592 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
594 GstMatroskaTrackContext *context;
595 GstMatroskaPad *collect_pad;
600 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
602 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
603 switch (GST_EVENT_TYPE (event)) {
607 GST_DEBUG_OBJECT (mux, "received tag event");
608 gst_event_parse_tag (event, &list);
610 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
611 g_assert (collect_pad);
612 context = collect_pad->track;
615 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
616 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
617 const gchar *lang_code;
619 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
621 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
622 context->language = g_strdup (lang_code);
624 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
629 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
630 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
633 case GST_EVENT_NEWSEGMENT:
634 /* We don't support NEWSEGMENT events */
636 gst_event_unref (event);
642 /* now GstCollectPads can take care of the rest, e.g. EOS */
644 ret = mux->collect_event (pad, event);
645 gst_object_unref (mux);
652 * gst_matroska_mux_video_pad_setcaps:
653 * @pad: Pad which got the caps.
656 * Setcaps function for video sink pad.
658 * Returns: #TRUE on success.
661 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
663 GstMatroskaTrackContext *context = NULL;
664 GstMatroskaTrackVideoContext *videocontext;
666 GstMatroskaPad *collect_pad;
667 GstStructure *structure;
668 const gchar *mimetype;
669 const GValue *value = NULL;
670 const GstBuffer *codec_buf = NULL;
671 gint width, height, pixel_width, pixel_height;
673 gboolean interlaced = FALSE;
675 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
678 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
679 g_assert (collect_pad);
680 context = collect_pad->track;
682 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
683 videocontext = (GstMatroskaTrackVideoContext *) context;
685 /* gst -> matroska ID'ing */
686 structure = gst_caps_get_structure (caps, 0);
688 mimetype = gst_structure_get_name (structure);
690 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
692 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
694 if (!strcmp (mimetype, "video/x-theora")) {
695 /* we'll extract the details later from the theora identification header */
699 /* get general properties */
700 /* spec says it is mandatory */
701 if (!gst_structure_get_int (structure, "width", &width) ||
702 !gst_structure_get_int (structure, "height", &height))
705 videocontext->pixel_width = width;
706 videocontext->pixel_height = height;
707 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
709 context->default_duration =
710 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
711 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
712 GST_TIME_ARGS (context->default_duration));
714 context->default_duration = 0;
716 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
717 &pixel_width, &pixel_height)) {
718 if (pixel_width > pixel_height) {
719 videocontext->display_width = width * pixel_width / pixel_height;
720 videocontext->display_height = height;
721 } else if (pixel_width < pixel_height) {
722 videocontext->display_width = width;
723 videocontext->display_height = height * pixel_height / pixel_width;
725 videocontext->display_width = 0;
726 videocontext->display_height = 0;
729 videocontext->display_width = 0;
730 videocontext->display_height = 0;
735 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
736 videocontext->fourcc = 0;
738 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
739 * data and other settings
743 /* extract codec_data, may turn out needed */
744 value = gst_structure_get_value (structure, "codec_data");
746 codec_buf = gst_value_get_buffer (value);
749 if (!strcmp (mimetype, "video/x-raw-yuv")) {
750 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
751 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
752 } else if (!strcmp (mimetype, "image/jpeg")) {
753 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
754 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
755 ||!strcmp (mimetype, "video/x-huffyuv")
756 || !strcmp (mimetype, "video/x-divx")
757 || !strcmp (mimetype, "video/x-dv")
758 || !strcmp (mimetype, "video/x-h263")
759 || !strcmp (mimetype, "video/x-msmpeg")
760 || !strcmp (mimetype, "video/x-wmv")) {
761 gst_riff_strf_vids *bih;
762 gint size = sizeof (gst_riff_strf_vids);
765 if (!strcmp (mimetype, "video/x-xvid"))
766 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
767 else if (!strcmp (mimetype, "video/x-huffyuv"))
768 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
769 else if (!strcmp (mimetype, "video/x-dv"))
770 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
771 else if (!strcmp (mimetype, "video/x-h263"))
772 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
773 else if (!strcmp (mimetype, "video/x-divx")) {
776 gst_structure_get_int (structure, "divxversion", &divxversion);
777 switch (divxversion) {
779 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
782 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
785 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
788 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
791 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
792 switch (msmpegversion) {
794 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
797 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
803 } else if (!strcmp (mimetype, "video/x-wmv")) {
806 if (gst_structure_get_fourcc (structure, "format", &format)) {
808 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
809 if (wmvversion == 2) {
810 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
811 } else if (wmvversion == 1) {
812 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
813 } else if (wmvversion == 3) {
814 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
822 bih = g_new0 (gst_riff_strf_vids, 1);
823 GST_WRITE_UINT32_LE (&bih->size, size);
824 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
825 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
826 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
827 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
828 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
829 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
830 videocontext->pixel_height * 3);
832 /* process codec private/initialization data, if any */
834 size += GST_BUFFER_SIZE (codec_buf);
835 bih = g_realloc (bih, size);
836 GST_WRITE_UINT32_LE (&bih->size, size);
837 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
838 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
841 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
842 context->codec_priv = (gpointer) bih;
843 context->codec_priv_size = size;
844 } else if (!strcmp (mimetype, "video/x-h264")) {
845 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
847 if (context->codec_priv != NULL) {
848 g_free (context->codec_priv);
849 context->codec_priv = NULL;
850 context->codec_priv_size = 0;
853 /* Create avcC header */
854 if (codec_buf != NULL) {
855 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
856 context->codec_priv = g_malloc0 (context->codec_priv_size);
857 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
858 context->codec_priv_size);
860 } else if (!strcmp (mimetype, "video/x-theora")) {
861 const GValue *streamheader;
863 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
865 if (context->codec_priv != NULL) {
866 g_free (context->codec_priv);
867 context->codec_priv = NULL;
868 context->codec_priv_size = 0;
871 streamheader = gst_structure_get_value (structure, "streamheader");
872 if (!theora_streamheader_to_codecdata (streamheader, context)) {
873 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
874 ("theora stream headers missing or malformed"));
877 } else if (!strcmp (mimetype, "video/x-dirac")) {
878 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
879 } else if (!strcmp (mimetype, "video/x-vp8")) {
880 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
881 } else if (!strcmp (mimetype, "video/mpeg")) {
884 gst_structure_get_int (structure, "mpegversion", &mpegversion);
885 switch (mpegversion) {
887 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
890 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
899 /* global headers may be in codec data */
900 if (codec_buf != NULL) {
901 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
902 context->codec_priv = g_malloc0 (context->codec_priv_size);
903 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
904 context->codec_priv_size);
906 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
908 /* can only make it here if preceding case verified it was version 3 */
909 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
910 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
912 const GValue *mdpr_data;
914 gst_structure_get_int (structure, "rmversion", &rmversion);
917 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
920 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
923 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
926 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
932 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
933 if (mdpr_data != NULL) {
934 guint8 *priv_data = NULL;
935 guint priv_data_size = 0;
937 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
939 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
940 priv_data = g_malloc0 (priv_data_size);
942 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
944 context->codec_priv = priv_data;
945 context->codec_priv_size = priv_data_size;
954 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
955 GST_PAD_NAME (pad), caps);
960 /* N > 0 to expect a particular number of headers, negative if the
961 number of headers is variable */
963 xiphN_streamheader_to_codecdata (const GValue * streamheader,
964 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
966 GstBuffer **buf = NULL;
969 guint bufi, i, offset, priv_data_size;
971 if (streamheader == NULL)
972 goto no_stream_headers;
974 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
977 bufarr = g_value_peek_pointer (streamheader);
978 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
980 if (N > 0 && bufarr->len != N)
983 context->xiph_headers_to_skip = bufarr->len;
985 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
986 for (i = 0; i < bufarr->len; i++) {
987 GValue *bufval = &g_array_index (bufarr, GValue, i);
989 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
991 goto wrong_content_type;
994 buf[i] = g_value_peek_pointer (bufval);
998 if (bufarr->len > 0) {
999 for (i = 0; i < bufarr->len - 1; i++) {
1000 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1004 for (i = 0; i < bufarr->len; ++i) {
1005 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1008 priv_data = g_malloc0 (priv_data_size);
1010 priv_data[0] = bufarr->len - 1;
1013 if (bufarr->len > 0) {
1014 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1015 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1016 priv_data[offset++] = 0xff;
1018 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1022 for (i = 0; i < bufarr->len; ++i) {
1023 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1024 GST_BUFFER_SIZE (buf[i]));
1025 offset += GST_BUFFER_SIZE (buf[i]);
1028 context->codec_priv = priv_data;
1029 context->codec_priv_size = priv_data_size;
1032 *p_buf0 = gst_buffer_ref (buf[0]);
1041 GST_WARNING ("required streamheaders missing in sink caps!");
1046 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1047 G_VALUE_TYPE_NAME (streamheader));
1052 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1057 GST_WARNING ("streamheaders array does not contain GstBuffers");
1063 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1064 GstMatroskaTrackContext * context)
1066 GstBuffer *buf0 = NULL;
1068 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1071 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1072 GST_WARNING ("First vorbis header too small, ignoring");
1074 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1075 GstMatroskaTrackAudioContext *audiocontext;
1078 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1079 audiocontext = (GstMatroskaTrackAudioContext *) context;
1080 audiocontext->channels = GST_READ_UINT8 (hdr);
1081 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1086 gst_buffer_unref (buf0);
1092 theora_streamheader_to_codecdata (const GValue * streamheader,
1093 GstMatroskaTrackContext * context)
1095 GstBuffer *buf0 = NULL;
1097 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1100 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1101 GST_WARNING ("First theora header too small, ignoring");
1102 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1103 GST_WARNING ("First header not a theora identification header, ignoring");
1105 GstMatroskaTrackVideoContext *videocontext;
1106 guint fps_num, fps_denom, par_num, par_denom;
1109 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1111 videocontext = (GstMatroskaTrackVideoContext *) context;
1112 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1113 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1114 hdr += 3 + 3 + 1 + 1;
1115 fps_num = GST_READ_UINT32_BE (hdr);
1116 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1117 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1118 fps_denom, fps_num);
1120 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1121 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1122 if (par_num > 0 && par_num > 0) {
1123 if (par_num > par_denom) {
1124 videocontext->display_width =
1125 videocontext->pixel_width * par_num / par_denom;
1126 videocontext->display_height = videocontext->pixel_height;
1127 } else if (par_num < par_denom) {
1128 videocontext->display_width = videocontext->pixel_width;
1129 videocontext->display_height =
1130 videocontext->pixel_height * par_denom / par_num;
1132 videocontext->display_width = 0;
1133 videocontext->display_height = 0;
1136 videocontext->display_width = 0;
1137 videocontext->display_height = 0;
1143 gst_buffer_unref (buf0);
1149 kate_streamheader_to_codecdata (const GValue * streamheader,
1150 GstMatroskaTrackContext * context)
1152 GstBuffer *buf0 = NULL;
1154 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1157 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1158 GST_WARNING ("First kate header too small, ignoring");
1159 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1160 GST_WARNING ("First header not a kate identification header, ignoring");
1164 gst_buffer_unref (buf0);
1170 flac_streamheader_to_codecdata (const GValue * streamheader,
1171 GstMatroskaTrackContext * context)
1178 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1179 GST_WARNING ("No or invalid streamheader field in the caps");
1183 bufarr = g_value_peek_pointer (streamheader);
1184 if (bufarr->len < 2) {
1185 GST_WARNING ("Too few headers in streamheader field");
1189 context->xiph_headers_to_skip = bufarr->len + 1;
1191 bufval = &g_array_index (bufarr, GValue, 0);
1192 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1193 GST_WARNING ("streamheaders array does not contain GstBuffers");
1197 buffer = g_value_peek_pointer (bufval);
1199 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1200 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1201 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1202 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1203 GST_WARNING ("Invalid streamheader for FLAC");
1207 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1208 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1209 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1210 GST_BUFFER_SIZE (buffer) - 9);
1212 for (i = 1; i < bufarr->len; i++) {
1213 bufval = &g_array_index (bufarr, GValue, i);
1215 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1216 g_free (context->codec_priv);
1217 context->codec_priv = NULL;
1218 context->codec_priv_size = 0;
1219 GST_WARNING ("streamheaders array does not contain GstBuffers");
1223 buffer = g_value_peek_pointer (bufval);
1225 context->codec_priv =
1226 g_realloc (context->codec_priv,
1227 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1228 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1229 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1230 context->codec_priv_size =
1231 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1238 speex_streamheader_to_codecdata (const GValue * streamheader,
1239 GstMatroskaTrackContext * context)
1245 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1246 GST_WARNING ("No or invalid streamheader field in the caps");
1250 bufarr = g_value_peek_pointer (streamheader);
1251 if (bufarr->len != 2) {
1252 GST_WARNING ("Too few headers in streamheader field");
1256 context->xiph_headers_to_skip = bufarr->len + 1;
1258 bufval = &g_array_index (bufarr, GValue, 0);
1259 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1260 GST_WARNING ("streamheaders array does not contain GstBuffers");
1264 buffer = g_value_peek_pointer (bufval);
1266 if (GST_BUFFER_SIZE (buffer) < 80
1267 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1268 GST_WARNING ("Invalid streamheader for Speex");
1272 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1273 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1274 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1275 GST_BUFFER_SIZE (buffer));
1277 bufval = &g_array_index (bufarr, GValue, 1);
1279 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1280 g_free (context->codec_priv);
1281 context->codec_priv = NULL;
1282 context->codec_priv_size = 0;
1283 GST_WARNING ("streamheaders array does not contain GstBuffers");
1287 buffer = g_value_peek_pointer (bufval);
1289 context->codec_priv =
1290 g_realloc (context->codec_priv,
1291 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1292 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1293 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1294 context->codec_priv_size =
1295 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1300 static const gchar *
1301 aac_codec_data_to_codec_id (const GstBuffer * buf)
1303 const gchar *result;
1306 /* default to MAIN */
1309 if (GST_BUFFER_SIZE (buf) >= 2) {
1310 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1328 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1337 * gst_matroska_mux_audio_pad_setcaps:
1338 * @pad: Pad which got the caps.
1341 * Setcaps function for audio sink pad.
1343 * Returns: #TRUE on success.
1346 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1348 GstMatroskaTrackContext *context = NULL;
1349 GstMatroskaTrackAudioContext *audiocontext;
1350 GstMatroskaMux *mux;
1351 GstMatroskaPad *collect_pad;
1352 const gchar *mimetype;
1353 gint samplerate = 0, channels = 0;
1354 GstStructure *structure;
1355 const GValue *codec_data = NULL;
1356 const GstBuffer *buf = NULL;
1357 const gchar *stream_format = NULL;
1359 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1362 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1363 g_assert (collect_pad);
1364 context = collect_pad->track;
1366 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1367 audiocontext = (GstMatroskaTrackAudioContext *) context;
1369 structure = gst_caps_get_structure (caps, 0);
1370 mimetype = gst_structure_get_name (structure);
1373 gst_structure_get_int (structure, "rate", &samplerate);
1374 gst_structure_get_int (structure, "channels", &channels);
1376 audiocontext->samplerate = samplerate;
1377 audiocontext->channels = channels;
1378 audiocontext->bitdepth = 0;
1379 context->default_duration = 0;
1381 codec_data = gst_structure_get_value (structure, "codec_data");
1383 buf = gst_value_get_buffer (codec_data);
1385 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1386 * data and other settings
1390 if (!strcmp (mimetype, "audio/mpeg")) {
1391 gint mpegversion = 0;
1393 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1394 switch (mpegversion) {
1400 gst_structure_get_int (structure, "layer", &layer);
1402 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1403 GST_WARNING_OBJECT (mux,
1404 "Unable to determine MPEG audio version, assuming 1");
1410 else if (layer == 2)
1412 else if (version == 2)
1417 context->default_duration =
1418 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1422 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1425 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1428 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1437 stream_format = gst_structure_get_string (structure, "stream-format");
1438 /* check this is raw aac */
1439 if (stream_format) {
1440 if (strcmp (stream_format, "raw") != 0) {
1441 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1445 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1450 if (mpegversion == 2)
1452 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1453 aac_codec_data_to_codec_id (buf));
1454 else if (mpegversion == 4)
1456 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1457 aac_codec_data_to_codec_id (buf));
1459 g_assert_not_reached ();
1461 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1468 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1470 gint endianness = G_LITTLE_ENDIAN;
1471 gboolean signedness = TRUE;
1473 if (!gst_structure_get_int (structure, "width", &width) ||
1474 !gst_structure_get_int (structure, "depth", &depth) ||
1475 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1476 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1481 !gst_structure_get_int (structure, "endianness", &endianness)) {
1482 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1486 if (width != depth) {
1487 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1491 /* FIXME: where is this spec'ed out? (tpm) */
1492 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1493 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1497 audiocontext->bitdepth = depth;
1498 if (endianness == G_BIG_ENDIAN)
1499 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1501 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1503 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1506 if (!gst_structure_get_int (structure, "width", &width)) {
1507 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1511 audiocontext->bitdepth = width;
1512 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1514 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1515 const GValue *streamheader;
1517 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1519 if (context->codec_priv != NULL) {
1520 g_free (context->codec_priv);
1521 context->codec_priv = NULL;
1522 context->codec_priv_size = 0;
1525 streamheader = gst_structure_get_value (structure, "streamheader");
1526 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1527 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1528 ("vorbis stream headers missing or malformed"));
1531 } else if (!strcmp (mimetype, "audio/x-flac")) {
1532 const GValue *streamheader;
1534 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1535 if (context->codec_priv != NULL) {
1536 g_free (context->codec_priv);
1537 context->codec_priv = NULL;
1538 context->codec_priv_size = 0;
1541 streamheader = gst_structure_get_value (structure, "streamheader");
1542 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1543 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1544 ("flac stream headers missing or malformed"));
1547 } else if (!strcmp (mimetype, "audio/x-speex")) {
1548 const GValue *streamheader;
1550 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1551 if (context->codec_priv != NULL) {
1552 g_free (context->codec_priv);
1553 context->codec_priv = NULL;
1554 context->codec_priv_size = 0;
1557 streamheader = gst_structure_get_value (structure, "streamheader");
1558 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1559 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1560 ("speex stream headers missing or malformed"));
1563 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1564 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1565 } else if (!strcmp (mimetype, "audio/x-tta")) {
1568 /* TTA frame duration */
1569 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1571 gst_structure_get_int (structure, "width", &width);
1572 audiocontext->bitdepth = width;
1573 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1575 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1577 const GValue *mdpr_data;
1579 gst_structure_get_int (structure, "raversion", &raversion);
1580 switch (raversion) {
1582 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1585 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1588 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1594 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1595 if (mdpr_data != NULL) {
1596 guint8 *priv_data = NULL;
1597 guint priv_data_size = 0;
1599 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1601 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1602 priv_data = g_malloc0 (priv_data_size);
1604 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1606 context->codec_priv = priv_data;
1607 context->codec_priv_size = priv_data_size;
1610 } else if (!strcmp (mimetype, "audio/x-wma")) {
1612 guint codec_priv_size;
1619 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1620 || !gst_structure_get_int (structure, "block_align", &block_align)
1621 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1622 || samplerate == 0 || channels == 0) {
1623 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1624 "channels/rate on WMA caps");
1628 switch (wmaversion) {
1630 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1633 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1636 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1639 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1643 if (gst_structure_get_int (structure, "depth", &depth))
1644 audiocontext->bitdepth = depth;
1646 codec_priv_size = WAVEFORMATEX_SIZE;
1648 codec_priv_size += GST_BUFFER_SIZE (buf);
1650 /* serialize waveformatex structure */
1651 codec_priv = g_malloc0 (codec_priv_size);
1652 GST_WRITE_UINT16_LE (codec_priv, format);
1653 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1654 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1655 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1656 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1657 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1659 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1661 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1663 /* process codec private/initialization data, if any */
1665 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1666 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1669 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1670 context->codec_priv = (gpointer) codec_priv;
1671 context->codec_priv_size = codec_priv_size;
1679 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1680 GST_PAD_NAME (pad), caps);
1687 * gst_matroska_mux_subtitle_pad_setcaps:
1688 * @pad: Pad which got the caps.
1691 * Setcaps function for subtitle sink pad.
1693 * Returns: #TRUE on success.
1696 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1699 * Consider this as boilerplate code for now. There is
1700 * no single subtitle creation element in GStreamer,
1701 * neither do I know how subtitling works at all. */
1703 /* There is now (at least) one such alement (kateenc), and I'm going
1704 to handle it here and claim it works when it can be piped back
1705 through GStreamer and VLC */
1707 GstMatroskaTrackContext *context = NULL;
1708 GstMatroskaTrackSubtitleContext *scontext;
1709 GstMatroskaMux *mux;
1710 GstMatroskaPad *collect_pad;
1711 const gchar *mimetype;
1712 GstStructure *structure;
1714 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1717 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1718 g_assert (collect_pad);
1719 context = collect_pad->track;
1721 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1722 scontext = (GstMatroskaTrackSubtitleContext *) context;
1724 structure = gst_caps_get_structure (caps, 0);
1725 mimetype = gst_structure_get_name (structure);
1728 scontext->check_utf8 = 1;
1729 scontext->invalid_utf8 = 0;
1730 context->default_duration = 0;
1732 /* TODO: - other format than Kate */
1734 if (!strcmp (mimetype, "subtitle/x-kate")) {
1735 const GValue *streamheader;
1737 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1739 if (context->codec_priv != NULL) {
1740 g_free (context->codec_priv);
1741 context->codec_priv = NULL;
1742 context->codec_priv_size = 0;
1745 streamheader = gst_structure_get_value (structure, "streamheader");
1746 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1747 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1748 ("kate stream headers missing or malformed"));
1759 * gst_matroska_mux_request_new_pad:
1760 * @element: #GstMatroskaMux.
1761 * @templ: #GstPadTemplate.
1762 * @pad_name: New pad name.
1764 * Request pad function for sink templates.
1766 * Returns: New #GstPad.
1769 gst_matroska_mux_request_new_pad (GstElement * element,
1770 GstPadTemplate * templ, const gchar * pad_name)
1772 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1773 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1774 GstMatroskaPad *collect_pad;
1775 GstPad *newpad = NULL;
1777 GstPadSetCapsFunction setcapsfunc = NULL;
1778 GstMatroskaTrackContext *context = NULL;
1780 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1781 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1782 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1783 context = (GstMatroskaTrackContext *)
1784 g_new0 (GstMatroskaTrackAudioContext, 1);
1785 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1786 context->name = g_strdup ("Audio");
1787 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1788 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1789 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1790 context = (GstMatroskaTrackContext *)
1791 g_new0 (GstMatroskaTrackVideoContext, 1);
1792 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1793 context->name = g_strdup ("Video");
1794 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1795 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1796 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1797 context = (GstMatroskaTrackContext *)
1798 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1799 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1800 context->name = g_strdup ("Subtitle");
1802 GST_WARNING_OBJECT (mux, "This is not our template!");
1806 newpad = gst_pad_new_from_template (templ, name);
1808 collect_pad = (GstMatroskaPad *)
1809 gst_collect_pads_add_pad_full (mux->collect, newpad,
1810 sizeof (GstMatroskaPad),
1811 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1813 collect_pad->track = context;
1814 gst_matroska_pad_reset (collect_pad, FALSE);
1816 /* FIXME: hacked way to override/extend the event function of
1817 * GstCollectPads; because it sets its own event function giving the
1818 * element no access to events.
1819 * TODO GstCollectPads should really give its 'users' a clean chance to
1820 * properly handle events that are not meant for collectpads itself.
1821 * Perhaps a callback or so, though rejected (?) in #340060.
1822 * This would allow (clean) transcoding of info from demuxer/streams
1823 * to another muxer */
1824 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1825 gst_pad_set_event_function (newpad,
1826 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1828 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1829 gst_pad_set_active (newpad, TRUE);
1830 gst_element_add_pad (element, newpad);
1837 * gst_matroska_mux_release_pad:
1838 * @element: #GstMatroskaMux.
1839 * @pad: Pad to release.
1841 * Release a previously requested pad.
1844 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1846 GstMatroskaMux *mux;
1849 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1851 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1852 GstCollectData *cdata = (GstCollectData *) walk->data;
1853 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1855 if (cdata->pad == pad) {
1856 GstClockTime min_dur; /* observed minimum duration */
1858 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1859 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1860 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1861 if (collect_pad->duration < min_dur)
1862 collect_pad->duration = min_dur;
1865 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1866 mux->duration < collect_pad->duration)
1867 mux->duration = collect_pad->duration;
1873 gst_collect_pads_remove_pad (mux->collect, pad);
1874 if (gst_element_remove_pad (element, pad))
1880 * gst_matroska_mux_track_header:
1881 * @mux: #GstMatroskaMux
1882 * @context: Tack context.
1884 * Write a track header.
1887 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1888 GstMatroskaTrackContext * context)
1890 GstEbmlWrite *ebml = mux->ebml_write;
1893 /* TODO: check if everything necessary is written and check default values */
1895 /* track type goes before the type-specific stuff */
1896 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1897 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1899 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1900 gst_matroska_mux_create_uid ());
1901 if (context->default_duration) {
1902 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1903 context->default_duration);
1905 if (context->language) {
1906 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1910 /* type-specific stuff */
1911 switch (context->type) {
1912 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1913 GstMatroskaTrackVideoContext *videocontext =
1914 (GstMatroskaTrackVideoContext *) context;
1916 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1917 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1918 videocontext->pixel_width);
1919 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1920 videocontext->pixel_height);
1921 if (videocontext->display_width && videocontext->display_height) {
1922 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1923 videocontext->display_width);
1924 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1925 videocontext->display_height);
1927 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1928 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1929 if (videocontext->fourcc) {
1930 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1932 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1933 (gpointer) & fcc_le, 4);
1935 gst_ebml_write_master_finish (ebml, master);
1940 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1941 GstMatroskaTrackAudioContext *audiocontext =
1942 (GstMatroskaTrackAudioContext *) context;
1944 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1945 if (audiocontext->samplerate != 8000)
1946 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1947 audiocontext->samplerate);
1948 if (audiocontext->channels != 1)
1949 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1950 audiocontext->channels);
1951 if (audiocontext->bitdepth) {
1952 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1953 audiocontext->bitdepth);
1955 gst_ebml_write_master_finish (ebml, master);
1961 /* doesn't need type-specific data */
1965 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1966 if (context->codec_priv)
1967 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1968 context->codec_priv, context->codec_priv_size);
1969 /* FIXME: until we have a nice way of getting the codecname
1970 * out of the caps, I'm not going to enable this. Too much
1971 * (useless, double, boring) work... */
1972 /* TODO: Use value from tags if any */
1973 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1974 context->codec_name); */
1975 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1980 * gst_matroska_mux_start:
1981 * @mux: #GstMatroskaMux
1983 * Start a new matroska file (write headers etc...)
1986 gst_matroska_mux_start (GstMatroskaMux * mux)
1988 GstEbmlWrite *ebml = mux->ebml_write;
1989 const gchar *doctype;
1990 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1991 GST_MATROSKA_ID_TRACKS,
1992 GST_MATROSKA_ID_CUES,
1993 GST_MATROSKA_ID_TAGS,
1996 guint64 master, child;
2000 GstClockTime duration = 0;
2001 guint32 segment_uid[4];
2002 GTimeVal time = { 0, 0 };
2004 /* we start with a EBML header */
2005 doctype = mux->doctype;
2006 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2007 doctype, mux->doctype_version);
2008 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2010 /* start a segment */
2012 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2013 mux->segment_master = ebml->pos;
2015 /* the rest of the header is cached */
2016 gst_ebml_write_set_cache (ebml, 0x1000);
2018 /* seekhead (table of contents) - we set the positions later */
2019 mux->seekhead_pos = ebml->pos;
2020 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2021 for (i = 0; seekhead_id[i] != 0; i++) {
2022 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2023 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2024 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2025 gst_ebml_write_master_finish (ebml, child);
2027 gst_ebml_write_master_finish (ebml, master);
2030 mux->info_pos = ebml->pos;
2031 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2032 for (i = 0; i < 4; i++) {
2033 segment_uid[i] = g_random_int ();
2035 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2036 (guint8 *) segment_uid, 16);
2037 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2038 mux->duration_pos = ebml->pos;
2040 for (collected = mux->collect->data; collected;
2041 collected = g_slist_next (collected)) {
2042 GstMatroskaPad *collect_pad;
2043 GstFormat format = GST_FORMAT_TIME;
2045 gint64 trackduration;
2047 collect_pad = (GstMatroskaPad *) collected->data;
2048 thepad = collect_pad->collect.pad;
2050 /* Query the total length of the track. */
2051 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2052 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2053 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2054 GST_TIME_ARGS (trackduration));
2055 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2056 duration = (GstClockTime) trackduration;
2060 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2061 gst_guint64_to_gdouble (duration) /
2062 gst_guint64_to_gdouble (mux->time_scale));
2064 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2065 "GStreamer plugin version " PACKAGE_VERSION);
2066 if (mux->writing_app && mux->writing_app[0]) {
2067 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2069 g_get_current_time (&time);
2070 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2071 gst_ebml_write_master_finish (ebml, master);
2074 mux->tracks_pos = ebml->pos;
2075 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2077 for (collected = mux->collect->data; collected;
2078 collected = g_slist_next (collected)) {
2079 GstMatroskaPad *collect_pad;
2082 collect_pad = (GstMatroskaPad *) collected->data;
2083 thepad = collect_pad->collect.pad;
2085 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2086 collect_pad->track->codec_id != 0) {
2087 collect_pad->track->num = tracknum++;
2088 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2089 gst_matroska_mux_track_header (mux, collect_pad->track);
2090 gst_ebml_write_master_finish (ebml, child);
2093 gst_ebml_write_master_finish (ebml, master);
2095 /* lastly, flush the cache */
2096 gst_ebml_write_flush_cache (ebml);
2100 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2103 /* TODO: more sensible tag mappings */
2106 const gchar *matroska_tagname;
2107 const gchar *gstreamer_tagname;
2111 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2112 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2113 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2114 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2115 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2116 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2117 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2118 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2119 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2120 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2121 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2122 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2123 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2124 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2125 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2127 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2129 guint64 simpletag_master;
2131 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2132 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2133 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2135 if (strcmp (tagname_gst, tag) == 0) {
2136 GValue src = { 0, };
2139 if (!gst_tag_list_copy_value (&src, list, tag))
2141 if ((dest = gst_value_serialize (&src))) {
2143 simpletag_master = gst_ebml_write_master_start (ebml,
2144 GST_MATROSKA_ID_SIMPLETAG);
2145 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2146 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2147 gst_ebml_write_master_finish (ebml, simpletag_master);
2150 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2152 g_value_unset (&src);
2160 * gst_matroska_mux_finish:
2161 * @mux: #GstMatroskaMux
2163 * Finish a new matroska file (write index etc...)
2166 gst_matroska_mux_finish (GstMatroskaMux * mux)
2168 GstEbmlWrite *ebml = mux->ebml_write;
2170 guint64 duration = 0;
2172 const GstTagList *tags;
2174 /* finish last cluster */
2176 gst_ebml_write_master_finish (ebml, mux->cluster);
2180 if (mux->index != NULL) {
2182 guint64 master, pointentry_master, trackpos_master;
2184 mux->cues_pos = ebml->pos;
2185 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2186 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2188 for (n = 0; n < mux->num_indexes; n++) {
2189 GstMatroskaIndex *idx = &mux->index[n];
2191 pointentry_master = gst_ebml_write_master_start (ebml,
2192 GST_MATROSKA_ID_POINTENTRY);
2193 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2194 idx->time / mux->time_scale);
2195 trackpos_master = gst_ebml_write_master_start (ebml,
2196 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2197 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2198 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2199 idx->pos - mux->segment_master);
2200 gst_ebml_write_master_finish (ebml, trackpos_master);
2201 gst_ebml_write_master_finish (ebml, pointentry_master);
2204 gst_ebml_write_master_finish (ebml, master);
2205 gst_ebml_write_flush_cache (ebml);
2209 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2211 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2212 guint64 master_tags, master_tag;
2214 GST_DEBUG ("Writing tags");
2216 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2217 mux->tags_pos = ebml->pos;
2218 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2219 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2220 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2221 gst_ebml_write_master_finish (ebml, master_tag);
2222 gst_ebml_write_master_finish (ebml, master_tags);
2225 /* update seekhead. We know that:
2226 * - a seekhead contains 4 entries.
2227 * - order of entries is as above.
2228 * - a seekhead has a 4-byte header + 8-byte length
2229 * - each entry is 2-byte master, 2-byte ID pointer,
2230 * 2-byte length pointer, all 8/1-byte length, 4-
2231 * byte ID and 8-byte length pointer, where the
2232 * length pointer starts at 20.
2233 * - all entries are local to the segment (so pos - segment_master).
2234 * - so each entry is at 12 + 20 + num * 28. */
2235 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2236 mux->info_pos - mux->segment_master);
2237 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2238 mux->tracks_pos - mux->segment_master);
2239 if (mux->index != NULL) {
2240 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2241 mux->cues_pos - mux->segment_master);
2244 guint64 my_pos = ebml->pos;
2246 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2247 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2248 gst_ebml_write_seek (ebml, my_pos);
2251 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2252 mux->tags_pos - mux->segment_master);
2255 guint64 my_pos = ebml->pos;
2257 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2258 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2259 gst_ebml_write_seek (ebml, my_pos);
2262 /* update duration */
2263 /* first get the overall duration */
2264 /* a released track may have left a duration in here */
2265 duration = mux->duration;
2266 for (collected = mux->collect->data; collected;
2267 collected = g_slist_next (collected)) {
2268 GstMatroskaPad *collect_pad;
2269 GstClockTime min_duration; /* observed minimum duration */
2271 collect_pad = (GstMatroskaPad *) collected->data;
2273 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2274 " end ts %" GST_TIME_FORMAT, collect_pad,
2275 GST_TIME_ARGS (collect_pad->start_ts),
2276 GST_TIME_ARGS (collect_pad->end_ts));
2278 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2279 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2281 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2282 if (collect_pad->duration < min_duration)
2283 collect_pad->duration = min_duration;
2284 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2285 GST_TIME_ARGS (collect_pad->duration));
2288 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2289 duration < collect_pad->duration)
2290 duration = collect_pad->duration;
2292 if (duration != 0) {
2293 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2294 GST_TIME_ARGS (duration));
2295 pos = mux->ebml_write->pos;
2296 gst_ebml_write_seek (ebml, mux->duration_pos);
2297 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2298 gst_guint64_to_gdouble (duration) /
2299 gst_guint64_to_gdouble (mux->time_scale));
2300 gst_ebml_write_seek (ebml, pos);
2303 guint64 my_pos = ebml->pos;
2305 gst_ebml_write_seek (ebml, mux->duration_pos);
2306 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2307 gst_ebml_write_seek (ebml, my_pos);
2310 /* finish segment - this also writes element length */
2311 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2316 * gst_matroska_mux_best_pad:
2317 * @mux: #GstMatroskaMux
2318 * @popped: True if at least one buffer was popped from #GstCollectPads
2320 * Find a pad with the oldest data
2321 * (data from this pad should be written first).
2323 * Returns: Selected pad.
2325 static GstMatroskaPad *
2326 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2329 GstMatroskaPad *best = NULL;
2332 for (collected = mux->collect->data; collected;
2333 collected = g_slist_next (collected)) {
2334 GstMatroskaPad *collect_pad;
2336 collect_pad = (GstMatroskaPad *) collected->data;
2337 /* fetch a new buffer if needed */
2338 if (collect_pad->buffer == NULL) {
2339 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2340 (GstCollectData *) collect_pad);
2342 if (collect_pad->buffer != NULL)
2346 /* if we have a buffer check if it is better then the current best one */
2347 if (collect_pad->buffer != NULL) {
2348 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2349 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2350 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2351 GST_BUFFER_TIMESTAMP (best->buffer))) {
2361 * gst_matroska_mux_buffer_header:
2362 * @track: Track context.
2363 * @relative_timestamp: relative timestamp of the buffer
2364 * @flags: Buffer flags.
2366 * Create a buffer containing buffer header.
2368 * Returns: New buffer.
2371 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2372 gint16 relative_timestamp, int flags)
2376 hdr = gst_buffer_new_and_alloc (4);
2377 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2378 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2379 /* time relative to clustertime */
2380 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2383 GST_BUFFER_DATA (hdr)[3] = flags;
2388 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2389 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2390 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2393 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2394 GstMatroskaPad * collect_pad, GstBuffer * buf)
2396 GstMatroskaTrackVideoContext *ctx =
2397 (GstMatroskaTrackVideoContext *) collect_pad->track;
2398 const guint8 *data = GST_BUFFER_DATA (buf);
2399 guint size = GST_BUFFER_SIZE (buf);
2401 guint32 next_parse_offset;
2402 GstBuffer *ret = NULL;
2403 gboolean is_muxing_unit = FALSE;
2405 if (GST_BUFFER_SIZE (buf) < 13) {
2406 gst_buffer_unref (buf);
2410 /* Check if this buffer contains a picture or end-of-sequence packet */
2411 while (size >= 13) {
2412 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2413 gst_buffer_unref (buf);
2417 parse_code = GST_READ_UINT8 (data + 4);
2418 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2419 if (ctx->dirac_unit) {
2420 gst_buffer_unref (ctx->dirac_unit);
2421 ctx->dirac_unit = NULL;
2423 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2424 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2425 is_muxing_unit = TRUE;
2429 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2431 if (G_UNLIKELY (next_parse_offset == 0))
2434 data += next_parse_offset;
2435 size -= next_parse_offset;
2438 if (ctx->dirac_unit)
2439 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2441 ctx->dirac_unit = gst_buffer_ref (buf);
2443 if (is_muxing_unit) {
2444 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2445 ctx->dirac_unit = NULL;
2446 gst_buffer_copy_metadata (ret, buf,
2447 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2448 GST_BUFFER_COPY_CAPS);
2449 gst_buffer_unref (buf);
2451 gst_buffer_unref (buf);
2459 * gst_matroska_mux_write_data:
2460 * @mux: #GstMatroskaMux
2461 * @collect_pad: #GstMatroskaPad with the data
2463 * Write collected data (called from gst_matroska_mux_collected).
2465 * Returns: Result of the gst_pad_push issued to write the data.
2467 static GstFlowReturn
2468 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2470 GstEbmlWrite *ebml = mux->ebml_write;
2471 GstBuffer *buf, *hdr;
2473 gboolean write_duration;
2474 gint16 relative_timestamp;
2475 gint64 relative_timestamp64;
2476 guint64 block_duration;
2477 gboolean is_video_keyframe = FALSE;
2480 buf = collect_pad->buffer;
2481 collect_pad->buffer = NULL;
2483 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2484 if (collect_pad->track->xiph_headers_to_skip > 0) {
2485 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2486 gst_buffer_unref (buf);
2487 --collect_pad->track->xiph_headers_to_skip;
2491 /* for dirac we have to queue up everything up to a picture unit */
2492 if (collect_pad->track->codec_id != NULL &&
2493 strcmp (collect_pad->track->codec_id,
2494 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2495 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2500 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2501 * this would wreak havoc with time stored in matroska file */
2502 /* TODO: maybe calculate a timestamp by using the previous timestamp
2503 * and default duration */
2504 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2505 GST_WARNING_OBJECT (collect_pad->collect.pad,
2506 "Invalid buffer timestamp; dropping buffer");
2507 gst_buffer_unref (buf);
2511 /* set the timestamp for outgoing buffers */
2512 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2514 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2515 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2516 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2517 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2518 is_video_keyframe = TRUE;
2522 /* start a new cluster every two seconds or at keyframe */
2523 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2524 || is_video_keyframe) {
2526 gst_ebml_write_master_finish (ebml, mux->cluster);
2527 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2528 mux->cluster_pos = ebml->pos;
2530 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2531 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2532 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2533 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2534 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2535 mux->prev_cluster_size);
2540 mux->cluster_pos = ebml->pos;
2541 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2542 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2543 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2544 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2547 /* update duration of this track */
2548 if (GST_BUFFER_DURATION_IS_VALID (buf))
2549 collect_pad->duration += GST_BUFFER_DURATION (buf);
2551 /* We currently write index entries for all video tracks or for the audio
2552 * track in a single-track audio file. This could be improved by keeping the
2553 * index only for the *first* video track. */
2555 /* TODO: index is useful for every track, should contain the number of
2556 * the block in the cluster which contains the timestamp, should also work
2557 * for files with multiple audio tracks.
2559 if (is_video_keyframe ||
2560 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2561 (mux->num_streams == 1))) {
2564 if (mux->min_index_interval != 0) {
2565 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2566 if (mux->index[last_idx].track == collect_pad->track->num)
2571 if (last_idx < 0 || mux->min_index_interval == 0 ||
2572 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2573 >= mux->min_index_interval)) {
2574 GstMatroskaIndex *idx;
2576 if (mux->num_indexes % 32 == 0) {
2577 mux->index = g_renew (GstMatroskaIndex, mux->index,
2578 mux->num_indexes + 32);
2580 idx = &mux->index[mux->num_indexes++];
2582 idx->pos = mux->cluster_pos;
2583 idx->time = GST_BUFFER_TIMESTAMP (buf);
2584 idx->track = collect_pad->track->num;
2588 /* Check if the duration differs from the default duration. */
2589 write_duration = FALSE;
2590 block_duration = GST_BUFFER_DURATION (buf);
2591 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2592 if (block_duration != collect_pad->track->default_duration) {
2593 write_duration = TRUE;
2597 /* write the block, for doctype v2 use SimpleBlock if possible
2598 * one slice (*breath*).
2599 * FIXME: Need to do correct lacing! */
2600 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2601 if (relative_timestamp64 >= 0) {
2602 /* round the timestamp */
2603 relative_timestamp64 += mux->time_scale / 2;
2605 /* round the timestamp */
2606 relative_timestamp64 -= mux->time_scale / 2;
2608 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2609 if (mux->doctype_version > 1 && !write_duration) {
2611 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2614 gst_matroska_mux_create_buffer_header (collect_pad->track,
2615 relative_timestamp, flags);
2616 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2617 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2618 gst_ebml_write_buffer (ebml, hdr);
2619 gst_ebml_write_buffer (ebml, buf);
2621 return gst_ebml_last_write_result (ebml);
2623 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2625 gst_matroska_mux_create_buffer_header (collect_pad->track,
2626 relative_timestamp, 0);
2627 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2628 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2629 gst_ebml_write_buffer (ebml, hdr);
2630 gst_ebml_write_buffer (ebml, buf);
2631 if (write_duration) {
2632 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2633 block_duration / mux->time_scale);
2635 gst_ebml_write_master_finish (ebml, blockgroup);
2636 return gst_ebml_last_write_result (ebml);
2642 * gst_matroska_mux_collected:
2643 * @pads: #GstCollectPads
2644 * @uuser_data: #GstMatroskaMux
2646 * Collectpads callback.
2648 * Returns: #GstFlowReturn
2650 static GstFlowReturn
2651 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2653 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2654 GstMatroskaPad *best;
2658 GST_DEBUG_OBJECT (mux, "Collected pads");
2660 /* start with a header */
2661 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2662 if (mux->collect->data == NULL) {
2663 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2664 ("No input streams configured"));
2665 return GST_FLOW_ERROR;
2667 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2668 gst_matroska_mux_start (mux);
2669 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2673 /* which stream to write from? */
2674 best = gst_matroska_mux_best_pad (mux, &popped);
2676 /* if there is no best pad, we have reached EOS */
2678 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2679 gst_matroska_mux_finish (mux);
2680 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2681 ret = GST_FLOW_UNEXPECTED;
2684 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2685 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2686 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2687 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2689 /* make note of first and last encountered timestamps, so we can calculate
2690 * the actual duration later when we send an updated header on eos */
2691 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2692 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2693 GstClockTime end_ts = start_ts;
2695 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2696 end_ts += GST_BUFFER_DURATION (best->buffer);
2697 else if (best->track->default_duration)
2698 end_ts += best->track->default_duration;
2700 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2701 best->end_ts = end_ts;
2703 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2704 start_ts < best->start_ts))
2705 best->start_ts = start_ts;
2708 /* write one buffer */
2709 ret = gst_matroska_mux_write_data (mux, best);
2710 } while (ret == GST_FLOW_OK && !popped);
2717 * gst_matroska_mux_change_state:
2718 * @element: #GstMatroskaMux
2719 * @transition: State change transition.
2721 * Change the muxer state.
2723 * Returns: #GstStateChangeReturn
2725 static GstStateChangeReturn
2726 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2728 GstStateChangeReturn ret;
2729 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2731 switch (transition) {
2732 case GST_STATE_CHANGE_NULL_TO_READY:
2734 case GST_STATE_CHANGE_READY_TO_PAUSED:
2735 gst_collect_pads_start (mux->collect);
2737 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2739 case GST_STATE_CHANGE_PAUSED_TO_READY:
2740 gst_collect_pads_stop (mux->collect);
2746 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2748 switch (transition) {
2749 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2751 case GST_STATE_CHANGE_PAUSED_TO_READY:
2752 gst_matroska_mux_reset (GST_ELEMENT (mux));
2754 case GST_STATE_CHANGE_READY_TO_NULL:
2764 gst_matroska_mux_set_property (GObject * object,
2765 guint prop_id, const GValue * value, GParamSpec * pspec)
2767 GstMatroskaMux *mux;
2769 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2770 mux = GST_MATROSKA_MUX (object);
2773 case ARG_WRITING_APP:
2774 if (!g_value_get_string (value)) {
2775 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2778 g_free (mux->writing_app);
2779 mux->writing_app = g_value_dup_string (value);
2781 case ARG_DOCTYPE_VERSION:
2782 mux->doctype_version = g_value_get_int (value);
2784 case ARG_MIN_INDEX_INTERVAL:
2785 mux->min_index_interval = g_value_get_int64 (value);
2788 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2794 gst_matroska_mux_get_property (GObject * object,
2795 guint prop_id, GValue * value, GParamSpec * pspec)
2797 GstMatroskaMux *mux;
2799 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2800 mux = GST_MATROSKA_MUX (object);
2803 case ARG_WRITING_APP:
2804 g_value_set_string (value, mux->writing_app);
2806 case ARG_DOCTYPE_VERSION:
2807 g_value_set_int (value, mux->doctype_version);
2809 case ARG_MIN_INDEX_INTERVAL:
2810 g_value_set_int64 (value, mux->min_index_interval);
2813 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2818 #define parent_class webm_parent_class
2820 GType gst_webm_mux_get_type (void);
2822 typedef GstMatroskaMux GstWebMMux;
2823 typedef GstMatroskaMuxClass GstWebMMuxClass;
2824 #define GST_TYPE_WEBM_MUX \
2825 (gst_webm_mux_get_type ())
2826 #define GST_WEBM_MUX(obj) \
2827 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WEBM_MUX, GstWebMMux))
2828 #define GST_WEBM_MUX_CLASS(klass) \
2829 (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WEBM_MUX, GstWebMMuxClass))
2830 #define GST_IS_WEBM_MUX(obj) \
2831 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WEBM_MUX))
2832 #define GST_IS_WEBM_MUX_CLASS(klass) \
2833 (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WEBM_MUX))
2835 GST_BOILERPLATE (GstWebMMux, gst_webm_mux, GstMatroskaMux,
2836 GST_TYPE_MATROSKA_MUX);
2838 static GstStaticPadTemplate webm_src_templ = GST_STATIC_PAD_TEMPLATE ("src",
2841 GST_STATIC_CAPS ("video/webm")
2844 static GstStaticPadTemplate webm_videosink_templ =
2845 GST_STATIC_PAD_TEMPLATE ("video_%d",
2848 GST_STATIC_CAPS ("video/x-vp8, " COMMON_VIDEO_CAPS)
2851 static GstStaticPadTemplate webm_audiosink_templ =
2852 GST_STATIC_PAD_TEMPLATE ("audio_%d",
2855 GST_STATIC_CAPS ("audio/x-vorbis, " COMMON_AUDIO_CAPS)
2859 gst_webm_mux_base_init (gpointer g_class)
2864 gst_webm_mux_class_init (GstWebMMuxClass * klass)
2866 GstElementClass *gstelement_class = (GstElementClass *) klass;
2868 gst_element_class_add_pad_template (gstelement_class,
2869 gst_static_pad_template_get (&webm_videosink_templ));
2870 gst_element_class_add_pad_template (gstelement_class,
2871 gst_static_pad_template_get (&webm_audiosink_templ));
2872 gst_element_class_add_pad_template (gstelement_class,
2873 gst_static_pad_template_get (&webm_src_templ));
2874 gst_element_class_set_details_simple (gstelement_class, "WebM muxer",
2876 "Muxes video/audio/subtitle streams into a WebM stream",
2877 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
2881 gst_webm_mux_init (GstWebMMux * mux, GstWebMMuxClass * g_class)
2883 mux->doctype = "webm";
2887 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2889 return gst_element_register (plugin, "matroskamux",
2890 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX) &&
2891 gst_element_register (plugin, "webmmux",
2892 GST_RANK_PRIMARY, GST_TYPE_WEBM_MUX);