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
66 ARG_MIN_INDEX_INTERVAL
69 #define DEFAULT_DOCTYPE GST_MATROSKA_DOCTYPE_MATROSKA
70 #define DEFAULT_DOCTYPE_VERSION 1
71 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
72 #define DEFAULT_MIN_INDEX_INTERVAL 0
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 GType gst_matroska_mux_get_type (void);
210 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
211 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
213 /* Matroska muxer destructor */
214 static void gst_matroska_mux_finalize (GObject * object);
216 /* Pads collected callback */
218 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
221 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
223 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
224 GstPadTemplate * templ, const gchar * name);
225 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
227 /* gst internal change state handler */
228 static GstStateChangeReturn
229 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
231 /* gobject bla bla */
232 static void gst_matroska_mux_set_property (GObject * object,
233 guint prop_id, const GValue * value, GParamSpec * pspec);
234 static void gst_matroska_mux_get_property (GObject * object,
235 guint prop_id, GValue * value, GParamSpec * pspec);
238 static void gst_matroska_mux_reset (GstElement * element);
241 static guint64 gst_matroska_mux_create_uid ();
243 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
244 GstMatroskaTrackContext * context);
245 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
246 GstMatroskaTrackContext * context);
247 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
248 GstMatroskaTrackContext * context);
249 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
250 GstMatroskaTrackContext * context);
251 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
252 GstMatroskaTrackContext * context);
255 gst_matroska_mux_add_interfaces (GType type)
257 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
259 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
263 gst_matroska_mux_base_init (gpointer g_class)
265 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
267 gst_element_class_add_pad_template (element_class,
268 gst_static_pad_template_get (&videosink_templ));
269 gst_element_class_add_pad_template (element_class,
270 gst_static_pad_template_get (&audiosink_templ));
271 gst_element_class_add_pad_template (element_class,
272 gst_static_pad_template_get (&subtitlesink_templ));
273 gst_element_class_add_pad_template (element_class,
274 gst_static_pad_template_get (&src_templ));
275 gst_element_class_set_details_simple (element_class, "Matroska muxer",
277 "Muxes video/audio/subtitle streams into a matroska stream",
278 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
280 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
285 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
287 GObjectClass *gobject_class;
288 GstElementClass *gstelement_class;
290 gobject_class = (GObjectClass *) klass;
291 gstelement_class = (GstElementClass *) klass;
293 gobject_class->finalize = gst_matroska_mux_finalize;
295 gobject_class->get_property = gst_matroska_mux_get_property;
296 gobject_class->set_property = gst_matroska_mux_set_property;
298 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
299 g_param_spec_string ("writing-app", "Writing application.",
300 "The name the application that creates the matroska file.",
301 NULL, G_PARAM_READWRITE));
302 g_object_class_install_property (gobject_class, ARG_DOCTYPE,
303 g_param_spec_enum ("doctype", "DocType.",
304 "The type of document.", GST_TYPE_MATROSKA_DOCTYPE,
305 DEFAULT_DOCTYPE, G_PARAM_READWRITE));
306 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
307 g_param_spec_int ("version", "DocType version",
308 "This parameter determines what matroska features can be used.",
309 1, 2, DEFAULT_DOCTYPE_VERSION, G_PARAM_READWRITE));
310 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
311 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
312 "entries", "An index entry is created every so many nanoseconds.",
313 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
315 gstelement_class->change_state =
316 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
317 gstelement_class->request_new_pad =
318 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
319 gstelement_class->release_pad =
320 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
325 * gst_matroska_mux_init:
326 * @mux: #GstMatroskaMux that should be initialized.
327 * @g_class: Class of the muxer.
329 * Matroska muxer constructor.
332 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
334 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
335 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
336 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
338 mux->collect = gst_collect_pads_new ();
339 gst_collect_pads_set_function (mux->collect,
340 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
343 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
345 /* property defaults */
346 mux->doctype = DEFAULT_DOCTYPE;
347 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
348 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
349 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
351 /* initialize internal variables */
353 mux->num_streams = 0;
354 mux->num_a_streams = 0;
355 mux->num_t_streams = 0;
356 mux->num_v_streams = 0;
358 /* initialize remaining variables */
359 gst_matroska_mux_reset (GST_ELEMENT (mux));
364 * gst_matroska_mux_finalize:
365 * @object: #GstMatroskaMux that should be finalized.
367 * Finalize matroska muxer.
370 gst_matroska_mux_finalize (GObject * object)
372 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
374 gst_object_unref (mux->collect);
375 gst_object_unref (mux->ebml_write);
376 if (mux->writing_app)
377 g_free (mux->writing_app);
379 G_OBJECT_CLASS (parent_class)->finalize (object);
384 * gst_matroska_mux_create_uid:
386 * Generate new unused track UID.
388 * Returns: New track UID.
391 gst_matroska_mux_create_uid (void)
398 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
403 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
404 for (i = 0; i < used_uids->len; i++) {
405 if (g_array_index (used_uids, guint64, i) == uid) {
410 g_array_append_val (used_uids, uid);
413 G_UNLOCK (used_uids);
419 * gst_matroska_pad_reset:
420 * @collect_pad: the #GstMatroskaPad
422 * Reset and/or release resources of a matroska collect pad.
425 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
428 GstMatroskaTrackType type = 0;
430 /* free track information */
431 if (collect_pad->track != NULL) {
432 /* retrieve for optional later use */
433 name = collect_pad->track->name;
434 type = collect_pad->track->type;
435 /* extra for video */
436 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
437 GstMatroskaTrackVideoContext *ctx =
438 (GstMatroskaTrackVideoContext *) collect_pad->track;
440 if (ctx->dirac_unit) {
441 gst_buffer_unref (ctx->dirac_unit);
442 ctx->dirac_unit = NULL;
445 g_free (collect_pad->track->codec_id);
446 g_free (collect_pad->track->codec_name);
448 g_free (collect_pad->track->name);
449 g_free (collect_pad->track->language);
450 g_free (collect_pad->track->codec_priv);
451 g_free (collect_pad->track);
452 collect_pad->track = NULL;
455 /* free cached buffer */
456 if (collect_pad->buffer != NULL) {
457 gst_buffer_unref (collect_pad->buffer);
458 collect_pad->buffer = NULL;
461 if (!full && type != 0) {
462 GstMatroskaTrackContext *context;
464 /* create a fresh context */
466 case GST_MATROSKA_TRACK_TYPE_VIDEO:
467 context = (GstMatroskaTrackContext *)
468 g_new0 (GstMatroskaTrackVideoContext, 1);
470 case GST_MATROSKA_TRACK_TYPE_AUDIO:
471 context = (GstMatroskaTrackContext *)
472 g_new0 (GstMatroskaTrackAudioContext, 1);
474 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
475 context = (GstMatroskaTrackContext *)
476 g_new0 (GstMatroskaTrackSubtitleContext, 1);
479 g_assert_not_reached ();
483 context->type = type;
484 context->name = name;
485 /* TODO: check default values for the context */
486 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
487 collect_pad->track = context;
488 collect_pad->buffer = NULL;
489 collect_pad->duration = 0;
490 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
491 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
496 * gst_matroska_pad_free:
497 * @collect_pad: the #GstMatroskaPad
499 * Release resources of a matroska collect pad.
502 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
504 gst_matroska_pad_reset (collect_pad, TRUE);
509 * gst_matroska_mux_reset:
510 * @element: #GstMatroskaMux that should be reseted.
512 * Reset matroska muxer back to initial state.
515 gst_matroska_mux_reset (GstElement * element)
517 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
520 /* reset EBML write */
521 gst_ebml_write_reset (mux->ebml_write);
524 mux->state = GST_MATROSKA_MUX_STATE_START;
526 /* clean up existing streams */
528 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
529 GstMatroskaPad *collect_pad;
531 collect_pad = (GstMatroskaPad *) walk->data;
533 /* reset collect pad to pristine state */
534 gst_matroska_pad_reset (collect_pad, FALSE);
538 mux->num_indexes = 0;
543 mux->time_scale = GST_MSECOND;
548 mux->cluster_time = 0;
549 mux->cluster_pos = 0;
550 mux->prev_cluster_size = 0;
553 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
557 * gst_matroska_mux_handle_src_event:
558 * @pad: Pad which received the event.
559 * @event: Received event.
561 * handle events - copied from oggmux without understanding
563 * Returns: #TRUE on success.
566 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
570 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
574 /* disable seeking for now */
580 return gst_pad_event_default (pad, event);
584 * gst_matroska_mux_handle_sink_event:
585 * @pad: Pad which received the event.
586 * @event: Received event.
588 * handle events - informational ones like tags
590 * Returns: #TRUE on success.
593 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
595 GstMatroskaTrackContext *context;
596 GstMatroskaPad *collect_pad;
601 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
603 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
604 switch (GST_EVENT_TYPE (event)) {
608 GST_DEBUG_OBJECT (mux, "received tag event");
609 gst_event_parse_tag (event, &list);
611 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
612 g_assert (collect_pad);
613 context = collect_pad->track;
616 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
617 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
618 const gchar *lang_code;
620 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
622 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
623 context->language = g_strdup (lang_code);
625 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
630 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
631 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
634 case GST_EVENT_NEWSEGMENT:
635 /* We don't support NEWSEGMENT events */
637 gst_event_unref (event);
643 /* now GstCollectPads can take care of the rest, e.g. EOS */
645 ret = mux->collect_event (pad, event);
646 gst_object_unref (mux);
653 * gst_matroska_mux_video_pad_setcaps:
654 * @pad: Pad which got the caps.
657 * Setcaps function for video sink pad.
659 * Returns: #TRUE on success.
662 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
664 GstMatroskaTrackContext *context = NULL;
665 GstMatroskaTrackVideoContext *videocontext;
667 GstMatroskaPad *collect_pad;
668 GstStructure *structure;
669 const gchar *mimetype;
670 const GValue *value = NULL;
671 const GstBuffer *codec_buf = NULL;
672 gint width, height, pixel_width, pixel_height;
674 gboolean interlaced = FALSE;
676 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
679 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
680 g_assert (collect_pad);
681 context = collect_pad->track;
683 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
684 videocontext = (GstMatroskaTrackVideoContext *) context;
686 /* gst -> matroska ID'ing */
687 structure = gst_caps_get_structure (caps, 0);
689 mimetype = gst_structure_get_name (structure);
691 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
693 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
695 if (!strcmp (mimetype, "video/x-theora")) {
696 /* we'll extract the details later from the theora identification header */
700 /* get general properties */
701 /* spec says it is mandatory */
702 if (!gst_structure_get_int (structure, "width", &width) ||
703 !gst_structure_get_int (structure, "height", &height))
706 videocontext->pixel_width = width;
707 videocontext->pixel_height = height;
708 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
710 context->default_duration =
711 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
712 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
713 GST_TIME_ARGS (context->default_duration));
715 context->default_duration = 0;
717 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
718 &pixel_width, &pixel_height)) {
719 if (pixel_width > pixel_height) {
720 videocontext->display_width = width * pixel_width / pixel_height;
721 videocontext->display_height = height;
722 } else if (pixel_width < pixel_height) {
723 videocontext->display_width = width;
724 videocontext->display_height = height * pixel_height / pixel_width;
726 videocontext->display_width = 0;
727 videocontext->display_height = 0;
730 videocontext->display_width = 0;
731 videocontext->display_height = 0;
736 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
737 videocontext->fourcc = 0;
739 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
740 * data and other settings
744 /* extract codec_data, may turn out needed */
745 value = gst_structure_get_value (structure, "codec_data");
747 codec_buf = gst_value_get_buffer (value);
750 if (!strcmp (mimetype, "video/x-raw-yuv")) {
751 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
752 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
753 } else if (!strcmp (mimetype, "image/jpeg")) {
754 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
755 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
756 ||!strcmp (mimetype, "video/x-huffyuv")
757 || !strcmp (mimetype, "video/x-divx")
758 || !strcmp (mimetype, "video/x-dv")
759 || !strcmp (mimetype, "video/x-h263")
760 || !strcmp (mimetype, "video/x-msmpeg")
761 || !strcmp (mimetype, "video/x-wmv")) {
762 gst_riff_strf_vids *bih;
763 gint size = sizeof (gst_riff_strf_vids);
766 if (!strcmp (mimetype, "video/x-xvid"))
767 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
768 else if (!strcmp (mimetype, "video/x-huffyuv"))
769 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
770 else if (!strcmp (mimetype, "video/x-dv"))
771 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
772 else if (!strcmp (mimetype, "video/x-h263"))
773 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
774 else if (!strcmp (mimetype, "video/x-divx")) {
777 gst_structure_get_int (structure, "divxversion", &divxversion);
778 switch (divxversion) {
780 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
783 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
786 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
789 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
792 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
793 switch (msmpegversion) {
795 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
798 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
804 } else if (!strcmp (mimetype, "video/x-wmv")) {
807 if (gst_structure_get_fourcc (structure, "format", &format)) {
809 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
810 if (wmvversion == 2) {
811 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
812 } else if (wmvversion == 1) {
813 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
814 } else if (wmvversion == 3) {
815 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
823 bih = g_new0 (gst_riff_strf_vids, 1);
824 GST_WRITE_UINT32_LE (&bih->size, size);
825 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
826 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
827 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
828 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
829 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
830 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
831 videocontext->pixel_height * 3);
833 /* process codec private/initialization data, if any */
835 size += GST_BUFFER_SIZE (codec_buf);
836 bih = g_realloc (bih, size);
837 GST_WRITE_UINT32_LE (&bih->size, size);
838 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
839 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
842 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
843 context->codec_priv = (gpointer) bih;
844 context->codec_priv_size = size;
845 } else if (!strcmp (mimetype, "video/x-h264")) {
846 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
848 if (context->codec_priv != NULL) {
849 g_free (context->codec_priv);
850 context->codec_priv = NULL;
851 context->codec_priv_size = 0;
854 /* Create avcC header */
855 if (codec_buf != NULL) {
856 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
857 context->codec_priv = g_malloc0 (context->codec_priv_size);
858 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
859 context->codec_priv_size);
861 } else if (!strcmp (mimetype, "video/x-theora")) {
862 const GValue *streamheader;
864 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
866 if (context->codec_priv != NULL) {
867 g_free (context->codec_priv);
868 context->codec_priv = NULL;
869 context->codec_priv_size = 0;
872 streamheader = gst_structure_get_value (structure, "streamheader");
873 if (!theora_streamheader_to_codecdata (streamheader, context)) {
874 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
875 ("theora stream headers missing or malformed"));
878 } else if (!strcmp (mimetype, "video/x-dirac")) {
879 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
880 } else if (!strcmp (mimetype, "video/x-vp8")) {
881 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
882 } else if (!strcmp (mimetype, "video/mpeg")) {
885 gst_structure_get_int (structure, "mpegversion", &mpegversion);
886 switch (mpegversion) {
888 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
891 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
894 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
900 /* global headers may be in codec data */
901 if (codec_buf != NULL) {
902 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
903 context->codec_priv = g_malloc0 (context->codec_priv_size);
904 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
905 context->codec_priv_size);
907 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
909 /* can only make it here if preceding case verified it was version 3 */
910 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
911 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
913 const GValue *mdpr_data;
915 gst_structure_get_int (structure, "rmversion", &rmversion);
918 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
921 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
924 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
927 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
933 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
934 if (mdpr_data != NULL) {
935 guint8 *priv_data = NULL;
936 guint priv_data_size = 0;
938 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
940 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
941 priv_data = g_malloc0 (priv_data_size);
943 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
945 context->codec_priv = priv_data;
946 context->codec_priv_size = priv_data_size;
955 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
956 GST_PAD_NAME (pad), caps);
961 /* N > 0 to expect a particular number of headers, negative if the
962 number of headers is variable */
964 xiphN_streamheader_to_codecdata (const GValue * streamheader,
965 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
967 GstBuffer **buf = NULL;
970 guint bufi, i, offset, priv_data_size;
972 if (streamheader == NULL)
973 goto no_stream_headers;
975 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
978 bufarr = g_value_peek_pointer (streamheader);
979 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
981 if (N > 0 && bufarr->len != N)
984 context->xiph_headers_to_skip = bufarr->len;
986 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
987 for (i = 0; i < bufarr->len; i++) {
988 GValue *bufval = &g_array_index (bufarr, GValue, i);
990 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
992 goto wrong_content_type;
995 buf[i] = g_value_peek_pointer (bufval);
999 if (bufarr->len > 0) {
1000 for (i = 0; i < bufarr->len - 1; i++) {
1001 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1005 for (i = 0; i < bufarr->len; ++i) {
1006 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1009 priv_data = g_malloc0 (priv_data_size);
1011 priv_data[0] = bufarr->len - 1;
1014 if (bufarr->len > 0) {
1015 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1016 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1017 priv_data[offset++] = 0xff;
1019 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1023 for (i = 0; i < bufarr->len; ++i) {
1024 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1025 GST_BUFFER_SIZE (buf[i]));
1026 offset += GST_BUFFER_SIZE (buf[i]);
1029 context->codec_priv = priv_data;
1030 context->codec_priv_size = priv_data_size;
1033 *p_buf0 = gst_buffer_ref (buf[0]);
1042 GST_WARNING ("required streamheaders missing in sink caps!");
1047 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1048 G_VALUE_TYPE_NAME (streamheader));
1053 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1058 GST_WARNING ("streamheaders array does not contain GstBuffers");
1064 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1065 GstMatroskaTrackContext * context)
1067 GstBuffer *buf0 = NULL;
1069 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1072 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1073 GST_WARNING ("First vorbis header too small, ignoring");
1075 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1076 GstMatroskaTrackAudioContext *audiocontext;
1079 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1080 audiocontext = (GstMatroskaTrackAudioContext *) context;
1081 audiocontext->channels = GST_READ_UINT8 (hdr);
1082 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1087 gst_buffer_unref (buf0);
1093 theora_streamheader_to_codecdata (const GValue * streamheader,
1094 GstMatroskaTrackContext * context)
1096 GstBuffer *buf0 = NULL;
1098 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1101 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1102 GST_WARNING ("First theora header too small, ignoring");
1103 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1104 GST_WARNING ("First header not a theora identification header, ignoring");
1106 GstMatroskaTrackVideoContext *videocontext;
1107 guint fps_num, fps_denom, par_num, par_denom;
1110 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1112 videocontext = (GstMatroskaTrackVideoContext *) context;
1113 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1114 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1115 hdr += 3 + 3 + 1 + 1;
1116 fps_num = GST_READ_UINT32_BE (hdr);
1117 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1118 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1119 fps_denom, fps_num);
1121 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1122 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1123 if (par_num > 0 && par_num > 0) {
1124 if (par_num > par_denom) {
1125 videocontext->display_width =
1126 videocontext->pixel_width * par_num / par_denom;
1127 videocontext->display_height = videocontext->pixel_height;
1128 } else if (par_num < par_denom) {
1129 videocontext->display_width = videocontext->pixel_width;
1130 videocontext->display_height =
1131 videocontext->pixel_height * par_denom / par_num;
1133 videocontext->display_width = 0;
1134 videocontext->display_height = 0;
1137 videocontext->display_width = 0;
1138 videocontext->display_height = 0;
1144 gst_buffer_unref (buf0);
1150 kate_streamheader_to_codecdata (const GValue * streamheader,
1151 GstMatroskaTrackContext * context)
1153 GstBuffer *buf0 = NULL;
1155 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1158 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1159 GST_WARNING ("First kate header too small, ignoring");
1160 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1161 GST_WARNING ("First header not a kate identification header, ignoring");
1165 gst_buffer_unref (buf0);
1171 flac_streamheader_to_codecdata (const GValue * streamheader,
1172 GstMatroskaTrackContext * context)
1179 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1180 GST_WARNING ("No or invalid streamheader field in the caps");
1184 bufarr = g_value_peek_pointer (streamheader);
1185 if (bufarr->len < 2) {
1186 GST_WARNING ("Too few headers in streamheader field");
1190 context->xiph_headers_to_skip = bufarr->len + 1;
1192 bufval = &g_array_index (bufarr, GValue, 0);
1193 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1194 GST_WARNING ("streamheaders array does not contain GstBuffers");
1198 buffer = g_value_peek_pointer (bufval);
1200 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1201 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1202 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1203 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1204 GST_WARNING ("Invalid streamheader for FLAC");
1208 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1209 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1210 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1211 GST_BUFFER_SIZE (buffer) - 9);
1213 for (i = 1; i < bufarr->len; i++) {
1214 bufval = &g_array_index (bufarr, GValue, i);
1216 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1217 g_free (context->codec_priv);
1218 context->codec_priv = NULL;
1219 context->codec_priv_size = 0;
1220 GST_WARNING ("streamheaders array does not contain GstBuffers");
1224 buffer = g_value_peek_pointer (bufval);
1226 context->codec_priv =
1227 g_realloc (context->codec_priv,
1228 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1229 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1230 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1231 context->codec_priv_size =
1232 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1239 speex_streamheader_to_codecdata (const GValue * streamheader,
1240 GstMatroskaTrackContext * context)
1246 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1247 GST_WARNING ("No or invalid streamheader field in the caps");
1251 bufarr = g_value_peek_pointer (streamheader);
1252 if (bufarr->len != 2) {
1253 GST_WARNING ("Too few headers in streamheader field");
1257 context->xiph_headers_to_skip = bufarr->len + 1;
1259 bufval = &g_array_index (bufarr, GValue, 0);
1260 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1261 GST_WARNING ("streamheaders array does not contain GstBuffers");
1265 buffer = g_value_peek_pointer (bufval);
1267 if (GST_BUFFER_SIZE (buffer) < 80
1268 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1269 GST_WARNING ("Invalid streamheader for Speex");
1273 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1274 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1275 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1276 GST_BUFFER_SIZE (buffer));
1278 bufval = &g_array_index (bufarr, GValue, 1);
1280 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1281 g_free (context->codec_priv);
1282 context->codec_priv = NULL;
1283 context->codec_priv_size = 0;
1284 GST_WARNING ("streamheaders array does not contain GstBuffers");
1288 buffer = g_value_peek_pointer (bufval);
1290 context->codec_priv =
1291 g_realloc (context->codec_priv,
1292 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1293 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1294 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1295 context->codec_priv_size =
1296 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1301 static const gchar *
1302 aac_codec_data_to_codec_id (const GstBuffer * buf)
1304 const gchar *result;
1307 /* default to MAIN */
1310 if (GST_BUFFER_SIZE (buf) >= 2) {
1311 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1329 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1338 * gst_matroska_mux_audio_pad_setcaps:
1339 * @pad: Pad which got the caps.
1342 * Setcaps function for audio sink pad.
1344 * Returns: #TRUE on success.
1347 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1349 GstMatroskaTrackContext *context = NULL;
1350 GstMatroskaTrackAudioContext *audiocontext;
1351 GstMatroskaMux *mux;
1352 GstMatroskaPad *collect_pad;
1353 const gchar *mimetype;
1354 gint samplerate = 0, channels = 0;
1355 GstStructure *structure;
1356 const GValue *codec_data = NULL;
1357 const GstBuffer *buf = NULL;
1358 const gchar *stream_format = NULL;
1360 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1363 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1364 g_assert (collect_pad);
1365 context = collect_pad->track;
1367 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1368 audiocontext = (GstMatroskaTrackAudioContext *) context;
1370 structure = gst_caps_get_structure (caps, 0);
1371 mimetype = gst_structure_get_name (structure);
1374 gst_structure_get_int (structure, "rate", &samplerate);
1375 gst_structure_get_int (structure, "channels", &channels);
1377 audiocontext->samplerate = samplerate;
1378 audiocontext->channels = channels;
1379 audiocontext->bitdepth = 0;
1380 context->default_duration = 0;
1382 codec_data = gst_structure_get_value (structure, "codec_data");
1384 buf = gst_value_get_buffer (codec_data);
1386 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1387 * data and other settings
1391 if (!strcmp (mimetype, "audio/mpeg")) {
1392 gint mpegversion = 0;
1394 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1395 switch (mpegversion) {
1401 gst_structure_get_int (structure, "layer", &layer);
1403 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1404 GST_WARNING_OBJECT (mux,
1405 "Unable to determine MPEG audio version, assuming 1");
1411 else if (layer == 2)
1413 else if (version == 2)
1418 context->default_duration =
1419 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1423 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1426 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1429 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1438 stream_format = gst_structure_get_string (structure, "stream-format");
1439 /* check this is raw aac */
1440 if (stream_format) {
1441 if (strcmp (stream_format, "raw") != 0) {
1442 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1446 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1451 if (mpegversion == 2)
1453 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1454 aac_codec_data_to_codec_id (buf));
1455 else if (mpegversion == 4)
1457 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1458 aac_codec_data_to_codec_id (buf));
1460 g_assert_not_reached ();
1462 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1469 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1471 gint endianness = G_LITTLE_ENDIAN;
1472 gboolean signedness = TRUE;
1474 if (!gst_structure_get_int (structure, "width", &width) ||
1475 !gst_structure_get_int (structure, "depth", &depth) ||
1476 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1477 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1482 !gst_structure_get_int (structure, "endianness", &endianness)) {
1483 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1487 if (width != depth) {
1488 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1492 /* FIXME: where is this spec'ed out? (tpm) */
1493 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1494 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1498 audiocontext->bitdepth = depth;
1499 if (endianness == G_BIG_ENDIAN)
1500 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1502 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1504 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1507 if (!gst_structure_get_int (structure, "width", &width)) {
1508 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1512 audiocontext->bitdepth = width;
1513 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1515 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1516 const GValue *streamheader;
1518 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1520 if (context->codec_priv != NULL) {
1521 g_free (context->codec_priv);
1522 context->codec_priv = NULL;
1523 context->codec_priv_size = 0;
1526 streamheader = gst_structure_get_value (structure, "streamheader");
1527 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1528 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1529 ("vorbis stream headers missing or malformed"));
1532 } else if (!strcmp (mimetype, "audio/x-flac")) {
1533 const GValue *streamheader;
1535 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1536 if (context->codec_priv != NULL) {
1537 g_free (context->codec_priv);
1538 context->codec_priv = NULL;
1539 context->codec_priv_size = 0;
1542 streamheader = gst_structure_get_value (structure, "streamheader");
1543 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1544 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1545 ("flac stream headers missing or malformed"));
1548 } else if (!strcmp (mimetype, "audio/x-speex")) {
1549 const GValue *streamheader;
1551 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1552 if (context->codec_priv != NULL) {
1553 g_free (context->codec_priv);
1554 context->codec_priv = NULL;
1555 context->codec_priv_size = 0;
1558 streamheader = gst_structure_get_value (structure, "streamheader");
1559 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1560 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1561 ("speex stream headers missing or malformed"));
1564 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1565 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1566 } else if (!strcmp (mimetype, "audio/x-tta")) {
1569 /* TTA frame duration */
1570 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1572 gst_structure_get_int (structure, "width", &width);
1573 audiocontext->bitdepth = width;
1574 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1576 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1578 const GValue *mdpr_data;
1580 gst_structure_get_int (structure, "raversion", &raversion);
1581 switch (raversion) {
1583 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1586 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1589 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1595 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1596 if (mdpr_data != NULL) {
1597 guint8 *priv_data = NULL;
1598 guint priv_data_size = 0;
1600 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1602 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1603 priv_data = g_malloc0 (priv_data_size);
1605 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1607 context->codec_priv = priv_data;
1608 context->codec_priv_size = priv_data_size;
1611 } else if (!strcmp (mimetype, "audio/x-wma")) {
1613 guint codec_priv_size;
1620 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1621 || !gst_structure_get_int (structure, "block_align", &block_align)
1622 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1623 || samplerate == 0 || channels == 0) {
1624 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1625 "channels/rate on WMA caps");
1629 switch (wmaversion) {
1631 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1634 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1637 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1640 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1644 if (gst_structure_get_int (structure, "depth", &depth))
1645 audiocontext->bitdepth = depth;
1647 codec_priv_size = WAVEFORMATEX_SIZE;
1649 codec_priv_size += GST_BUFFER_SIZE (buf);
1651 /* serialize waveformatex structure */
1652 codec_priv = g_malloc0 (codec_priv_size);
1653 GST_WRITE_UINT16_LE (codec_priv, format);
1654 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1655 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1656 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1657 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1658 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1660 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1662 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1664 /* process codec private/initialization data, if any */
1666 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1667 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1670 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1671 context->codec_priv = (gpointer) codec_priv;
1672 context->codec_priv_size = codec_priv_size;
1680 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1681 GST_PAD_NAME (pad), caps);
1688 * gst_matroska_mux_subtitle_pad_setcaps:
1689 * @pad: Pad which got the caps.
1692 * Setcaps function for subtitle sink pad.
1694 * Returns: #TRUE on success.
1697 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1700 * Consider this as boilerplate code for now. There is
1701 * no single subtitle creation element in GStreamer,
1702 * neither do I know how subtitling works at all. */
1704 /* There is now (at least) one such alement (kateenc), and I'm going
1705 to handle it here and claim it works when it can be piped back
1706 through GStreamer and VLC */
1708 GstMatroskaTrackContext *context = NULL;
1709 GstMatroskaTrackSubtitleContext *scontext;
1710 GstMatroskaMux *mux;
1711 GstMatroskaPad *collect_pad;
1712 const gchar *mimetype;
1713 GstStructure *structure;
1715 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1718 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1719 g_assert (collect_pad);
1720 context = collect_pad->track;
1722 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1723 scontext = (GstMatroskaTrackSubtitleContext *) context;
1725 structure = gst_caps_get_structure (caps, 0);
1726 mimetype = gst_structure_get_name (structure);
1729 scontext->check_utf8 = 1;
1730 scontext->invalid_utf8 = 0;
1731 context->default_duration = 0;
1733 /* TODO: - other format than Kate */
1735 if (!strcmp (mimetype, "subtitle/x-kate")) {
1736 const GValue *streamheader;
1738 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1740 if (context->codec_priv != NULL) {
1741 g_free (context->codec_priv);
1742 context->codec_priv = NULL;
1743 context->codec_priv_size = 0;
1746 streamheader = gst_structure_get_value (structure, "streamheader");
1747 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1748 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1749 ("kate stream headers missing or malformed"));
1760 * gst_matroska_mux_request_new_pad:
1761 * @element: #GstMatroskaMux.
1762 * @templ: #GstPadTemplate.
1763 * @pad_name: New pad name.
1765 * Request pad function for sink templates.
1767 * Returns: New #GstPad.
1770 gst_matroska_mux_request_new_pad (GstElement * element,
1771 GstPadTemplate * templ, const gchar * pad_name)
1773 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1774 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1775 GstMatroskaPad *collect_pad;
1776 GstPad *newpad = NULL;
1778 GstPadSetCapsFunction setcapsfunc = NULL;
1779 GstMatroskaTrackContext *context = NULL;
1781 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1782 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1783 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1784 context = (GstMatroskaTrackContext *)
1785 g_new0 (GstMatroskaTrackAudioContext, 1);
1786 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1787 context->name = g_strdup ("Audio");
1788 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1789 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1790 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1791 context = (GstMatroskaTrackContext *)
1792 g_new0 (GstMatroskaTrackVideoContext, 1);
1793 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1794 context->name = g_strdup ("Video");
1795 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1796 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1797 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1798 context = (GstMatroskaTrackContext *)
1799 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1800 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1801 context->name = g_strdup ("Subtitle");
1803 GST_WARNING_OBJECT (mux, "This is not our template!");
1807 newpad = gst_pad_new_from_template (templ, name);
1809 collect_pad = (GstMatroskaPad *)
1810 gst_collect_pads_add_pad_full (mux->collect, newpad,
1811 sizeof (GstMatroskaPad),
1812 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1814 collect_pad->track = context;
1815 gst_matroska_pad_reset (collect_pad, FALSE);
1817 /* FIXME: hacked way to override/extend the event function of
1818 * GstCollectPads; because it sets its own event function giving the
1819 * element no access to events.
1820 * TODO GstCollectPads should really give its 'users' a clean chance to
1821 * properly handle events that are not meant for collectpads itself.
1822 * Perhaps a callback or so, though rejected (?) in #340060.
1823 * This would allow (clean) transcoding of info from demuxer/streams
1824 * to another muxer */
1825 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1826 gst_pad_set_event_function (newpad,
1827 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1829 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1830 gst_pad_set_active (newpad, TRUE);
1831 gst_element_add_pad (element, newpad);
1838 * gst_matroska_mux_release_pad:
1839 * @element: #GstMatroskaMux.
1840 * @pad: Pad to release.
1842 * Release a previously requested pad.
1845 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1847 GstMatroskaMux *mux;
1850 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1852 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1853 GstCollectData *cdata = (GstCollectData *) walk->data;
1854 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1856 if (cdata->pad == pad) {
1857 GstClockTime min_dur; /* observed minimum duration */
1859 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1860 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1861 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1862 if (collect_pad->duration < min_dur)
1863 collect_pad->duration = min_dur;
1866 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1867 mux->duration < collect_pad->duration)
1868 mux->duration = collect_pad->duration;
1874 gst_collect_pads_remove_pad (mux->collect, pad);
1875 if (gst_element_remove_pad (element, pad))
1881 * gst_matroska_mux_track_header:
1882 * @mux: #GstMatroskaMux
1883 * @context: Tack context.
1885 * Write a track header.
1888 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1889 GstMatroskaTrackContext * context)
1891 GstEbmlWrite *ebml = mux->ebml_write;
1894 /* TODO: check if everything necessary is written and check default values */
1896 /* track type goes before the type-specific stuff */
1897 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1898 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1900 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1901 gst_matroska_mux_create_uid ());
1902 if (context->default_duration) {
1903 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1904 context->default_duration);
1906 if (context->language) {
1907 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1911 /* type-specific stuff */
1912 switch (context->type) {
1913 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1914 GstMatroskaTrackVideoContext *videocontext =
1915 (GstMatroskaTrackVideoContext *) context;
1917 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1918 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1919 videocontext->pixel_width);
1920 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1921 videocontext->pixel_height);
1922 if (videocontext->display_width && videocontext->display_height) {
1923 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1924 videocontext->display_width);
1925 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1926 videocontext->display_height);
1928 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1929 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1930 if (videocontext->fourcc) {
1931 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1933 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1934 (gpointer) & fcc_le, 4);
1936 gst_ebml_write_master_finish (ebml, master);
1941 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1942 GstMatroskaTrackAudioContext *audiocontext =
1943 (GstMatroskaTrackAudioContext *) context;
1945 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1946 if (audiocontext->samplerate != 8000)
1947 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1948 audiocontext->samplerate);
1949 if (audiocontext->channels != 1)
1950 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1951 audiocontext->channels);
1952 if (audiocontext->bitdepth) {
1953 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1954 audiocontext->bitdepth);
1956 gst_ebml_write_master_finish (ebml, master);
1962 /* doesn't need type-specific data */
1966 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1967 if (context->codec_priv)
1968 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1969 context->codec_priv, context->codec_priv_size);
1970 /* FIXME: until we have a nice way of getting the codecname
1971 * out of the caps, I'm not going to enable this. Too much
1972 * (useless, double, boring) work... */
1973 /* TODO: Use value from tags if any */
1974 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1975 context->codec_name); */
1976 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1981 * gst_matroska_mux_start:
1982 * @mux: #GstMatroskaMux
1984 * Start a new matroska file (write headers etc...)
1987 gst_matroska_mux_start (GstMatroskaMux * mux)
1989 GstEbmlWrite *ebml = mux->ebml_write;
1990 GEnumClass *doctype_class;
1991 GEnumValue *doctype_value;
1992 const gchar *doctype;
1993 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1994 GST_MATROSKA_ID_TRACKS,
1995 GST_MATROSKA_ID_CUES,
1996 GST_MATROSKA_ID_TAGS,
1999 guint64 master, child;
2003 GstClockTime duration = 0;
2004 guint32 segment_uid[4];
2005 GTimeVal time = { 0, 0 };
2007 /* we start with a EBML header */
2008 doctype_class = g_type_class_ref (GST_TYPE_MATROSKA_DOCTYPE);
2009 doctype_value = g_enum_get_value (doctype_class, mux->doctype);
2010 doctype = doctype_value->value_nick;
2011 g_type_class_unref (doctype_class);
2012 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2013 doctype, mux->doctype_version);
2014 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2016 /* start a segment */
2018 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2019 mux->segment_master = ebml->pos;
2021 /* the rest of the header is cached */
2022 gst_ebml_write_set_cache (ebml, 0x1000);
2024 /* seekhead (table of contents) - we set the positions later */
2025 mux->seekhead_pos = ebml->pos;
2026 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2027 for (i = 0; seekhead_id[i] != 0; i++) {
2028 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2029 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2030 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2031 gst_ebml_write_master_finish (ebml, child);
2033 gst_ebml_write_master_finish (ebml, master);
2036 mux->info_pos = ebml->pos;
2037 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2038 for (i = 0; i < 4; i++) {
2039 segment_uid[i] = g_random_int ();
2041 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2042 (guint8 *) segment_uid, 16);
2043 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2044 mux->duration_pos = ebml->pos;
2046 for (collected = mux->collect->data; collected;
2047 collected = g_slist_next (collected)) {
2048 GstMatroskaPad *collect_pad;
2049 GstFormat format = GST_FORMAT_TIME;
2051 gint64 trackduration;
2053 collect_pad = (GstMatroskaPad *) collected->data;
2054 thepad = collect_pad->collect.pad;
2056 /* Query the total length of the track. */
2057 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2058 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2059 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2060 GST_TIME_ARGS (trackduration));
2061 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2062 duration = (GstClockTime) trackduration;
2066 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2067 gst_guint64_to_gdouble (duration) /
2068 gst_guint64_to_gdouble (mux->time_scale));
2070 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2071 "GStreamer plugin version " PACKAGE_VERSION);
2072 if (mux->writing_app && mux->writing_app[0]) {
2073 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2075 g_get_current_time (&time);
2076 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2077 gst_ebml_write_master_finish (ebml, master);
2080 mux->tracks_pos = ebml->pos;
2081 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2083 for (collected = mux->collect->data; collected;
2084 collected = g_slist_next (collected)) {
2085 GstMatroskaPad *collect_pad;
2088 collect_pad = (GstMatroskaPad *) collected->data;
2089 thepad = collect_pad->collect.pad;
2091 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2092 collect_pad->track->codec_id != 0) {
2093 collect_pad->track->num = tracknum++;
2094 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2095 gst_matroska_mux_track_header (mux, collect_pad->track);
2096 gst_ebml_write_master_finish (ebml, child);
2099 gst_ebml_write_master_finish (ebml, master);
2101 /* lastly, flush the cache */
2102 gst_ebml_write_flush_cache (ebml);
2106 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2109 /* TODO: more sensible tag mappings */
2112 const gchar *matroska_tagname;
2113 const gchar *gstreamer_tagname;
2117 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2118 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2119 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2120 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2121 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2122 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2123 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2124 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2125 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2126 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2127 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2128 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2129 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2130 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2131 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2133 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2135 guint64 simpletag_master;
2137 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2138 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2139 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2141 if (strcmp (tagname_gst, tag) == 0) {
2142 GValue src = { 0, };
2145 if (!gst_tag_list_copy_value (&src, list, tag))
2147 if ((dest = gst_value_serialize (&src))) {
2149 simpletag_master = gst_ebml_write_master_start (ebml,
2150 GST_MATROSKA_ID_SIMPLETAG);
2151 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2152 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2153 gst_ebml_write_master_finish (ebml, simpletag_master);
2156 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2158 g_value_unset (&src);
2166 * gst_matroska_mux_finish:
2167 * @mux: #GstMatroskaMux
2169 * Finish a new matroska file (write index etc...)
2172 gst_matroska_mux_finish (GstMatroskaMux * mux)
2174 GstEbmlWrite *ebml = mux->ebml_write;
2176 guint64 duration = 0;
2178 const GstTagList *tags;
2180 /* finish last cluster */
2182 gst_ebml_write_master_finish (ebml, mux->cluster);
2186 if (mux->index != NULL) {
2188 guint64 master, pointentry_master, trackpos_master;
2190 mux->cues_pos = ebml->pos;
2191 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2192 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2194 for (n = 0; n < mux->num_indexes; n++) {
2195 GstMatroskaIndex *idx = &mux->index[n];
2197 pointentry_master = gst_ebml_write_master_start (ebml,
2198 GST_MATROSKA_ID_POINTENTRY);
2199 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2200 idx->time / mux->time_scale);
2201 trackpos_master = gst_ebml_write_master_start (ebml,
2202 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2203 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2204 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2205 idx->pos - mux->segment_master);
2206 gst_ebml_write_master_finish (ebml, trackpos_master);
2207 gst_ebml_write_master_finish (ebml, pointentry_master);
2210 gst_ebml_write_master_finish (ebml, master);
2211 gst_ebml_write_flush_cache (ebml);
2215 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2217 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2218 guint64 master_tags, master_tag;
2220 GST_DEBUG ("Writing tags");
2222 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2223 mux->tags_pos = ebml->pos;
2224 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2225 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2226 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2227 gst_ebml_write_master_finish (ebml, master_tag);
2228 gst_ebml_write_master_finish (ebml, master_tags);
2231 /* update seekhead. We know that:
2232 * - a seekhead contains 4 entries.
2233 * - order of entries is as above.
2234 * - a seekhead has a 4-byte header + 8-byte length
2235 * - each entry is 2-byte master, 2-byte ID pointer,
2236 * 2-byte length pointer, all 8/1-byte length, 4-
2237 * byte ID and 8-byte length pointer, where the
2238 * length pointer starts at 20.
2239 * - all entries are local to the segment (so pos - segment_master).
2240 * - so each entry is at 12 + 20 + num * 28. */
2241 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2242 mux->info_pos - mux->segment_master);
2243 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2244 mux->tracks_pos - mux->segment_master);
2245 if (mux->index != NULL) {
2246 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2247 mux->cues_pos - mux->segment_master);
2250 guint64 my_pos = ebml->pos;
2252 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2253 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2254 gst_ebml_write_seek (ebml, my_pos);
2257 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2258 mux->tags_pos - mux->segment_master);
2261 guint64 my_pos = ebml->pos;
2263 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2264 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2265 gst_ebml_write_seek (ebml, my_pos);
2268 /* update duration */
2269 /* first get the overall duration */
2270 /* a released track may have left a duration in here */
2271 duration = mux->duration;
2272 for (collected = mux->collect->data; collected;
2273 collected = g_slist_next (collected)) {
2274 GstMatroskaPad *collect_pad;
2275 GstClockTime min_duration; /* observed minimum duration */
2277 collect_pad = (GstMatroskaPad *) collected->data;
2279 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2280 " end ts %" GST_TIME_FORMAT, collect_pad,
2281 GST_TIME_ARGS (collect_pad->start_ts),
2282 GST_TIME_ARGS (collect_pad->end_ts));
2284 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2285 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2287 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2288 if (collect_pad->duration < min_duration)
2289 collect_pad->duration = min_duration;
2290 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2291 GST_TIME_ARGS (collect_pad->duration));
2294 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2295 duration < collect_pad->duration)
2296 duration = collect_pad->duration;
2298 if (duration != 0) {
2299 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2300 GST_TIME_ARGS (duration));
2301 pos = mux->ebml_write->pos;
2302 gst_ebml_write_seek (ebml, mux->duration_pos);
2303 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2304 gst_guint64_to_gdouble (duration) /
2305 gst_guint64_to_gdouble (mux->time_scale));
2306 gst_ebml_write_seek (ebml, pos);
2309 guint64 my_pos = ebml->pos;
2311 gst_ebml_write_seek (ebml, mux->duration_pos);
2312 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2313 gst_ebml_write_seek (ebml, my_pos);
2316 /* finish segment - this also writes element length */
2317 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2322 * gst_matroska_mux_best_pad:
2323 * @mux: #GstMatroskaMux
2324 * @popped: True if at least one buffer was popped from #GstCollectPads
2326 * Find a pad with the oldest data
2327 * (data from this pad should be written first).
2329 * Returns: Selected pad.
2331 static GstMatroskaPad *
2332 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2335 GstMatroskaPad *best = NULL;
2338 for (collected = mux->collect->data; collected;
2339 collected = g_slist_next (collected)) {
2340 GstMatroskaPad *collect_pad;
2342 collect_pad = (GstMatroskaPad *) collected->data;
2343 /* fetch a new buffer if needed */
2344 if (collect_pad->buffer == NULL) {
2345 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2346 (GstCollectData *) collect_pad);
2348 if (collect_pad->buffer != NULL)
2352 /* if we have a buffer check if it is better then the current best one */
2353 if (collect_pad->buffer != NULL) {
2354 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2355 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2356 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2357 GST_BUFFER_TIMESTAMP (best->buffer))) {
2367 * gst_matroska_mux_buffer_header:
2368 * @track: Track context.
2369 * @relative_timestamp: relative timestamp of the buffer
2370 * @flags: Buffer flags.
2372 * Create a buffer containing buffer header.
2374 * Returns: New buffer.
2377 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2378 gint16 relative_timestamp, int flags)
2382 hdr = gst_buffer_new_and_alloc (4);
2383 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2384 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2385 /* time relative to clustertime */
2386 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2389 GST_BUFFER_DATA (hdr)[3] = flags;
2394 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2395 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2396 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2399 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2400 GstMatroskaPad * collect_pad, GstBuffer * buf)
2402 GstMatroskaTrackVideoContext *ctx =
2403 (GstMatroskaTrackVideoContext *) collect_pad->track;
2404 const guint8 *data = GST_BUFFER_DATA (buf);
2405 guint size = GST_BUFFER_SIZE (buf);
2407 guint32 next_parse_offset;
2408 GstBuffer *ret = NULL;
2409 gboolean is_muxing_unit = FALSE;
2411 if (GST_BUFFER_SIZE (buf) < 13) {
2412 gst_buffer_unref (buf);
2416 /* Check if this buffer contains a picture or end-of-sequence packet */
2417 while (size >= 13) {
2418 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2419 gst_buffer_unref (buf);
2423 parse_code = GST_READ_UINT8 (data + 4);
2424 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2425 if (ctx->dirac_unit) {
2426 gst_buffer_unref (ctx->dirac_unit);
2427 ctx->dirac_unit = NULL;
2429 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2430 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2431 is_muxing_unit = TRUE;
2435 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2437 if (G_UNLIKELY (next_parse_offset == 0))
2440 data += next_parse_offset;
2441 size -= next_parse_offset;
2444 if (ctx->dirac_unit)
2445 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2447 ctx->dirac_unit = gst_buffer_ref (buf);
2449 if (is_muxing_unit) {
2450 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2451 ctx->dirac_unit = NULL;
2452 gst_buffer_copy_metadata (ret, buf,
2453 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2454 GST_BUFFER_COPY_CAPS);
2455 gst_buffer_unref (buf);
2457 gst_buffer_unref (buf);
2465 * gst_matroska_mux_write_data:
2466 * @mux: #GstMatroskaMux
2467 * @collect_pad: #GstMatroskaPad with the data
2469 * Write collected data (called from gst_matroska_mux_collected).
2471 * Returns: Result of the gst_pad_push issued to write the data.
2473 static GstFlowReturn
2474 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2476 GstEbmlWrite *ebml = mux->ebml_write;
2477 GstBuffer *buf, *hdr;
2479 gboolean write_duration;
2480 gint16 relative_timestamp;
2481 gint64 relative_timestamp64;
2482 guint64 block_duration;
2483 gboolean is_video_keyframe = FALSE;
2486 buf = collect_pad->buffer;
2487 collect_pad->buffer = NULL;
2489 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2490 if (collect_pad->track->xiph_headers_to_skip > 0) {
2491 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2492 gst_buffer_unref (buf);
2493 --collect_pad->track->xiph_headers_to_skip;
2497 /* for dirac we have to queue up everything up to a picture unit */
2498 if (collect_pad->track->codec_id != NULL &&
2499 strcmp (collect_pad->track->codec_id,
2500 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2501 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2506 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2507 * this would wreak havoc with time stored in matroska file */
2508 /* TODO: maybe calculate a timestamp by using the previous timestamp
2509 * and default duration */
2510 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2511 GST_WARNING_OBJECT (collect_pad->collect.pad,
2512 "Invalid buffer timestamp; dropping buffer");
2513 gst_buffer_unref (buf);
2517 /* set the timestamp for outgoing buffers */
2518 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2520 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2521 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2522 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2523 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2524 is_video_keyframe = TRUE;
2528 /* start a new cluster every two seconds or at keyframe */
2529 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2530 || is_video_keyframe) {
2532 gst_ebml_write_master_finish (ebml, mux->cluster);
2533 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2534 mux->cluster_pos = ebml->pos;
2536 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2537 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2538 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2539 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2540 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2541 mux->prev_cluster_size);
2546 mux->cluster_pos = ebml->pos;
2547 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2548 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2549 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2550 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2553 /* update duration of this track */
2554 if (GST_BUFFER_DURATION_IS_VALID (buf))
2555 collect_pad->duration += GST_BUFFER_DURATION (buf);
2557 /* We currently write index entries for all video tracks or for the audio
2558 * track in a single-track audio file. This could be improved by keeping the
2559 * index only for the *first* video track. */
2561 /* TODO: index is useful for every track, should contain the number of
2562 * the block in the cluster which contains the timestamp, should also work
2563 * for files with multiple audio tracks.
2565 if (is_video_keyframe ||
2566 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2567 (mux->num_streams == 1))) {
2570 if (mux->min_index_interval != 0) {
2571 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2572 if (mux->index[last_idx].track == collect_pad->track->num)
2577 if (last_idx < 0 || mux->min_index_interval == 0 ||
2578 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2579 >= mux->min_index_interval)) {
2580 GstMatroskaIndex *idx;
2582 if (mux->num_indexes % 32 == 0) {
2583 mux->index = g_renew (GstMatroskaIndex, mux->index,
2584 mux->num_indexes + 32);
2586 idx = &mux->index[mux->num_indexes++];
2588 idx->pos = mux->cluster_pos;
2589 idx->time = GST_BUFFER_TIMESTAMP (buf);
2590 idx->track = collect_pad->track->num;
2594 /* Check if the duration differs from the default duration. */
2595 write_duration = FALSE;
2596 block_duration = GST_BUFFER_DURATION (buf);
2597 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2598 if (block_duration != collect_pad->track->default_duration) {
2599 write_duration = TRUE;
2603 /* write the block, for doctype v2 use SimpleBlock if possible
2604 * one slice (*breath*).
2605 * FIXME: Need to do correct lacing! */
2606 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2607 if (relative_timestamp64 >= 0) {
2608 /* round the timestamp */
2609 relative_timestamp64 += mux->time_scale / 2;
2611 /* round the timestamp */
2612 relative_timestamp64 -= mux->time_scale / 2;
2614 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2615 if (mux->doctype_version > 1 && !write_duration) {
2617 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2620 gst_matroska_mux_create_buffer_header (collect_pad->track,
2621 relative_timestamp, flags);
2622 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2623 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2624 gst_ebml_write_buffer (ebml, hdr);
2625 gst_ebml_write_buffer (ebml, buf);
2627 return gst_ebml_last_write_result (ebml);
2629 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2631 gst_matroska_mux_create_buffer_header (collect_pad->track,
2632 relative_timestamp, 0);
2633 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2634 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2635 gst_ebml_write_buffer (ebml, hdr);
2636 gst_ebml_write_buffer (ebml, buf);
2637 if (write_duration) {
2638 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2639 block_duration / mux->time_scale);
2641 gst_ebml_write_master_finish (ebml, blockgroup);
2642 return gst_ebml_last_write_result (ebml);
2648 * gst_matroska_mux_collected:
2649 * @pads: #GstCollectPads
2650 * @uuser_data: #GstMatroskaMux
2652 * Collectpads callback.
2654 * Returns: #GstFlowReturn
2656 static GstFlowReturn
2657 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2659 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2660 GstMatroskaPad *best;
2664 GST_DEBUG_OBJECT (mux, "Collected pads");
2666 /* start with a header */
2667 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2668 if (mux->collect->data == NULL) {
2669 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2670 ("No input streams configured"));
2671 return GST_FLOW_ERROR;
2673 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2674 gst_matroska_mux_start (mux);
2675 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2679 /* which stream to write from? */
2680 best = gst_matroska_mux_best_pad (mux, &popped);
2682 /* if there is no best pad, we have reached EOS */
2684 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2685 gst_matroska_mux_finish (mux);
2686 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2687 ret = GST_FLOW_UNEXPECTED;
2690 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2691 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2692 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2693 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2695 /* make note of first and last encountered timestamps, so we can calculate
2696 * the actual duration later when we send an updated header on eos */
2697 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2698 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2699 GstClockTime end_ts = start_ts;
2701 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2702 end_ts += GST_BUFFER_DURATION (best->buffer);
2703 else if (best->track->default_duration)
2704 end_ts += best->track->default_duration;
2706 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2707 best->end_ts = end_ts;
2709 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2710 start_ts < best->start_ts))
2711 best->start_ts = start_ts;
2714 /* write one buffer */
2715 ret = gst_matroska_mux_write_data (mux, best);
2716 } while (ret == GST_FLOW_OK && !popped);
2723 * gst_matroska_mux_change_state:
2724 * @element: #GstMatroskaMux
2725 * @transition: State change transition.
2727 * Change the muxer state.
2729 * Returns: #GstStateChangeReturn
2731 static GstStateChangeReturn
2732 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2734 GstStateChangeReturn ret;
2735 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2737 switch (transition) {
2738 case GST_STATE_CHANGE_NULL_TO_READY:
2740 case GST_STATE_CHANGE_READY_TO_PAUSED:
2741 gst_collect_pads_start (mux->collect);
2743 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2745 case GST_STATE_CHANGE_PAUSED_TO_READY:
2746 gst_collect_pads_stop (mux->collect);
2752 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2754 switch (transition) {
2755 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2757 case GST_STATE_CHANGE_PAUSED_TO_READY:
2758 gst_matroska_mux_reset (GST_ELEMENT (mux));
2760 case GST_STATE_CHANGE_READY_TO_NULL:
2770 gst_matroska_mux_set_property (GObject * object,
2771 guint prop_id, const GValue * value, GParamSpec * pspec)
2773 GstMatroskaMux *mux;
2775 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2776 mux = GST_MATROSKA_MUX (object);
2779 case ARG_WRITING_APP:
2780 if (!g_value_get_string (value)) {
2781 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2784 g_free (mux->writing_app);
2785 mux->writing_app = g_value_dup_string (value);
2788 mux->doctype = g_value_get_enum (value);
2790 case ARG_DOCTYPE_VERSION:
2791 mux->doctype_version = g_value_get_int (value);
2793 case ARG_MIN_INDEX_INTERVAL:
2794 mux->min_index_interval = g_value_get_int64 (value);
2797 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2803 gst_matroska_mux_get_property (GObject * object,
2804 guint prop_id, GValue * value, GParamSpec * pspec)
2806 GstMatroskaMux *mux;
2808 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2809 mux = GST_MATROSKA_MUX (object);
2812 case ARG_WRITING_APP:
2813 g_value_set_string (value, mux->writing_app);
2816 g_value_set_enum (value, mux->doctype);
2818 case ARG_DOCTYPE_VERSION:
2819 g_value_set_int (value, mux->doctype_version);
2821 case ARG_MIN_INDEX_INTERVAL:
2822 g_value_set_int64 (value, mux->min_index_interval);
2825 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2831 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2833 return gst_element_register (plugin, "matroskamux",
2834 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);