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
67 #define DEFAULT_MATROSKA_VERSION 1
68 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
70 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
73 GST_STATIC_CAPS ("video/x-matroska")
76 #define COMMON_VIDEO_CAPS \
77 "width = (int) [ 16, 4096 ], " \
78 "height = (int) [ 16, 4096 ], " \
79 "framerate = (fraction) [ 0, MAX ]"
81 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
82 "width = (int) [ 16, 4096 ], " \
83 "height = (int) [ 16, 4096 ] "
86 * * require codec data, etc as needed
89 static GstStaticPadTemplate videosink_templ =
90 GST_STATIC_PAD_TEMPLATE ("video_%d",
93 GST_STATIC_CAPS ("video/mpeg, "
94 "mpegversion = (int) { 1, 2, 4 }, "
95 "systemstream = (boolean) false, "
96 COMMON_VIDEO_CAPS "; "
98 COMMON_VIDEO_CAPS "; "
100 COMMON_VIDEO_CAPS "; "
102 COMMON_VIDEO_CAPS "; "
104 COMMON_VIDEO_CAPS "; "
106 COMMON_VIDEO_CAPS "; "
108 COMMON_VIDEO_CAPS "; "
110 COMMON_VIDEO_CAPS "; "
112 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
115 COMMON_VIDEO_CAPS "; "
116 "video/x-pn-realvideo, "
117 "rmversion = (int) [1, 4], "
118 COMMON_VIDEO_CAPS "; "
120 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
121 COMMON_VIDEO_CAPS "; "
122 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
125 #define COMMON_AUDIO_CAPS \
126 "channels = (int) [ 1, MAX ], " \
127 "rate = (int) [ 1, MAX ]"
130 * * require codec data, etc as needed
132 static GstStaticPadTemplate audiosink_templ =
133 GST_STATIC_PAD_TEMPLATE ("audio_%d",
136 GST_STATIC_CAPS ("audio/mpeg, "
137 "mpegversion = (int) 1, "
138 "layer = (int) [ 1, 3 ], "
139 "stream-format = (string) { raw }, "
140 COMMON_AUDIO_CAPS "; "
142 "mpegversion = (int) { 2, 4 }, "
143 COMMON_AUDIO_CAPS "; "
145 COMMON_AUDIO_CAPS "; "
147 COMMON_AUDIO_CAPS "; "
149 COMMON_AUDIO_CAPS "; "
151 COMMON_AUDIO_CAPS "; "
155 "signed = (boolean) false, "
156 COMMON_AUDIO_CAPS ";"
160 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
161 "signed = (boolean) true, "
162 COMMON_AUDIO_CAPS ";"
166 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
167 "signed = (boolean) true, "
168 COMMON_AUDIO_CAPS ";"
172 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
173 "signed = (boolean) true, "
174 COMMON_AUDIO_CAPS ";"
175 "audio/x-raw-float, "
176 "width = (int) [ 32, 64 ], "
177 "endianness = (int) LITTLE_ENDIAN, "
178 COMMON_AUDIO_CAPS ";"
180 "width = (int) { 8, 16, 24 }, "
181 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
182 "audio/x-pn-realaudio, "
183 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
184 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
185 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
189 static GstStaticPadTemplate subtitlesink_templ =
190 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
193 GST_STATIC_CAPS_ANY);
195 static GArray *used_uids;
196 G_LOCK_DEFINE_STATIC (used_uids);
198 static void gst_matroska_mux_add_interfaces (GType type);
200 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
201 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
203 /* Matroska muxer destructor */
204 static void gst_matroska_mux_finalize (GObject * object);
206 /* Pads collected callback */
208 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
211 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
213 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
214 GstPadTemplate * templ, const gchar * name);
215 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
217 /* gst internal change state handler */
218 static GstStateChangeReturn
219 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
221 /* gobject bla bla */
222 static void gst_matroska_mux_set_property (GObject * object,
223 guint prop_id, const GValue * value, GParamSpec * pspec);
224 static void gst_matroska_mux_get_property (GObject * object,
225 guint prop_id, GValue * value, GParamSpec * pspec);
228 static void gst_matroska_mux_reset (GstElement * element);
231 static guint64 gst_matroska_mux_create_uid ();
233 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
234 GstMatroskaTrackContext * context);
235 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
236 GstMatroskaTrackContext * context);
237 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
238 GstMatroskaTrackContext * context);
239 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
240 GstMatroskaTrackContext * context);
241 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
242 GstMatroskaTrackContext * context);
245 gst_matroska_mux_add_interfaces (GType type)
247 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
249 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
253 gst_matroska_mux_base_init (gpointer g_class)
255 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
257 gst_element_class_add_pad_template (element_class,
258 gst_static_pad_template_get (&videosink_templ));
259 gst_element_class_add_pad_template (element_class,
260 gst_static_pad_template_get (&audiosink_templ));
261 gst_element_class_add_pad_template (element_class,
262 gst_static_pad_template_get (&subtitlesink_templ));
263 gst_element_class_add_pad_template (element_class,
264 gst_static_pad_template_get (&src_templ));
265 gst_element_class_set_details_simple (element_class, "Matroska muxer",
267 "Muxes video/audio/subtitle streams into a matroska stream",
268 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
270 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
275 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
277 GObjectClass *gobject_class;
278 GstElementClass *gstelement_class;
280 gobject_class = (GObjectClass *) klass;
281 gstelement_class = (GstElementClass *) klass;
283 gobject_class->finalize = gst_matroska_mux_finalize;
285 gobject_class->get_property = gst_matroska_mux_get_property;
286 gobject_class->set_property = gst_matroska_mux_set_property;
288 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
289 g_param_spec_string ("writing-app", "Writing application.",
290 "The name the application that creates the matroska file.",
291 NULL, G_PARAM_READWRITE));
292 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
293 g_param_spec_int ("version", "Matroska version",
294 "This parameter determines what matroska features can be used.",
295 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
297 gstelement_class->change_state =
298 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
299 gstelement_class->request_new_pad =
300 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
301 gstelement_class->release_pad =
302 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
307 * gst_matroska_mux_init:
308 * @mux: #GstMatroskaMux that should be initialized.
309 * @g_class: Class of the muxer.
311 * Matroska muxer constructor.
314 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
316 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
317 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
318 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
320 mux->collect = gst_collect_pads_new ();
321 gst_collect_pads_set_function (mux->collect,
322 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
325 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
327 /* property defaults */
328 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
329 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
331 /* initialize internal variables */
333 mux->num_streams = 0;
334 mux->num_a_streams = 0;
335 mux->num_t_streams = 0;
336 mux->num_v_streams = 0;
338 /* initialize remaining variables */
339 gst_matroska_mux_reset (GST_ELEMENT (mux));
344 * gst_matroska_mux_finalize:
345 * @object: #GstMatroskaMux that should be finalized.
347 * Finalize matroska muxer.
350 gst_matroska_mux_finalize (GObject * object)
352 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
354 gst_object_unref (mux->collect);
355 gst_object_unref (mux->ebml_write);
356 if (mux->writing_app)
357 g_free (mux->writing_app);
359 G_OBJECT_CLASS (parent_class)->finalize (object);
364 * gst_matroska_mux_create_uid:
366 * Generate new unused track UID.
368 * Returns: New track UID.
371 gst_matroska_mux_create_uid (void)
378 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
383 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
384 for (i = 0; i < used_uids->len; i++) {
385 if (g_array_index (used_uids, guint64, i) == uid) {
390 g_array_append_val (used_uids, uid);
393 G_UNLOCK (used_uids);
399 * gst_matroska_pad_reset:
400 * @collect_pad: the #GstMatroskaPad
402 * Reset and/or release resources of a matroska collect pad.
405 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
408 GstMatroskaTrackType type = 0;
410 /* free track information */
411 if (collect_pad->track != NULL) {
412 /* retrieve for optional later use */
413 name = collect_pad->track->name;
414 type = collect_pad->track->type;
415 /* extra for video */
416 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
417 GstMatroskaTrackVideoContext *ctx =
418 (GstMatroskaTrackVideoContext *) collect_pad->track;
420 if (ctx->dirac_unit) {
421 gst_buffer_unref (ctx->dirac_unit);
422 ctx->dirac_unit = NULL;
425 g_free (collect_pad->track->codec_id);
426 g_free (collect_pad->track->codec_name);
428 g_free (collect_pad->track->name);
429 g_free (collect_pad->track->language);
430 g_free (collect_pad->track->codec_priv);
431 g_free (collect_pad->track);
432 collect_pad->track = NULL;
435 /* free cached buffer */
436 if (collect_pad->buffer != NULL) {
437 gst_buffer_unref (collect_pad->buffer);
438 collect_pad->buffer = NULL;
441 if (!full && type != 0) {
442 GstMatroskaTrackContext *context;
444 /* create a fresh context */
446 case GST_MATROSKA_TRACK_TYPE_VIDEO:
447 context = (GstMatroskaTrackContext *)
448 g_new0 (GstMatroskaTrackVideoContext, 1);
450 case GST_MATROSKA_TRACK_TYPE_AUDIO:
451 context = (GstMatroskaTrackContext *)
452 g_new0 (GstMatroskaTrackAudioContext, 1);
454 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
455 context = (GstMatroskaTrackContext *)
456 g_new0 (GstMatroskaTrackSubtitleContext, 1);
459 g_assert_not_reached ();
463 context->type = type;
464 context->name = name;
465 /* TODO: check default values for the context */
466 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
467 collect_pad->track = context;
468 collect_pad->buffer = NULL;
469 collect_pad->duration = 0;
470 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
471 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
476 * gst_matroska_pad_free:
477 * @collect_pad: the #GstMatroskaPad
479 * Release resources of a matroska collect pad.
482 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
484 gst_matroska_pad_reset (collect_pad, TRUE);
489 * gst_matroska_mux_reset:
490 * @element: #GstMatroskaMux that should be reseted.
492 * Reset matroska muxer back to initial state.
495 gst_matroska_mux_reset (GstElement * element)
497 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
500 /* reset EBML write */
501 gst_ebml_write_reset (mux->ebml_write);
504 mux->state = GST_MATROSKA_MUX_STATE_START;
506 /* clean up existing streams */
508 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
509 GstMatroskaPad *collect_pad;
511 collect_pad = (GstMatroskaPad *) walk->data;
513 /* reset collect pad to pristine state */
514 gst_matroska_pad_reset (collect_pad, FALSE);
518 mux->num_indexes = 0;
523 mux->time_scale = GST_MSECOND;
528 mux->cluster_time = 0;
529 mux->cluster_pos = 0;
532 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
536 * gst_matroska_mux_handle_src_event:
537 * @pad: Pad which received the event.
538 * @event: Received event.
540 * handle events - copied from oggmux without understanding
542 * Returns: #TRUE on success.
545 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
549 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
553 /* disable seeking for now */
559 return gst_pad_event_default (pad, event);
563 * gst_matroska_mux_handle_sink_event:
564 * @pad: Pad which received the event.
565 * @event: Received event.
567 * handle events - informational ones like tags
569 * Returns: #TRUE on success.
572 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
574 GstMatroskaTrackContext *context;
575 GstMatroskaPad *collect_pad;
580 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
582 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
583 switch (GST_EVENT_TYPE (event)) {
587 GST_DEBUG_OBJECT (mux, "received tag event");
588 gst_event_parse_tag (event, &list);
590 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
591 g_assert (collect_pad);
592 context = collect_pad->track;
595 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
596 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
597 const gchar *lang_code;
599 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
601 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
602 context->language = g_strdup (lang_code);
604 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
609 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
610 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
613 case GST_EVENT_NEWSEGMENT:
614 /* We don't support NEWSEGMENT events */
616 gst_event_unref (event);
622 /* now GstCollectPads can take care of the rest, e.g. EOS */
624 ret = mux->collect_event (pad, event);
625 gst_object_unref (mux);
632 * gst_matroska_mux_video_pad_setcaps:
633 * @pad: Pad which got the caps.
636 * Setcaps function for video sink pad.
638 * Returns: #TRUE on success.
641 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
643 GstMatroskaTrackContext *context = NULL;
644 GstMatroskaTrackVideoContext *videocontext;
646 GstMatroskaPad *collect_pad;
647 GstStructure *structure;
648 const gchar *mimetype;
649 const GValue *value = NULL;
650 const GstBuffer *codec_buf = NULL;
651 gint width, height, pixel_width, pixel_height;
654 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
657 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
658 g_assert (collect_pad);
659 context = collect_pad->track;
661 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
662 videocontext = (GstMatroskaTrackVideoContext *) context;
664 /* gst -> matroska ID'ing */
665 structure = gst_caps_get_structure (caps, 0);
667 mimetype = gst_structure_get_name (structure);
669 if (!strcmp (mimetype, "video/x-theora")) {
670 /* we'll extract the details later from the theora identification header */
674 /* get general properties */
675 gst_structure_get_int (structure, "width", &width);
676 gst_structure_get_int (structure, "height", &height);
677 videocontext->pixel_width = width;
678 videocontext->pixel_height = height;
679 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
681 context->default_duration =
682 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
683 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
684 GST_TIME_ARGS (context->default_duration));
686 context->default_duration = 0;
688 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
689 &pixel_width, &pixel_height)) {
690 if (pixel_width > pixel_height) {
691 videocontext->display_width = width * pixel_width / pixel_height;
692 videocontext->display_height = height;
693 } else if (pixel_width < pixel_height) {
694 videocontext->display_width = width;
695 videocontext->display_height = height * pixel_height / pixel_width;
697 videocontext->display_width = 0;
698 videocontext->display_height = 0;
701 videocontext->display_width = 0;
702 videocontext->display_height = 0;
707 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
708 videocontext->fourcc = 0;
710 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
711 * data and other settings
715 /* extract codec_data, may turn out needed */
716 value = gst_structure_get_value (structure, "codec_data");
718 codec_buf = gst_value_get_buffer (value);
721 if (!strcmp (mimetype, "video/x-raw-yuv")) {
722 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
723 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
726 } else if (!strcmp (mimetype, "image/jpeg")) {
727 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
730 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
731 ||!strcmp (mimetype, "video/x-huffyuv")
732 || !strcmp (mimetype, "video/x-divx")
733 || !strcmp (mimetype, "video/x-dv")
734 || !strcmp (mimetype, "video/x-h263")
735 || !strcmp (mimetype, "video/x-msmpeg")
736 || !strcmp (mimetype, "video/x-wmv")) {
737 BITMAPINFOHEADER *bih;
738 gint size = sizeof (BITMAPINFOHEADER);
741 if (!strcmp (mimetype, "video/x-xvid"))
742 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
743 else if (!strcmp (mimetype, "video/x-huffyuv"))
744 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
745 else if (!strcmp (mimetype, "video/x-dv"))
746 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
747 else if (!strcmp (mimetype, "video/x-h263"))
748 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
749 else if (!strcmp (mimetype, "video/x-divx")) {
752 gst_structure_get_int (structure, "divxversion", &divxversion);
753 switch (divxversion) {
755 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
758 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
761 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
764 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
767 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
768 switch (msmpegversion) {
770 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
773 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
779 } else if (!strcmp (mimetype, "video/x-wmv")) {
782 if (gst_structure_get_fourcc (structure, "format", &format)) {
784 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
785 if (wmvversion == 2) {
786 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
787 } else if (wmvversion == 1) {
788 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
789 } else if (wmvversion == 3) {
790 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
798 bih = g_new0 (BITMAPINFOHEADER, 1);
799 GST_WRITE_UINT32_LE (&bih->bi_size, size);
800 GST_WRITE_UINT32_LE (&bih->bi_width, videocontext->pixel_width);
801 GST_WRITE_UINT32_LE (&bih->bi_height, videocontext->pixel_height);
802 GST_WRITE_UINT32_LE (&bih->bi_compression, fourcc);
803 GST_WRITE_UINT16_LE (&bih->bi_planes, (guint16) 1);
804 GST_WRITE_UINT16_LE (&bih->bi_bit_count, (guint16) 24);
805 GST_WRITE_UINT32_LE (&bih->bi_size_image, videocontext->pixel_width *
806 videocontext->pixel_height * 3);
808 /* process codec private/initialization data, if any */
810 size += GST_BUFFER_SIZE (codec_buf);
811 bih = g_realloc (bih, size);
812 GST_WRITE_UINT32_LE (&bih->bi_size, size);
813 memcpy ((guint8 *) bih + sizeof (BITMAPINFOHEADER),
814 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
817 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
818 context->codec_priv = (gpointer) bih;
819 context->codec_priv_size = size;
822 } else if (!strcmp (mimetype, "video/x-h264")) {
823 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
825 if (context->codec_priv != NULL) {
826 g_free (context->codec_priv);
827 context->codec_priv = NULL;
828 context->codec_priv_size = 0;
831 /* Create avcC header */
832 if (codec_buf != NULL) {
833 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
834 context->codec_priv = g_malloc0 (context->codec_priv_size);
835 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
836 context->codec_priv_size);
840 } else if (!strcmp (mimetype, "video/x-theora")) {
841 const GValue *streamheader;
843 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
845 if (context->codec_priv != NULL) {
846 g_free (context->codec_priv);
847 context->codec_priv = NULL;
848 context->codec_priv_size = 0;
851 streamheader = gst_structure_get_value (structure, "streamheader");
852 if (!theora_streamheader_to_codecdata (streamheader, context)) {
853 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
854 ("theora stream headers missing or malformed"));
858 } else if (!strcmp (mimetype, "video/x-dirac")) {
859 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
862 } else if (!strcmp (mimetype, "video/mpeg")) {
865 gst_structure_get_int (structure, "mpegversion", &mpegversion);
866 switch (mpegversion) {
868 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
871 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
874 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
880 /* global headers may be in codec data */
881 if (codec_buf != NULL) {
882 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
883 context->codec_priv = g_malloc0 (context->codec_priv_size);
884 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
885 context->codec_priv_size);
889 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
891 /* can only make it here if preceding case verified it was version 3 */
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
895 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
897 const GValue *mdpr_data;
899 gst_structure_get_int (structure, "rmversion", &rmversion);
902 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
905 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
911 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
917 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
918 if (mdpr_data != NULL) {
919 guint8 *priv_data = NULL;
920 guint priv_data_size = 0;
922 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
924 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
925 priv_data = g_malloc0 (priv_data_size);
927 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
929 context->codec_priv = priv_data;
930 context->codec_priv_size = priv_data_size;
939 /* N > 0 to expect a particular number of headers, negative if the
940 number of headers is variable */
942 xiphN_streamheader_to_codecdata (const GValue * streamheader,
943 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
945 GstBuffer **buf = NULL;
948 guint bufi, i, offset, priv_data_size;
950 if (streamheader == NULL)
951 goto no_stream_headers;
953 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
956 bufarr = g_value_peek_pointer (streamheader);
957 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
959 if (N > 0 && bufarr->len != N)
962 context->xiph_headers_to_skip = bufarr->len;
964 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
965 for (i = 0; i < bufarr->len; i++) {
966 GValue *bufval = &g_array_index (bufarr, GValue, i);
968 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
970 goto wrong_content_type;
973 buf[i] = g_value_peek_pointer (bufval);
977 if (bufarr->len > 0) {
978 for (i = 0; i < bufarr->len - 1; i++) {
979 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
983 for (i = 0; i < bufarr->len; ++i) {
984 priv_data_size += GST_BUFFER_SIZE (buf[i]);
987 priv_data = g_malloc0 (priv_data_size);
989 priv_data[0] = bufarr->len - 1;
992 if (bufarr->len > 0) {
993 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
994 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
995 priv_data[offset++] = 0xff;
997 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1001 for (i = 0; i < bufarr->len; ++i) {
1002 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1003 GST_BUFFER_SIZE (buf[i]));
1004 offset += GST_BUFFER_SIZE (buf[i]);
1007 context->codec_priv = priv_data;
1008 context->codec_priv_size = priv_data_size;
1011 *p_buf0 = gst_buffer_ref (buf[0]);
1020 GST_WARNING ("required streamheaders missing in sink caps!");
1025 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1026 G_VALUE_TYPE_NAME (streamheader));
1031 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1036 GST_WARNING ("streamheaders array does not contain GstBuffers");
1041 /* FIXME: after release make all code use xiph3_streamheader_to_codecdata() */
1043 xiph3_streamheader_to_codecdata (const GValue * streamheader,
1044 GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
1049 guint i, offset, priv_data_size;
1051 if (streamheader == NULL)
1052 goto no_stream_headers;
1054 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1057 bufarr = g_value_peek_pointer (streamheader);
1058 if (bufarr->len != 3)
1061 context->xiph_headers_to_skip = bufarr->len;
1063 for (i = 0; i < 3; i++) {
1064 GValue *bufval = &g_array_index (bufarr, GValue, i);
1066 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER)
1067 goto wrong_content_type;
1069 buf[i] = g_value_peek_pointer (bufval);
1073 priv_data_size += GST_BUFFER_SIZE (buf[0]) / 0xff + 1;
1074 priv_data_size += GST_BUFFER_SIZE (buf[1]) / 0xff + 1;
1076 for (i = 0; i < 3; ++i) {
1077 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1080 priv_data = g_malloc0 (priv_data_size);
1085 for (i = 0; i < GST_BUFFER_SIZE (buf[0]) / 0xff; ++i) {
1086 priv_data[offset++] = 0xff;
1088 priv_data[offset++] = GST_BUFFER_SIZE (buf[0]) % 0xff;
1090 for (i = 0; i < GST_BUFFER_SIZE (buf[1]) / 0xff; ++i) {
1091 priv_data[offset++] = 0xff;
1093 priv_data[offset++] = GST_BUFFER_SIZE (buf[1]) % 0xff;
1095 for (i = 0; i < 3; ++i) {
1096 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1097 GST_BUFFER_SIZE (buf[i]));
1098 offset += GST_BUFFER_SIZE (buf[i]);
1101 context->codec_priv = priv_data;
1102 context->codec_priv_size = priv_data_size;
1105 *p_buf0 = gst_buffer_ref (buf[0]);
1112 GST_WARNING ("required streamheaders missing in sink caps!");
1117 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1118 G_VALUE_TYPE_NAME (streamheader));
1123 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1128 GST_WARNING ("streamheaders array does not contain GstBuffers");
1134 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1135 GstMatroskaTrackContext * context)
1137 GstBuffer *buf0 = NULL;
1139 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1140 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1143 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1144 GST_WARNING ("First vorbis header too small, ignoring");
1146 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1147 GstMatroskaTrackAudioContext *audiocontext;
1150 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1151 audiocontext = (GstMatroskaTrackAudioContext *) context;
1152 audiocontext->channels = GST_READ_UINT8 (hdr);
1153 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1158 gst_buffer_unref (buf0);
1164 theora_streamheader_to_codecdata (const GValue * streamheader,
1165 GstMatroskaTrackContext * context)
1167 GstBuffer *buf0 = NULL;
1169 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1170 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1173 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1174 GST_WARNING ("First theora header too small, ignoring");
1175 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1176 GST_WARNING ("First header not a theora identification header, ignoring");
1178 GstMatroskaTrackVideoContext *videocontext;
1179 guint fps_num, fps_denom, par_num, par_denom;
1182 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1184 videocontext = (GstMatroskaTrackVideoContext *) context;
1185 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1186 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1187 hdr += 3 + 3 + 1 + 1;
1188 fps_num = GST_READ_UINT32_BE (hdr);
1189 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1190 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1191 fps_denom, fps_num);
1193 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1194 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1195 if (par_num > 0 && par_num > 0) {
1196 if (par_num > par_denom) {
1197 videocontext->display_width =
1198 videocontext->pixel_width * par_num / par_denom;
1199 videocontext->display_height = videocontext->pixel_height;
1200 } else if (par_num < par_denom) {
1201 videocontext->display_width = videocontext->pixel_width;
1202 videocontext->display_height =
1203 videocontext->pixel_height * par_denom / par_num;
1205 videocontext->display_width = 0;
1206 videocontext->display_height = 0;
1209 videocontext->display_width = 0;
1210 videocontext->display_height = 0;
1216 gst_buffer_unref (buf0);
1222 kate_streamheader_to_codecdata (const GValue * streamheader,
1223 GstMatroskaTrackContext * context)
1225 GstBuffer *buf0 = NULL;
1227 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1230 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1231 GST_WARNING ("First kate header too small, ignoring");
1232 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1233 GST_WARNING ("First header not a kate identification header, ignoring");
1237 gst_buffer_unref (buf0);
1243 flac_streamheader_to_codecdata (const GValue * streamheader,
1244 GstMatroskaTrackContext * context)
1251 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1252 GST_WARNING ("No or invalid streamheader field in the caps");
1256 bufarr = g_value_peek_pointer (streamheader);
1257 if (bufarr->len < 2) {
1258 GST_WARNING ("Too few headers in streamheader field");
1262 context->xiph_headers_to_skip = bufarr->len + 1;
1264 bufval = &g_array_index (bufarr, GValue, 0);
1265 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1266 GST_WARNING ("streamheaders array does not contain GstBuffers");
1270 buffer = g_value_peek_pointer (bufval);
1272 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1273 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1274 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1275 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1276 GST_WARNING ("Invalid streamheader for FLAC");
1280 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1281 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1282 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1283 GST_BUFFER_SIZE (buffer) - 9);
1285 for (i = 1; i < bufarr->len; i++) {
1286 bufval = &g_array_index (bufarr, GValue, i);
1288 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1289 g_free (context->codec_priv);
1290 context->codec_priv = NULL;
1291 context->codec_priv_size = 0;
1292 GST_WARNING ("streamheaders array does not contain GstBuffers");
1296 buffer = g_value_peek_pointer (bufval);
1298 context->codec_priv =
1299 g_realloc (context->codec_priv,
1300 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1301 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1302 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1303 context->codec_priv_size =
1304 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1311 speex_streamheader_to_codecdata (const GValue * streamheader,
1312 GstMatroskaTrackContext * context)
1318 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1319 GST_WARNING ("No or invalid streamheader field in the caps");
1323 bufarr = g_value_peek_pointer (streamheader);
1324 if (bufarr->len != 2) {
1325 GST_WARNING ("Too few headers in streamheader field");
1329 context->xiph_headers_to_skip = bufarr->len + 1;
1331 bufval = &g_array_index (bufarr, GValue, 0);
1332 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1333 GST_WARNING ("streamheaders array does not contain GstBuffers");
1337 buffer = g_value_peek_pointer (bufval);
1339 if (GST_BUFFER_SIZE (buffer) < 80
1340 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1341 GST_WARNING ("Invalid streamheader for Speex");
1345 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1346 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1347 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1348 GST_BUFFER_SIZE (buffer));
1350 bufval = &g_array_index (bufarr, GValue, 1);
1352 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1353 g_free (context->codec_priv);
1354 context->codec_priv = NULL;
1355 context->codec_priv_size = 0;
1356 GST_WARNING ("streamheaders array does not contain GstBuffers");
1360 buffer = g_value_peek_pointer (bufval);
1362 context->codec_priv =
1363 g_realloc (context->codec_priv,
1364 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1365 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1366 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1367 context->codec_priv_size =
1368 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1374 aac_codec_data_to_codec_id (const GstBuffer * buf)
1379 /* default to MAIN */
1382 if (GST_BUFFER_SIZE (buf) >= 2) {
1383 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1401 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1410 * gst_matroska_mux_audio_pad_setcaps:
1411 * @pad: Pad which got the caps.
1414 * Setcaps function for audio sink pad.
1416 * Returns: #TRUE on success.
1419 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1421 GstMatroskaTrackContext *context = NULL;
1422 GstMatroskaTrackAudioContext *audiocontext;
1423 GstMatroskaMux *mux;
1424 GstMatroskaPad *collect_pad;
1425 const gchar *mimetype;
1426 gint samplerate = 0, channels = 0;
1427 GstStructure *structure;
1428 const GValue *codec_data = NULL;
1429 const GstBuffer *buf = NULL;
1430 const gchar *stream_format = NULL;
1432 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1435 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1436 g_assert (collect_pad);
1437 context = collect_pad->track;
1439 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1440 audiocontext = (GstMatroskaTrackAudioContext *) context;
1442 structure = gst_caps_get_structure (caps, 0);
1443 mimetype = gst_structure_get_name (structure);
1446 gst_structure_get_int (structure, "rate", &samplerate);
1447 gst_structure_get_int (structure, "channels", &channels);
1449 audiocontext->samplerate = samplerate;
1450 audiocontext->channels = channels;
1451 audiocontext->bitdepth = 0;
1452 context->default_duration = 0;
1454 codec_data = gst_structure_get_value (structure, "codec_data");
1456 buf = gst_value_get_buffer (codec_data);
1458 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1459 * data and other settings
1463 if (!strcmp (mimetype, "audio/mpeg")) {
1464 gint mpegversion = 0;
1466 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1467 switch (mpegversion) {
1473 gst_structure_get_int (structure, "layer", &layer);
1475 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1476 GST_WARNING_OBJECT (mux,
1477 "Unable to determine MPEG audio version, assuming 1");
1483 else if (layer == 2)
1485 else if (version == 2)
1490 context->default_duration =
1491 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1495 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1498 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1501 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1510 stream_format = gst_structure_get_string (structure, "stream-format");
1511 /* check this is raw aac */
1512 if (stream_format) {
1513 if (strcmp (stream_format, "raw") != 0) {
1514 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1518 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1523 if (mpegversion == 2)
1525 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1526 aac_codec_data_to_codec_id (buf));
1527 else if (mpegversion == 4)
1529 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1530 aac_codec_data_to_codec_id (buf));
1532 g_assert_not_reached ();
1534 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1543 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1545 gint endianness = G_LITTLE_ENDIAN;
1546 gboolean signedness = TRUE;
1548 if (!gst_structure_get_int (structure, "width", &width) ||
1549 !gst_structure_get_int (structure, "depth", &depth) ||
1550 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1551 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1556 !gst_structure_get_int (structure, "endianness", &endianness)) {
1557 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1561 if (width != depth) {
1562 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1566 /* FIXME: where is this spec'ed out? (tpm) */
1567 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1568 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1572 audiocontext->bitdepth = depth;
1573 if (endianness == G_BIG_ENDIAN)
1574 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1576 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1579 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1582 if (!gst_structure_get_int (structure, "width", &width)) {
1583 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1587 audiocontext->bitdepth = width;
1588 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1591 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1592 const GValue *streamheader;
1594 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1596 if (context->codec_priv != NULL) {
1597 g_free (context->codec_priv);
1598 context->codec_priv = NULL;
1599 context->codec_priv_size = 0;
1602 streamheader = gst_structure_get_value (structure, "streamheader");
1603 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1604 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1605 ("vorbis stream headers missing or malformed"));
1609 } else if (!strcmp (mimetype, "audio/x-flac")) {
1610 const GValue *streamheader;
1612 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1613 if (context->codec_priv != NULL) {
1614 g_free (context->codec_priv);
1615 context->codec_priv = NULL;
1616 context->codec_priv_size = 0;
1619 streamheader = gst_structure_get_value (structure, "streamheader");
1620 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1621 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1622 ("flac stream headers missing or malformed"));
1626 } else if (!strcmp (mimetype, "audio/x-speex")) {
1627 const GValue *streamheader;
1629 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1630 if (context->codec_priv != NULL) {
1631 g_free (context->codec_priv);
1632 context->codec_priv = NULL;
1633 context->codec_priv_size = 0;
1636 streamheader = gst_structure_get_value (structure, "streamheader");
1637 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1638 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1639 ("speex stream headers missing or malformed"));
1643 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1644 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1647 } else if (!strcmp (mimetype, "audio/x-tta")) {
1650 /* TTA frame duration */
1651 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1653 gst_structure_get_int (structure, "width", &width);
1654 audiocontext->bitdepth = width;
1655 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1658 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1660 const GValue *mdpr_data;
1662 gst_structure_get_int (structure, "raversion", &raversion);
1663 switch (raversion) {
1665 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1668 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1671 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1677 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1678 if (mdpr_data != NULL) {
1679 guint8 *priv_data = NULL;
1680 guint priv_data_size = 0;
1682 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1684 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1685 priv_data = g_malloc0 (priv_data_size);
1687 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1689 context->codec_priv = priv_data;
1690 context->codec_priv_size = priv_data_size;
1694 } else if (!strcmp (mimetype, "audio/x-wma")) {
1696 guint codec_priv_size;
1703 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1704 || !gst_structure_get_int (structure, "block_align", &block_align)
1705 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1706 || samplerate == 0 || channels == 0) {
1707 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1708 "channels/rate on WMA caps");
1712 switch (wmaversion) {
1714 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1717 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1720 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1723 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1727 if (gst_structure_get_int (structure, "depth", &depth))
1728 audiocontext->bitdepth = depth;
1730 codec_priv_size = WAVEFORMATEX_SIZE;
1732 codec_priv_size += GST_BUFFER_SIZE (buf);
1734 /* serialize waveformatex structure */
1735 codec_priv = g_malloc0 (codec_priv_size);
1736 GST_WRITE_UINT16_LE (codec_priv, format);
1737 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1738 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1739 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1740 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1741 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1743 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1745 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1747 /* process codec private/initialization data, if any */
1749 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1750 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1753 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1754 context->codec_priv = (gpointer) codec_priv;
1755 context->codec_priv_size = codec_priv_size;
1764 * gst_matroska_mux_subtitle_pad_setcaps:
1765 * @pad: Pad which got the caps.
1768 * Setcaps function for subtitle sink pad.
1770 * Returns: #TRUE on success.
1773 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1776 * Consider this as boilerplate code for now. There is
1777 * no single subtitle creation element in GStreamer,
1778 * neither do I know how subtitling works at all. */
1780 /* There is now (at least) one such alement (kateenc), and I'm going
1781 to handle it here and claim it works when it can be piped back
1782 through GStreamer and VLC */
1784 GstMatroskaTrackContext *context = NULL;
1785 GstMatroskaTrackSubtitleContext *scontext;
1786 GstMatroskaMux *mux;
1787 GstMatroskaPad *collect_pad;
1788 const gchar *mimetype;
1789 GstStructure *structure;
1791 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1794 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1795 g_assert (collect_pad);
1796 context = collect_pad->track;
1798 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1799 scontext = (GstMatroskaTrackSubtitleContext *) context;
1801 structure = gst_caps_get_structure (caps, 0);
1802 mimetype = gst_structure_get_name (structure);
1805 scontext->check_utf8 = 1;
1806 scontext->invalid_utf8 = 0;
1807 context->default_duration = 0;
1809 /* TODO: - other format than Kate */
1811 if (!strcmp (mimetype, "subtitle/x-kate")) {
1812 const GValue *streamheader;
1814 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1816 if (context->codec_priv != NULL) {
1817 g_free (context->codec_priv);
1818 context->codec_priv = NULL;
1819 context->codec_priv_size = 0;
1822 streamheader = gst_structure_get_value (structure, "streamheader");
1823 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1824 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1825 ("kate stream headers missing or malformed"));
1836 * gst_matroska_mux_request_new_pad:
1837 * @element: #GstMatroskaMux.
1838 * @templ: #GstPadTemplate.
1839 * @pad_name: New pad name.
1841 * Request pad function for sink templates.
1843 * Returns: New #GstPad.
1846 gst_matroska_mux_request_new_pad (GstElement * element,
1847 GstPadTemplate * templ, const gchar * pad_name)
1849 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1850 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1851 GstMatroskaPad *collect_pad;
1852 GstPad *newpad = NULL;
1854 GstPadSetCapsFunction setcapsfunc = NULL;
1855 GstMatroskaTrackContext *context = NULL;
1857 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1858 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1859 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1860 context = (GstMatroskaTrackContext *)
1861 g_new0 (GstMatroskaTrackAudioContext, 1);
1862 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1863 context->name = g_strdup ("Audio");
1864 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1865 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1866 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1867 context = (GstMatroskaTrackContext *)
1868 g_new0 (GstMatroskaTrackVideoContext, 1);
1869 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1870 context->name = g_strdup ("Video");
1871 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1872 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1873 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1874 context = (GstMatroskaTrackContext *)
1875 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1876 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1877 context->name = g_strdup ("Subtitle");
1879 GST_WARNING_OBJECT (mux, "This is not our template!");
1883 newpad = gst_pad_new_from_template (templ, name);
1885 collect_pad = (GstMatroskaPad *)
1886 gst_collect_pads_add_pad_full (mux->collect, newpad,
1887 sizeof (GstMatroskaPad),
1888 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1890 collect_pad->track = context;
1891 gst_matroska_pad_reset (collect_pad, FALSE);
1893 /* FIXME: hacked way to override/extend the event function of
1894 * GstCollectPads; because it sets its own event function giving the
1895 * element no access to events.
1896 * TODO GstCollectPads should really give its 'users' a clean chance to
1897 * properly handle events that are not meant for collectpads itself.
1898 * Perhaps a callback or so, though rejected (?) in #340060.
1899 * This would allow (clean) transcoding of info from demuxer/streams
1900 * to another muxer */
1901 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1902 gst_pad_set_event_function (newpad,
1903 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1905 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1906 gst_pad_set_active (newpad, TRUE);
1907 gst_element_add_pad (element, newpad);
1914 * gst_matroska_mux_release_pad:
1915 * @element: #GstMatroskaMux.
1916 * @pad: Pad to release.
1918 * Release a previously requested pad.
1921 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1923 GstMatroskaMux *mux;
1926 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1928 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1929 GstCollectData *cdata = (GstCollectData *) walk->data;
1930 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1932 if (cdata->pad == pad) {
1933 GstClockTime min_dur; /* observed minimum duration */
1935 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1936 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1937 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1938 if (collect_pad->duration < min_dur)
1939 collect_pad->duration = min_dur;
1942 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1943 mux->duration < collect_pad->duration)
1944 mux->duration = collect_pad->duration;
1950 gst_collect_pads_remove_pad (mux->collect, pad);
1951 if (gst_element_remove_pad (element, pad))
1957 * gst_matroska_mux_track_header:
1958 * @mux: #GstMatroskaMux
1959 * @context: Tack context.
1961 * Write a track header.
1964 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1965 GstMatroskaTrackContext * context)
1967 GstEbmlWrite *ebml = mux->ebml_write;
1970 /* TODO: check if everything necessary is written and check default values */
1972 /* track type goes before the type-specific stuff */
1973 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1974 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1976 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1977 gst_matroska_mux_create_uid ());
1978 if (context->default_duration) {
1979 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1980 context->default_duration);
1982 if (context->language) {
1983 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1987 /* type-specific stuff */
1988 switch (context->type) {
1989 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1990 GstMatroskaTrackVideoContext *videocontext =
1991 (GstMatroskaTrackVideoContext *) context;
1993 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1994 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1995 videocontext->pixel_width);
1996 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1997 videocontext->pixel_height);
1998 if (videocontext->display_width && videocontext->display_height) {
1999 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2000 videocontext->display_width);
2001 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2002 videocontext->display_height);
2004 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
2005 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2006 if (videocontext->fourcc) {
2007 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2009 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2010 (gpointer) & fcc_le, 4);
2012 gst_ebml_write_master_finish (ebml, master);
2017 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2018 GstMatroskaTrackAudioContext *audiocontext =
2019 (GstMatroskaTrackAudioContext *) context;
2021 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2022 if (audiocontext->samplerate != 8000)
2023 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2024 audiocontext->samplerate);
2025 if (audiocontext->channels != 1)
2026 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2027 audiocontext->channels);
2028 if (audiocontext->bitdepth) {
2029 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2030 audiocontext->bitdepth);
2032 gst_ebml_write_master_finish (ebml, master);
2038 /* doesn't need type-specific data */
2042 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2043 if (context->codec_priv)
2044 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2045 context->codec_priv, context->codec_priv_size);
2046 /* FIXME: until we have a nice way of getting the codecname
2047 * out of the caps, I'm not going to enable this. Too much
2048 * (useless, double, boring) work... */
2049 /* TODO: Use value from tags if any */
2050 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2051 context->codec_name); */
2052 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2057 * gst_matroska_mux_start:
2058 * @mux: #GstMatroskaMux
2060 * Start a new matroska file (write headers etc...)
2063 gst_matroska_mux_start (GstMatroskaMux * mux)
2065 GstEbmlWrite *ebml = mux->ebml_write;
2066 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2067 GST_MATROSKA_ID_TRACKS,
2068 GST_MATROSKA_ID_CUES,
2069 GST_MATROSKA_ID_TAGS,
2072 guint64 master, child;
2076 GstClockTime duration = 0;
2077 guint32 segment_uid[4];
2078 GTimeVal time = { 0, 0 };
2080 /* we start with a EBML header */
2081 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
2083 /* start a segment */
2085 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2086 mux->segment_master = ebml->pos;
2088 /* the rest of the header is cached */
2089 gst_ebml_write_set_cache (ebml, 0x1000);
2091 /* seekhead (table of contents) - we set the positions later */
2092 mux->seekhead_pos = ebml->pos;
2093 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2094 for (i = 0; seekhead_id[i] != 0; i++) {
2095 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2096 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2097 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2098 gst_ebml_write_master_finish (ebml, child);
2100 gst_ebml_write_master_finish (ebml, master);
2103 mux->info_pos = ebml->pos;
2104 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2105 for (i = 0; i < 4; i++) {
2106 segment_uid[i] = g_random_int ();
2108 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2109 (guint8 *) segment_uid, 16);
2110 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2111 mux->duration_pos = ebml->pos;
2113 for (collected = mux->collect->data; collected;
2114 collected = g_slist_next (collected)) {
2115 GstMatroskaPad *collect_pad;
2116 GstFormat format = GST_FORMAT_TIME;
2118 gint64 trackduration;
2120 collect_pad = (GstMatroskaPad *) collected->data;
2121 thepad = collect_pad->collect.pad;
2123 /* Query the total length of the track. */
2124 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2125 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2126 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2127 GST_TIME_ARGS (trackduration));
2128 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2129 duration = (GstClockTime) trackduration;
2133 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2134 gst_guint64_to_gdouble (duration) /
2135 gst_guint64_to_gdouble (mux->time_scale));
2137 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2138 "GStreamer plugin version " PACKAGE_VERSION);
2139 if (mux->writing_app && mux->writing_app[0]) {
2140 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2142 g_get_current_time (&time);
2143 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2144 gst_ebml_write_master_finish (ebml, master);
2147 mux->tracks_pos = ebml->pos;
2148 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2150 for (collected = mux->collect->data; collected;
2151 collected = g_slist_next (collected)) {
2152 GstMatroskaPad *collect_pad;
2155 collect_pad = (GstMatroskaPad *) collected->data;
2156 thepad = collect_pad->collect.pad;
2158 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2159 collect_pad->track->codec_id != 0) {
2160 collect_pad->track->num = tracknum++;
2161 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2162 gst_matroska_mux_track_header (mux, collect_pad->track);
2163 gst_ebml_write_master_finish (ebml, child);
2166 gst_ebml_write_master_finish (ebml, master);
2168 /* lastly, flush the cache */
2169 gst_ebml_write_flush_cache (ebml);
2173 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2176 /* TODO: more sensible tag mappings */
2179 gchar *matroska_tagname;
2180 gchar *gstreamer_tagname;
2184 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2185 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2186 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2187 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2188 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2189 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2190 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2191 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2192 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2193 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2194 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2195 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2196 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2197 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2198 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2200 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2202 guint64 simpletag_master;
2204 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2205 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2206 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2208 if (strcmp (tagname_gst, tag) == 0) {
2209 GValue src = { 0, };
2212 if (!gst_tag_list_copy_value (&src, list, tag))
2214 if ((dest = gst_value_serialize (&src))) {
2216 simpletag_master = gst_ebml_write_master_start (ebml,
2217 GST_MATROSKA_ID_SIMPLETAG);
2218 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2219 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2220 gst_ebml_write_master_finish (ebml, simpletag_master);
2223 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2225 g_value_unset (&src);
2233 * gst_matroska_mux_finish:
2234 * @mux: #GstMatroskaMux
2236 * Finish a new matroska file (write index etc...)
2239 gst_matroska_mux_finish (GstMatroskaMux * mux)
2241 GstEbmlWrite *ebml = mux->ebml_write;
2243 guint64 duration = 0;
2245 const GstTagList *tags;
2247 /* finish last cluster */
2249 gst_ebml_write_master_finish (ebml, mux->cluster);
2253 if (mux->index != NULL) {
2255 guint64 master, pointentry_master, trackpos_master;
2257 mux->cues_pos = ebml->pos;
2258 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2259 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2261 for (n = 0; n < mux->num_indexes; n++) {
2262 GstMatroskaIndex *idx = &mux->index[n];
2264 pointentry_master = gst_ebml_write_master_start (ebml,
2265 GST_MATROSKA_ID_POINTENTRY);
2266 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2267 idx->time / mux->time_scale);
2268 trackpos_master = gst_ebml_write_master_start (ebml,
2269 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2270 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2271 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2272 idx->pos - mux->segment_master);
2273 gst_ebml_write_master_finish (ebml, trackpos_master);
2274 gst_ebml_write_master_finish (ebml, pointentry_master);
2277 gst_ebml_write_master_finish (ebml, master);
2278 gst_ebml_write_flush_cache (ebml);
2282 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2284 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2285 guint64 master_tags, master_tag;
2287 GST_DEBUG ("Writing tags");
2289 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2290 mux->tags_pos = ebml->pos;
2291 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2292 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2293 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2294 gst_ebml_write_master_finish (ebml, master_tag);
2295 gst_ebml_write_master_finish (ebml, master_tags);
2298 /* update seekhead. We know that:
2299 * - a seekhead contains 4 entries.
2300 * - order of entries is as above.
2301 * - a seekhead has a 4-byte header + 8-byte length
2302 * - each entry is 2-byte master, 2-byte ID pointer,
2303 * 2-byte length pointer, all 8/1-byte length, 4-
2304 * byte ID and 8-byte length pointer, where the
2305 * length pointer starts at 20.
2306 * - all entries are local to the segment (so pos - segment_master).
2307 * - so each entry is at 12 + 20 + num * 28. */
2308 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2309 mux->info_pos - mux->segment_master);
2310 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2311 mux->tracks_pos - mux->segment_master);
2312 if (mux->index != NULL) {
2313 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2314 mux->cues_pos - mux->segment_master);
2317 guint64 my_pos = ebml->pos;
2319 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2320 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2321 gst_ebml_write_seek (ebml, my_pos);
2324 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2325 mux->tags_pos - mux->segment_master);
2328 guint64 my_pos = ebml->pos;
2330 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2331 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2332 gst_ebml_write_seek (ebml, my_pos);
2335 /* update duration */
2336 /* first get the overall duration */
2337 /* a released track may have left a duration in here */
2338 duration = mux->duration;
2339 for (collected = mux->collect->data; collected;
2340 collected = g_slist_next (collected)) {
2341 GstMatroskaPad *collect_pad;
2342 GstClockTime min_duration; /* observed minimum duration */
2344 collect_pad = (GstMatroskaPad *) collected->data;
2346 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2347 " end ts %" GST_TIME_FORMAT, collect_pad,
2348 GST_TIME_ARGS (collect_pad->start_ts),
2349 GST_TIME_ARGS (collect_pad->end_ts));
2351 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2352 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2354 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2355 if (collect_pad->duration < min_duration)
2356 collect_pad->duration = min_duration;
2357 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2358 GST_TIME_ARGS (collect_pad->duration));
2361 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2362 duration < collect_pad->duration)
2363 duration = collect_pad->duration;
2365 if (duration != 0) {
2366 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2367 GST_TIME_ARGS (duration));
2368 pos = mux->ebml_write->pos;
2369 gst_ebml_write_seek (ebml, mux->duration_pos);
2370 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2371 gst_guint64_to_gdouble (duration) /
2372 gst_guint64_to_gdouble (mux->time_scale));
2373 gst_ebml_write_seek (ebml, pos);
2376 guint64 my_pos = ebml->pos;
2378 gst_ebml_write_seek (ebml, mux->duration_pos);
2379 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2380 gst_ebml_write_seek (ebml, my_pos);
2383 /* finish segment - this also writes element length */
2384 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2389 * gst_matroska_mux_best_pad:
2390 * @mux: #GstMatroskaMux
2391 * @popped: True if at least one buffer was popped from #GstCollectPads
2393 * Find a pad with the oldest data
2394 * (data from this pad should be written first).
2396 * Returns: Selected pad.
2398 static GstMatroskaPad *
2399 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2402 GstMatroskaPad *best = NULL;
2405 for (collected = mux->collect->data; collected;
2406 collected = g_slist_next (collected)) {
2407 GstMatroskaPad *collect_pad;
2409 collect_pad = (GstMatroskaPad *) collected->data;
2410 /* fetch a new buffer if needed */
2411 if (collect_pad->buffer == NULL) {
2412 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2413 (GstCollectData *) collect_pad);
2415 if (collect_pad->buffer != NULL)
2419 /* if we have a buffer check if it is better then the current best one */
2420 if (collect_pad->buffer != NULL) {
2421 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2422 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2423 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2424 GST_BUFFER_TIMESTAMP (best->buffer))) {
2434 * gst_matroska_mux_buffer_header:
2435 * @track: Track context.
2436 * @relative_timestamp: relative timestamp of the buffer
2437 * @flags: Buffer flags.
2439 * Create a buffer containing buffer header.
2441 * Returns: New buffer.
2444 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2445 gint16 relative_timestamp, int flags)
2449 hdr = gst_buffer_new_and_alloc (4);
2450 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2451 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2452 /* time relative to clustertime */
2453 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2456 GST_BUFFER_DATA (hdr)[3] = flags;
2461 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2462 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2463 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2466 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2467 GstMatroskaPad * collect_pad, GstBuffer * buf)
2469 GstMatroskaTrackVideoContext *ctx =
2470 (GstMatroskaTrackVideoContext *) collect_pad->track;
2471 const guint8 *data = GST_BUFFER_DATA (buf);
2472 guint size = GST_BUFFER_SIZE (buf);
2474 guint32 next_parse_offset;
2475 GstBuffer *ret = NULL;
2476 gboolean is_muxing_unit = FALSE;
2478 if (GST_BUFFER_SIZE (buf) < 13) {
2479 gst_buffer_unref (buf);
2483 /* Check if this buffer contains a picture or end-of-sequence packet */
2484 while (size >= 13) {
2485 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2486 gst_buffer_unref (buf);
2490 parse_code = GST_READ_UINT8 (data + 4);
2491 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2492 if (ctx->dirac_unit) {
2493 gst_buffer_unref (ctx->dirac_unit);
2494 ctx->dirac_unit = NULL;
2496 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2497 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2498 is_muxing_unit = TRUE;
2502 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2504 if (G_UNLIKELY (next_parse_offset == 0))
2507 data += next_parse_offset;
2508 size -= next_parse_offset;
2511 if (ctx->dirac_unit)
2512 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2514 ctx->dirac_unit = gst_buffer_ref (buf);
2516 if (is_muxing_unit) {
2517 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2518 ctx->dirac_unit = NULL;
2519 gst_buffer_copy_metadata (ret, buf,
2520 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2521 GST_BUFFER_COPY_CAPS);
2522 gst_buffer_unref (buf);
2524 gst_buffer_unref (buf);
2532 * gst_matroska_mux_write_data:
2533 * @mux: #GstMatroskaMux
2534 * @collect_pad: #GstMatroskaPad with the data
2536 * Write collected data (called from gst_matroska_mux_collected).
2538 * Returns: Result of the gst_pad_push issued to write the data.
2540 static GstFlowReturn
2541 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2543 GstEbmlWrite *ebml = mux->ebml_write;
2544 GstBuffer *buf, *hdr;
2546 gboolean write_duration;
2547 gint16 relative_timestamp;
2548 gint64 relative_timestamp64;
2549 guint64 block_duration;
2550 gboolean is_video_keyframe = FALSE;
2553 buf = collect_pad->buffer;
2554 collect_pad->buffer = NULL;
2556 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2557 if (collect_pad->track->xiph_headers_to_skip > 0) {
2558 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2559 gst_buffer_unref (buf);
2560 --collect_pad->track->xiph_headers_to_skip;
2564 /* for dirac we have to queue up everything up to a picture unit */
2565 if (collect_pad->track->codec_id != NULL &&
2566 strcmp (collect_pad->track->codec_id,
2567 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2568 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2573 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2574 * this would wreak havoc with time stored in matroska file */
2575 /* TODO: maybe calculate a timestamp by using the previous timestamp
2576 * and default duration */
2577 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2578 GST_WARNING_OBJECT (collect_pad->collect.pad,
2579 "Invalid buffer timestamp; dropping buffer");
2580 gst_buffer_unref (buf);
2584 /* set the timestamp for outgoing buffers */
2585 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2587 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2588 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2589 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2590 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2591 is_video_keyframe = TRUE;
2595 /* start a new cluster every two seconds or at keyframe */
2596 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2597 || is_video_keyframe) {
2599 gst_ebml_write_master_finish (ebml, mux->cluster);
2600 mux->cluster_pos = ebml->pos;
2602 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2603 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2604 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2605 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2610 mux->cluster_pos = ebml->pos;
2611 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2612 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2613 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2614 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2617 /* update duration of this track */
2618 if (GST_BUFFER_DURATION_IS_VALID (buf))
2619 collect_pad->duration += GST_BUFFER_DURATION (buf);
2621 /* We currently write an index entry for each keyframe in a
2622 * video track or one entry for each cluster in an audio track
2623 * for audio only files. This can be largely improved, such as doing
2624 * one for each keyframe or each second (for all-keyframe
2625 * streams), only the *first* video track. But that'll come later... */
2627 /* TODO: index is useful for every track, should contain the number of
2628 * the block in the cluster which contains the timestamp
2630 if (is_video_keyframe) {
2631 GstMatroskaIndex *idx;
2633 if (mux->num_indexes % 32 == 0) {
2634 mux->index = g_renew (GstMatroskaIndex, mux->index,
2635 mux->num_indexes + 32);
2637 idx = &mux->index[mux->num_indexes++];
2639 idx->pos = mux->cluster_pos;
2640 idx->time = GST_BUFFER_TIMESTAMP (buf);
2641 idx->track = collect_pad->track->num;
2642 } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2643 (mux->num_streams == 1)) {
2644 GstMatroskaIndex *idx;
2646 if (mux->num_indexes % 32 == 0) {
2647 mux->index = g_renew (GstMatroskaIndex, mux->index,
2648 mux->num_indexes + 32);
2650 idx = &mux->index[mux->num_indexes++];
2652 idx->pos = mux->cluster_pos;
2653 idx->time = GST_BUFFER_TIMESTAMP (buf);
2654 idx->track = collect_pad->track->num;
2657 /* Check if the duration differs from the default duration. */
2658 write_duration = FALSE;
2659 block_duration = GST_BUFFER_DURATION (buf);
2660 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2661 if (block_duration != collect_pad->track->default_duration) {
2662 write_duration = TRUE;
2666 /* write the block, for matroska v2 use SimpleBlock if possible
2667 * one slice (*breath*).
2668 * FIXME: Need to do correct lacing! */
2669 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2670 if (relative_timestamp64 >= 0) {
2671 /* round the timestamp */
2672 relative_timestamp64 += mux->time_scale / 2;
2674 /* round the timestamp */
2675 relative_timestamp64 -= mux->time_scale / 2;
2677 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2678 if (mux->matroska_version > 1 && !write_duration) {
2680 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2683 gst_matroska_mux_create_buffer_header (collect_pad->track,
2684 relative_timestamp, flags);
2685 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2686 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2687 gst_ebml_write_buffer (ebml, hdr);
2688 gst_ebml_write_buffer (ebml, buf);
2690 return gst_ebml_last_write_result (ebml);
2692 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2694 gst_matroska_mux_create_buffer_header (collect_pad->track,
2695 relative_timestamp, 0);
2696 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2697 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2698 gst_ebml_write_buffer (ebml, hdr);
2699 gst_ebml_write_buffer (ebml, buf);
2700 if (write_duration) {
2701 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2702 block_duration / mux->time_scale);
2704 gst_ebml_write_master_finish (ebml, blockgroup);
2705 return gst_ebml_last_write_result (ebml);
2711 * gst_matroska_mux_collected:
2712 * @pads: #GstCollectPads
2713 * @uuser_data: #GstMatroskaMux
2715 * Collectpads callback.
2717 * Returns: #GstFlowReturn
2719 static GstFlowReturn
2720 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2722 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2723 GstMatroskaPad *best;
2727 GST_DEBUG_OBJECT (mux, "Collected pads");
2729 /* start with a header */
2730 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2731 if (mux->collect->data == NULL) {
2732 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2733 ("No input streams configured"));
2734 return GST_FLOW_ERROR;
2736 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2737 gst_matroska_mux_start (mux);
2738 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2742 /* which stream to write from? */
2743 best = gst_matroska_mux_best_pad (mux, &popped);
2745 /* if there is no best pad, we have reached EOS */
2747 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2748 gst_matroska_mux_finish (mux);
2749 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2750 ret = GST_FLOW_UNEXPECTED;
2753 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2754 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2755 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2756 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2758 /* make note of first and last encountered timestamps, so we can calculate
2759 * the actual duration later when we send an updated header on eos */
2760 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2761 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2762 GstClockTime end_ts = start_ts;
2764 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2765 end_ts += GST_BUFFER_DURATION (best->buffer);
2766 else if (best->track->default_duration)
2767 end_ts += best->track->default_duration;
2769 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2770 best->end_ts = end_ts;
2772 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2773 start_ts < best->start_ts))
2774 best->start_ts = start_ts;
2777 /* write one buffer */
2778 ret = gst_matroska_mux_write_data (mux, best);
2779 } while (ret == GST_FLOW_OK && !popped);
2786 * gst_matroska_mux_change_state:
2787 * @element: #GstMatroskaMux
2788 * @transition: State change transition.
2790 * Change the muxer state.
2792 * Returns: #GstStateChangeReturn
2794 static GstStateChangeReturn
2795 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2797 GstStateChangeReturn ret;
2798 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2800 switch (transition) {
2801 case GST_STATE_CHANGE_NULL_TO_READY:
2803 case GST_STATE_CHANGE_READY_TO_PAUSED:
2804 gst_collect_pads_start (mux->collect);
2806 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2808 case GST_STATE_CHANGE_PAUSED_TO_READY:
2809 gst_collect_pads_stop (mux->collect);
2815 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2817 switch (transition) {
2818 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2820 case GST_STATE_CHANGE_PAUSED_TO_READY:
2821 gst_matroska_mux_reset (GST_ELEMENT (mux));
2823 case GST_STATE_CHANGE_READY_TO_NULL:
2833 gst_matroska_mux_set_property (GObject * object,
2834 guint prop_id, const GValue * value, GParamSpec * pspec)
2836 GstMatroskaMux *mux;
2838 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2839 mux = GST_MATROSKA_MUX (object);
2842 case ARG_WRITING_APP:
2843 if (!g_value_get_string (value)) {
2844 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2847 g_free (mux->writing_app);
2848 mux->writing_app = g_value_dup_string (value);
2850 case ARG_MATROSKA_VERSION:
2851 mux->matroska_version = g_value_get_int (value);
2854 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2860 gst_matroska_mux_get_property (GObject * object,
2861 guint prop_id, GValue * value, GParamSpec * pspec)
2863 GstMatroskaMux *mux;
2865 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2866 mux = GST_MATROSKA_MUX (object);
2869 case ARG_WRITING_APP:
2870 g_value_set_string (value, mux->writing_app);
2872 case ARG_MATROSKA_VERSION:
2873 g_value_set_int (value, mux->matroska_version);
2876 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2882 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2884 return gst_element_register (plugin, "matroskamux",
2885 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);