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 /* spec says it is mandatory */
676 if (!gst_structure_get_int (structure, "width", &width) ||
677 !gst_structure_get_int (structure, "height", &height))
680 videocontext->pixel_width = width;
681 videocontext->pixel_height = height;
682 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
684 context->default_duration =
685 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
686 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
687 GST_TIME_ARGS (context->default_duration));
689 context->default_duration = 0;
691 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
692 &pixel_width, &pixel_height)) {
693 if (pixel_width > pixel_height) {
694 videocontext->display_width = width * pixel_width / pixel_height;
695 videocontext->display_height = height;
696 } else if (pixel_width < pixel_height) {
697 videocontext->display_width = width;
698 videocontext->display_height = height * pixel_height / pixel_width;
700 videocontext->display_width = 0;
701 videocontext->display_height = 0;
704 videocontext->display_width = 0;
705 videocontext->display_height = 0;
710 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
711 videocontext->fourcc = 0;
713 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
714 * data and other settings
718 /* extract codec_data, may turn out needed */
719 value = gst_structure_get_value (structure, "codec_data");
721 codec_buf = gst_value_get_buffer (value);
724 if (!strcmp (mimetype, "video/x-raw-yuv")) {
725 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
726 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
727 } else if (!strcmp (mimetype, "image/jpeg")) {
728 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
729 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
730 ||!strcmp (mimetype, "video/x-huffyuv")
731 || !strcmp (mimetype, "video/x-divx")
732 || !strcmp (mimetype, "video/x-dv")
733 || !strcmp (mimetype, "video/x-h263")
734 || !strcmp (mimetype, "video/x-msmpeg")
735 || !strcmp (mimetype, "video/x-wmv")) {
736 BITMAPINFOHEADER *bih;
737 gint size = sizeof (BITMAPINFOHEADER);
740 if (!strcmp (mimetype, "video/x-xvid"))
741 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
742 else if (!strcmp (mimetype, "video/x-huffyuv"))
743 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
744 else if (!strcmp (mimetype, "video/x-dv"))
745 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
746 else if (!strcmp (mimetype, "video/x-h263"))
747 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
748 else if (!strcmp (mimetype, "video/x-divx")) {
751 gst_structure_get_int (structure, "divxversion", &divxversion);
752 switch (divxversion) {
754 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
757 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
760 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
763 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
766 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
767 switch (msmpegversion) {
769 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
772 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
778 } else if (!strcmp (mimetype, "video/x-wmv")) {
781 if (gst_structure_get_fourcc (structure, "format", &format)) {
783 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
784 if (wmvversion == 2) {
785 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
786 } else if (wmvversion == 1) {
787 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
788 } else if (wmvversion == 3) {
789 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
797 bih = g_new0 (BITMAPINFOHEADER, 1);
798 GST_WRITE_UINT32_LE (&bih->bi_size, size);
799 GST_WRITE_UINT32_LE (&bih->bi_width, videocontext->pixel_width);
800 GST_WRITE_UINT32_LE (&bih->bi_height, videocontext->pixel_height);
801 GST_WRITE_UINT32_LE (&bih->bi_compression, fourcc);
802 GST_WRITE_UINT16_LE (&bih->bi_planes, (guint16) 1);
803 GST_WRITE_UINT16_LE (&bih->bi_bit_count, (guint16) 24);
804 GST_WRITE_UINT32_LE (&bih->bi_size_image, videocontext->pixel_width *
805 videocontext->pixel_height * 3);
807 /* process codec private/initialization data, if any */
809 size += GST_BUFFER_SIZE (codec_buf);
810 bih = g_realloc (bih, size);
811 GST_WRITE_UINT32_LE (&bih->bi_size, size);
812 memcpy ((guint8 *) bih + sizeof (BITMAPINFOHEADER),
813 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
816 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
817 context->codec_priv = (gpointer) bih;
818 context->codec_priv_size = size;
819 } else if (!strcmp (mimetype, "video/x-h264")) {
820 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
822 if (context->codec_priv != NULL) {
823 g_free (context->codec_priv);
824 context->codec_priv = NULL;
825 context->codec_priv_size = 0;
828 /* Create avcC header */
829 if (codec_buf != NULL) {
830 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
831 context->codec_priv = g_malloc0 (context->codec_priv_size);
832 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
833 context->codec_priv_size);
835 } else if (!strcmp (mimetype, "video/x-theora")) {
836 const GValue *streamheader;
838 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
840 if (context->codec_priv != NULL) {
841 g_free (context->codec_priv);
842 context->codec_priv = NULL;
843 context->codec_priv_size = 0;
846 streamheader = gst_structure_get_value (structure, "streamheader");
847 if (!theora_streamheader_to_codecdata (streamheader, context)) {
848 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
849 ("theora stream headers missing or malformed"));
852 } else if (!strcmp (mimetype, "video/x-dirac")) {
853 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
854 } else if (!strcmp (mimetype, "video/mpeg")) {
857 gst_structure_get_int (structure, "mpegversion", &mpegversion);
858 switch (mpegversion) {
860 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
863 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
866 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
872 /* global headers may be in codec data */
873 if (codec_buf != NULL) {
874 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
875 context->codec_priv = g_malloc0 (context->codec_priv_size);
876 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
877 context->codec_priv_size);
879 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
881 /* can only make it here if preceding case verified it was version 3 */
882 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
883 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
885 const GValue *mdpr_data;
887 gst_structure_get_int (structure, "rmversion", &rmversion);
890 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
893 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
896 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
899 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
905 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
906 if (mdpr_data != NULL) {
907 guint8 *priv_data = NULL;
908 guint priv_data_size = 0;
910 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
912 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
913 priv_data = g_malloc0 (priv_data_size);
915 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
917 context->codec_priv = priv_data;
918 context->codec_priv_size = priv_data_size;
927 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
928 GST_PAD_NAME (pad), caps);
933 /* N > 0 to expect a particular number of headers, negative if the
934 number of headers is variable */
936 xiphN_streamheader_to_codecdata (const GValue * streamheader,
937 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
939 GstBuffer **buf = NULL;
942 guint bufi, i, offset, priv_data_size;
944 if (streamheader == NULL)
945 goto no_stream_headers;
947 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
950 bufarr = g_value_peek_pointer (streamheader);
951 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
953 if (N > 0 && bufarr->len != N)
956 context->xiph_headers_to_skip = bufarr->len;
958 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
959 for (i = 0; i < bufarr->len; i++) {
960 GValue *bufval = &g_array_index (bufarr, GValue, i);
962 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
964 goto wrong_content_type;
967 buf[i] = g_value_peek_pointer (bufval);
971 if (bufarr->len > 0) {
972 for (i = 0; i < bufarr->len - 1; i++) {
973 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
977 for (i = 0; i < bufarr->len; ++i) {
978 priv_data_size += GST_BUFFER_SIZE (buf[i]);
981 priv_data = g_malloc0 (priv_data_size);
983 priv_data[0] = bufarr->len - 1;
986 if (bufarr->len > 0) {
987 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
988 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
989 priv_data[offset++] = 0xff;
991 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
995 for (i = 0; i < bufarr->len; ++i) {
996 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
997 GST_BUFFER_SIZE (buf[i]));
998 offset += GST_BUFFER_SIZE (buf[i]);
1001 context->codec_priv = priv_data;
1002 context->codec_priv_size = priv_data_size;
1005 *p_buf0 = gst_buffer_ref (buf[0]);
1014 GST_WARNING ("required streamheaders missing in sink caps!");
1019 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1020 G_VALUE_TYPE_NAME (streamheader));
1025 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1030 GST_WARNING ("streamheaders array does not contain GstBuffers");
1035 /* FIXME: after release make all code use xiph3_streamheader_to_codecdata() */
1037 xiph3_streamheader_to_codecdata (const GValue * streamheader,
1038 GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
1043 guint i, offset, priv_data_size;
1045 if (streamheader == NULL)
1046 goto no_stream_headers;
1048 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1051 bufarr = g_value_peek_pointer (streamheader);
1052 if (bufarr->len != 3)
1055 context->xiph_headers_to_skip = bufarr->len;
1057 for (i = 0; i < 3; i++) {
1058 GValue *bufval = &g_array_index (bufarr, GValue, i);
1060 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER)
1061 goto wrong_content_type;
1063 buf[i] = g_value_peek_pointer (bufval);
1067 priv_data_size += GST_BUFFER_SIZE (buf[0]) / 0xff + 1;
1068 priv_data_size += GST_BUFFER_SIZE (buf[1]) / 0xff + 1;
1070 for (i = 0; i < 3; ++i) {
1071 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1074 priv_data = g_malloc0 (priv_data_size);
1079 for (i = 0; i < GST_BUFFER_SIZE (buf[0]) / 0xff; ++i) {
1080 priv_data[offset++] = 0xff;
1082 priv_data[offset++] = GST_BUFFER_SIZE (buf[0]) % 0xff;
1084 for (i = 0; i < GST_BUFFER_SIZE (buf[1]) / 0xff; ++i) {
1085 priv_data[offset++] = 0xff;
1087 priv_data[offset++] = GST_BUFFER_SIZE (buf[1]) % 0xff;
1089 for (i = 0; i < 3; ++i) {
1090 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1091 GST_BUFFER_SIZE (buf[i]));
1092 offset += GST_BUFFER_SIZE (buf[i]);
1095 context->codec_priv = priv_data;
1096 context->codec_priv_size = priv_data_size;
1099 *p_buf0 = gst_buffer_ref (buf[0]);
1106 GST_WARNING ("required streamheaders missing in sink caps!");
1111 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1112 G_VALUE_TYPE_NAME (streamheader));
1117 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1122 GST_WARNING ("streamheaders array does not contain GstBuffers");
1128 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1129 GstMatroskaTrackContext * context)
1131 GstBuffer *buf0 = NULL;
1133 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1134 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1137 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1138 GST_WARNING ("First vorbis header too small, ignoring");
1140 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1141 GstMatroskaTrackAudioContext *audiocontext;
1144 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1145 audiocontext = (GstMatroskaTrackAudioContext *) context;
1146 audiocontext->channels = GST_READ_UINT8 (hdr);
1147 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1152 gst_buffer_unref (buf0);
1158 theora_streamheader_to_codecdata (const GValue * streamheader,
1159 GstMatroskaTrackContext * context)
1161 GstBuffer *buf0 = NULL;
1163 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1164 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1167 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1168 GST_WARNING ("First theora header too small, ignoring");
1169 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1170 GST_WARNING ("First header not a theora identification header, ignoring");
1172 GstMatroskaTrackVideoContext *videocontext;
1173 guint fps_num, fps_denom, par_num, par_denom;
1176 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1178 videocontext = (GstMatroskaTrackVideoContext *) context;
1179 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1180 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1181 hdr += 3 + 3 + 1 + 1;
1182 fps_num = GST_READ_UINT32_BE (hdr);
1183 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1184 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1185 fps_denom, fps_num);
1187 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1188 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1189 if (par_num > 0 && par_num > 0) {
1190 if (par_num > par_denom) {
1191 videocontext->display_width =
1192 videocontext->pixel_width * par_num / par_denom;
1193 videocontext->display_height = videocontext->pixel_height;
1194 } else if (par_num < par_denom) {
1195 videocontext->display_width = videocontext->pixel_width;
1196 videocontext->display_height =
1197 videocontext->pixel_height * par_denom / par_num;
1199 videocontext->display_width = 0;
1200 videocontext->display_height = 0;
1203 videocontext->display_width = 0;
1204 videocontext->display_height = 0;
1210 gst_buffer_unref (buf0);
1216 kate_streamheader_to_codecdata (const GValue * streamheader,
1217 GstMatroskaTrackContext * context)
1219 GstBuffer *buf0 = NULL;
1221 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1224 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1225 GST_WARNING ("First kate header too small, ignoring");
1226 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1227 GST_WARNING ("First header not a kate identification header, ignoring");
1231 gst_buffer_unref (buf0);
1237 flac_streamheader_to_codecdata (const GValue * streamheader,
1238 GstMatroskaTrackContext * context)
1245 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1246 GST_WARNING ("No or invalid streamheader field in the caps");
1250 bufarr = g_value_peek_pointer (streamheader);
1251 if (bufarr->len < 2) {
1252 GST_WARNING ("Too few headers in streamheader field");
1256 context->xiph_headers_to_skip = bufarr->len + 1;
1258 bufval = &g_array_index (bufarr, GValue, 0);
1259 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1260 GST_WARNING ("streamheaders array does not contain GstBuffers");
1264 buffer = g_value_peek_pointer (bufval);
1266 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1267 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1268 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1269 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1270 GST_WARNING ("Invalid streamheader for FLAC");
1274 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1275 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1276 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1277 GST_BUFFER_SIZE (buffer) - 9);
1279 for (i = 1; i < bufarr->len; i++) {
1280 bufval = &g_array_index (bufarr, GValue, i);
1282 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1283 g_free (context->codec_priv);
1284 context->codec_priv = NULL;
1285 context->codec_priv_size = 0;
1286 GST_WARNING ("streamheaders array does not contain GstBuffers");
1290 buffer = g_value_peek_pointer (bufval);
1292 context->codec_priv =
1293 g_realloc (context->codec_priv,
1294 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1295 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1296 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1297 context->codec_priv_size =
1298 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1305 speex_streamheader_to_codecdata (const GValue * streamheader,
1306 GstMatroskaTrackContext * context)
1312 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1313 GST_WARNING ("No or invalid streamheader field in the caps");
1317 bufarr = g_value_peek_pointer (streamheader);
1318 if (bufarr->len != 2) {
1319 GST_WARNING ("Too few headers in streamheader field");
1323 context->xiph_headers_to_skip = bufarr->len + 1;
1325 bufval = &g_array_index (bufarr, GValue, 0);
1326 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1327 GST_WARNING ("streamheaders array does not contain GstBuffers");
1331 buffer = g_value_peek_pointer (bufval);
1333 if (GST_BUFFER_SIZE (buffer) < 80
1334 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1335 GST_WARNING ("Invalid streamheader for Speex");
1339 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1340 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1341 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1342 GST_BUFFER_SIZE (buffer));
1344 bufval = &g_array_index (bufarr, GValue, 1);
1346 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1347 g_free (context->codec_priv);
1348 context->codec_priv = NULL;
1349 context->codec_priv_size = 0;
1350 GST_WARNING ("streamheaders array does not contain GstBuffers");
1354 buffer = g_value_peek_pointer (bufval);
1356 context->codec_priv =
1357 g_realloc (context->codec_priv,
1358 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1359 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1360 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1361 context->codec_priv_size =
1362 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1368 aac_codec_data_to_codec_id (const GstBuffer * buf)
1373 /* default to MAIN */
1376 if (GST_BUFFER_SIZE (buf) >= 2) {
1377 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1395 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1404 * gst_matroska_mux_audio_pad_setcaps:
1405 * @pad: Pad which got the caps.
1408 * Setcaps function for audio sink pad.
1410 * Returns: #TRUE on success.
1413 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1415 GstMatroskaTrackContext *context = NULL;
1416 GstMatroskaTrackAudioContext *audiocontext;
1417 GstMatroskaMux *mux;
1418 GstMatroskaPad *collect_pad;
1419 const gchar *mimetype;
1420 gint samplerate = 0, channels = 0;
1421 GstStructure *structure;
1422 const GValue *codec_data = NULL;
1423 const GstBuffer *buf = NULL;
1424 const gchar *stream_format = NULL;
1426 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1429 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1430 g_assert (collect_pad);
1431 context = collect_pad->track;
1433 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1434 audiocontext = (GstMatroskaTrackAudioContext *) context;
1436 structure = gst_caps_get_structure (caps, 0);
1437 mimetype = gst_structure_get_name (structure);
1440 gst_structure_get_int (structure, "rate", &samplerate);
1441 gst_structure_get_int (structure, "channels", &channels);
1443 audiocontext->samplerate = samplerate;
1444 audiocontext->channels = channels;
1445 audiocontext->bitdepth = 0;
1446 context->default_duration = 0;
1448 codec_data = gst_structure_get_value (structure, "codec_data");
1450 buf = gst_value_get_buffer (codec_data);
1452 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1453 * data and other settings
1457 if (!strcmp (mimetype, "audio/mpeg")) {
1458 gint mpegversion = 0;
1460 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1461 switch (mpegversion) {
1467 gst_structure_get_int (structure, "layer", &layer);
1469 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1470 GST_WARNING_OBJECT (mux,
1471 "Unable to determine MPEG audio version, assuming 1");
1477 else if (layer == 2)
1479 else if (version == 2)
1484 context->default_duration =
1485 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1489 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1492 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1495 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1504 stream_format = gst_structure_get_string (structure, "stream-format");
1505 /* check this is raw aac */
1506 if (stream_format) {
1507 if (strcmp (stream_format, "raw") != 0) {
1508 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1512 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1517 if (mpegversion == 2)
1519 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1520 aac_codec_data_to_codec_id (buf));
1521 else if (mpegversion == 4)
1523 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1524 aac_codec_data_to_codec_id (buf));
1526 g_assert_not_reached ();
1528 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1535 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1537 gint endianness = G_LITTLE_ENDIAN;
1538 gboolean signedness = TRUE;
1540 if (!gst_structure_get_int (structure, "width", &width) ||
1541 !gst_structure_get_int (structure, "depth", &depth) ||
1542 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1543 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1548 !gst_structure_get_int (structure, "endianness", &endianness)) {
1549 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1553 if (width != depth) {
1554 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1558 /* FIXME: where is this spec'ed out? (tpm) */
1559 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1560 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1564 audiocontext->bitdepth = depth;
1565 if (endianness == G_BIG_ENDIAN)
1566 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1568 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1570 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1573 if (!gst_structure_get_int (structure, "width", &width)) {
1574 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1578 audiocontext->bitdepth = width;
1579 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1581 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1582 const GValue *streamheader;
1584 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1586 if (context->codec_priv != NULL) {
1587 g_free (context->codec_priv);
1588 context->codec_priv = NULL;
1589 context->codec_priv_size = 0;
1592 streamheader = gst_structure_get_value (structure, "streamheader");
1593 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1594 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1595 ("vorbis stream headers missing or malformed"));
1598 } else if (!strcmp (mimetype, "audio/x-flac")) {
1599 const GValue *streamheader;
1601 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1602 if (context->codec_priv != NULL) {
1603 g_free (context->codec_priv);
1604 context->codec_priv = NULL;
1605 context->codec_priv_size = 0;
1608 streamheader = gst_structure_get_value (structure, "streamheader");
1609 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1610 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1611 ("flac stream headers missing or malformed"));
1614 } else if (!strcmp (mimetype, "audio/x-speex")) {
1615 const GValue *streamheader;
1617 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1618 if (context->codec_priv != NULL) {
1619 g_free (context->codec_priv);
1620 context->codec_priv = NULL;
1621 context->codec_priv_size = 0;
1624 streamheader = gst_structure_get_value (structure, "streamheader");
1625 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1626 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1627 ("speex stream headers missing or malformed"));
1630 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1631 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1632 } else if (!strcmp (mimetype, "audio/x-tta")) {
1635 /* TTA frame duration */
1636 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1638 gst_structure_get_int (structure, "width", &width);
1639 audiocontext->bitdepth = width;
1640 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1642 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1644 const GValue *mdpr_data;
1646 gst_structure_get_int (structure, "raversion", &raversion);
1647 switch (raversion) {
1649 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1652 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1655 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1661 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1662 if (mdpr_data != NULL) {
1663 guint8 *priv_data = NULL;
1664 guint priv_data_size = 0;
1666 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1668 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1669 priv_data = g_malloc0 (priv_data_size);
1671 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1673 context->codec_priv = priv_data;
1674 context->codec_priv_size = priv_data_size;
1677 } else if (!strcmp (mimetype, "audio/x-wma")) {
1679 guint codec_priv_size;
1686 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1687 || !gst_structure_get_int (structure, "block_align", &block_align)
1688 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1689 || samplerate == 0 || channels == 0) {
1690 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1691 "channels/rate on WMA caps");
1695 switch (wmaversion) {
1697 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1700 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1703 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1706 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1710 if (gst_structure_get_int (structure, "depth", &depth))
1711 audiocontext->bitdepth = depth;
1713 codec_priv_size = WAVEFORMATEX_SIZE;
1715 codec_priv_size += GST_BUFFER_SIZE (buf);
1717 /* serialize waveformatex structure */
1718 codec_priv = g_malloc0 (codec_priv_size);
1719 GST_WRITE_UINT16_LE (codec_priv, format);
1720 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1721 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1722 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1723 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1724 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1726 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1728 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1730 /* process codec private/initialization data, if any */
1732 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1733 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1736 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1737 context->codec_priv = (gpointer) codec_priv;
1738 context->codec_priv_size = codec_priv_size;
1746 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1747 GST_PAD_NAME (pad), caps);
1754 * gst_matroska_mux_subtitle_pad_setcaps:
1755 * @pad: Pad which got the caps.
1758 * Setcaps function for subtitle sink pad.
1760 * Returns: #TRUE on success.
1763 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1766 * Consider this as boilerplate code for now. There is
1767 * no single subtitle creation element in GStreamer,
1768 * neither do I know how subtitling works at all. */
1770 /* There is now (at least) one such alement (kateenc), and I'm going
1771 to handle it here and claim it works when it can be piped back
1772 through GStreamer and VLC */
1774 GstMatroskaTrackContext *context = NULL;
1775 GstMatroskaTrackSubtitleContext *scontext;
1776 GstMatroskaMux *mux;
1777 GstMatroskaPad *collect_pad;
1778 const gchar *mimetype;
1779 GstStructure *structure;
1781 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1784 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1785 g_assert (collect_pad);
1786 context = collect_pad->track;
1788 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1789 scontext = (GstMatroskaTrackSubtitleContext *) context;
1791 structure = gst_caps_get_structure (caps, 0);
1792 mimetype = gst_structure_get_name (structure);
1795 scontext->check_utf8 = 1;
1796 scontext->invalid_utf8 = 0;
1797 context->default_duration = 0;
1799 /* TODO: - other format than Kate */
1801 if (!strcmp (mimetype, "subtitle/x-kate")) {
1802 const GValue *streamheader;
1804 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1806 if (context->codec_priv != NULL) {
1807 g_free (context->codec_priv);
1808 context->codec_priv = NULL;
1809 context->codec_priv_size = 0;
1812 streamheader = gst_structure_get_value (structure, "streamheader");
1813 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1814 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1815 ("kate stream headers missing or malformed"));
1826 * gst_matroska_mux_request_new_pad:
1827 * @element: #GstMatroskaMux.
1828 * @templ: #GstPadTemplate.
1829 * @pad_name: New pad name.
1831 * Request pad function for sink templates.
1833 * Returns: New #GstPad.
1836 gst_matroska_mux_request_new_pad (GstElement * element,
1837 GstPadTemplate * templ, const gchar * pad_name)
1839 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1840 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1841 GstMatroskaPad *collect_pad;
1842 GstPad *newpad = NULL;
1844 GstPadSetCapsFunction setcapsfunc = NULL;
1845 GstMatroskaTrackContext *context = NULL;
1847 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1848 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1849 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1850 context = (GstMatroskaTrackContext *)
1851 g_new0 (GstMatroskaTrackAudioContext, 1);
1852 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1853 context->name = g_strdup ("Audio");
1854 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1855 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1856 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1857 context = (GstMatroskaTrackContext *)
1858 g_new0 (GstMatroskaTrackVideoContext, 1);
1859 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1860 context->name = g_strdup ("Video");
1861 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1862 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1863 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1864 context = (GstMatroskaTrackContext *)
1865 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1866 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1867 context->name = g_strdup ("Subtitle");
1869 GST_WARNING_OBJECT (mux, "This is not our template!");
1873 newpad = gst_pad_new_from_template (templ, name);
1875 collect_pad = (GstMatroskaPad *)
1876 gst_collect_pads_add_pad_full (mux->collect, newpad,
1877 sizeof (GstMatroskaPad),
1878 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1880 collect_pad->track = context;
1881 gst_matroska_pad_reset (collect_pad, FALSE);
1883 /* FIXME: hacked way to override/extend the event function of
1884 * GstCollectPads; because it sets its own event function giving the
1885 * element no access to events.
1886 * TODO GstCollectPads should really give its 'users' a clean chance to
1887 * properly handle events that are not meant for collectpads itself.
1888 * Perhaps a callback or so, though rejected (?) in #340060.
1889 * This would allow (clean) transcoding of info from demuxer/streams
1890 * to another muxer */
1891 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1892 gst_pad_set_event_function (newpad,
1893 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1895 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1896 gst_pad_set_active (newpad, TRUE);
1897 gst_element_add_pad (element, newpad);
1904 * gst_matroska_mux_release_pad:
1905 * @element: #GstMatroskaMux.
1906 * @pad: Pad to release.
1908 * Release a previously requested pad.
1911 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1913 GstMatroskaMux *mux;
1916 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1918 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1919 GstCollectData *cdata = (GstCollectData *) walk->data;
1920 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1922 if (cdata->pad == pad) {
1923 GstClockTime min_dur; /* observed minimum duration */
1925 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1926 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1927 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1928 if (collect_pad->duration < min_dur)
1929 collect_pad->duration = min_dur;
1932 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1933 mux->duration < collect_pad->duration)
1934 mux->duration = collect_pad->duration;
1940 gst_collect_pads_remove_pad (mux->collect, pad);
1941 if (gst_element_remove_pad (element, pad))
1947 * gst_matroska_mux_track_header:
1948 * @mux: #GstMatroskaMux
1949 * @context: Tack context.
1951 * Write a track header.
1954 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1955 GstMatroskaTrackContext * context)
1957 GstEbmlWrite *ebml = mux->ebml_write;
1960 /* TODO: check if everything necessary is written and check default values */
1962 /* track type goes before the type-specific stuff */
1963 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1964 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1966 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1967 gst_matroska_mux_create_uid ());
1968 if (context->default_duration) {
1969 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1970 context->default_duration);
1972 if (context->language) {
1973 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1977 /* type-specific stuff */
1978 switch (context->type) {
1979 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1980 GstMatroskaTrackVideoContext *videocontext =
1981 (GstMatroskaTrackVideoContext *) context;
1983 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1985 videocontext->pixel_width);
1986 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1987 videocontext->pixel_height);
1988 if (videocontext->display_width && videocontext->display_height) {
1989 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1990 videocontext->display_width);
1991 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1992 videocontext->display_height);
1994 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1995 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1996 if (videocontext->fourcc) {
1997 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1999 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2000 (gpointer) & fcc_le, 4);
2002 gst_ebml_write_master_finish (ebml, master);
2007 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2008 GstMatroskaTrackAudioContext *audiocontext =
2009 (GstMatroskaTrackAudioContext *) context;
2011 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2012 if (audiocontext->samplerate != 8000)
2013 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2014 audiocontext->samplerate);
2015 if (audiocontext->channels != 1)
2016 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2017 audiocontext->channels);
2018 if (audiocontext->bitdepth) {
2019 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2020 audiocontext->bitdepth);
2022 gst_ebml_write_master_finish (ebml, master);
2028 /* doesn't need type-specific data */
2032 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2033 if (context->codec_priv)
2034 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2035 context->codec_priv, context->codec_priv_size);
2036 /* FIXME: until we have a nice way of getting the codecname
2037 * out of the caps, I'm not going to enable this. Too much
2038 * (useless, double, boring) work... */
2039 /* TODO: Use value from tags if any */
2040 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2041 context->codec_name); */
2042 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2047 * gst_matroska_mux_start:
2048 * @mux: #GstMatroskaMux
2050 * Start a new matroska file (write headers etc...)
2053 gst_matroska_mux_start (GstMatroskaMux * mux)
2055 GstEbmlWrite *ebml = mux->ebml_write;
2056 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2057 GST_MATROSKA_ID_TRACKS,
2058 GST_MATROSKA_ID_CUES,
2059 GST_MATROSKA_ID_TAGS,
2062 guint64 master, child;
2066 GstClockTime duration = 0;
2067 guint32 segment_uid[4];
2068 GTimeVal time = { 0, 0 };
2070 /* we start with a EBML header */
2071 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
2073 /* start a segment */
2075 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2076 mux->segment_master = ebml->pos;
2078 /* the rest of the header is cached */
2079 gst_ebml_write_set_cache (ebml, 0x1000);
2081 /* seekhead (table of contents) - we set the positions later */
2082 mux->seekhead_pos = ebml->pos;
2083 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2084 for (i = 0; seekhead_id[i] != 0; i++) {
2085 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2086 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2087 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2088 gst_ebml_write_master_finish (ebml, child);
2090 gst_ebml_write_master_finish (ebml, master);
2093 mux->info_pos = ebml->pos;
2094 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2095 for (i = 0; i < 4; i++) {
2096 segment_uid[i] = g_random_int ();
2098 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2099 (guint8 *) segment_uid, 16);
2100 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2101 mux->duration_pos = ebml->pos;
2103 for (collected = mux->collect->data; collected;
2104 collected = g_slist_next (collected)) {
2105 GstMatroskaPad *collect_pad;
2106 GstFormat format = GST_FORMAT_TIME;
2108 gint64 trackduration;
2110 collect_pad = (GstMatroskaPad *) collected->data;
2111 thepad = collect_pad->collect.pad;
2113 /* Query the total length of the track. */
2114 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2115 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2116 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2117 GST_TIME_ARGS (trackduration));
2118 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2119 duration = (GstClockTime) trackduration;
2123 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2124 gst_guint64_to_gdouble (duration) /
2125 gst_guint64_to_gdouble (mux->time_scale));
2127 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2128 "GStreamer plugin version " PACKAGE_VERSION);
2129 if (mux->writing_app && mux->writing_app[0]) {
2130 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2132 g_get_current_time (&time);
2133 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2134 gst_ebml_write_master_finish (ebml, master);
2137 mux->tracks_pos = ebml->pos;
2138 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2140 for (collected = mux->collect->data; collected;
2141 collected = g_slist_next (collected)) {
2142 GstMatroskaPad *collect_pad;
2145 collect_pad = (GstMatroskaPad *) collected->data;
2146 thepad = collect_pad->collect.pad;
2148 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2149 collect_pad->track->codec_id != 0) {
2150 collect_pad->track->num = tracknum++;
2151 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2152 gst_matroska_mux_track_header (mux, collect_pad->track);
2153 gst_ebml_write_master_finish (ebml, child);
2156 gst_ebml_write_master_finish (ebml, master);
2158 /* lastly, flush the cache */
2159 gst_ebml_write_flush_cache (ebml);
2163 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2166 /* TODO: more sensible tag mappings */
2169 gchar *matroska_tagname;
2170 gchar *gstreamer_tagname;
2174 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2175 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2176 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2177 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2178 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2179 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2180 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2181 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2182 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2183 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2184 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2185 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2186 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2187 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2188 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2190 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2192 guint64 simpletag_master;
2194 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2195 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2196 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2198 if (strcmp (tagname_gst, tag) == 0) {
2199 GValue src = { 0, };
2202 if (!gst_tag_list_copy_value (&src, list, tag))
2204 if ((dest = gst_value_serialize (&src))) {
2206 simpletag_master = gst_ebml_write_master_start (ebml,
2207 GST_MATROSKA_ID_SIMPLETAG);
2208 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2209 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2210 gst_ebml_write_master_finish (ebml, simpletag_master);
2213 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2215 g_value_unset (&src);
2223 * gst_matroska_mux_finish:
2224 * @mux: #GstMatroskaMux
2226 * Finish a new matroska file (write index etc...)
2229 gst_matroska_mux_finish (GstMatroskaMux * mux)
2231 GstEbmlWrite *ebml = mux->ebml_write;
2233 guint64 duration = 0;
2235 const GstTagList *tags;
2237 /* finish last cluster */
2239 gst_ebml_write_master_finish (ebml, mux->cluster);
2243 if (mux->index != NULL) {
2245 guint64 master, pointentry_master, trackpos_master;
2247 mux->cues_pos = ebml->pos;
2248 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2249 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2251 for (n = 0; n < mux->num_indexes; n++) {
2252 GstMatroskaIndex *idx = &mux->index[n];
2254 pointentry_master = gst_ebml_write_master_start (ebml,
2255 GST_MATROSKA_ID_POINTENTRY);
2256 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2257 idx->time / mux->time_scale);
2258 trackpos_master = gst_ebml_write_master_start (ebml,
2259 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2260 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2261 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2262 idx->pos - mux->segment_master);
2263 gst_ebml_write_master_finish (ebml, trackpos_master);
2264 gst_ebml_write_master_finish (ebml, pointentry_master);
2267 gst_ebml_write_master_finish (ebml, master);
2268 gst_ebml_write_flush_cache (ebml);
2272 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2274 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2275 guint64 master_tags, master_tag;
2277 GST_DEBUG ("Writing tags");
2279 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2280 mux->tags_pos = ebml->pos;
2281 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2282 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2283 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2284 gst_ebml_write_master_finish (ebml, master_tag);
2285 gst_ebml_write_master_finish (ebml, master_tags);
2288 /* update seekhead. We know that:
2289 * - a seekhead contains 4 entries.
2290 * - order of entries is as above.
2291 * - a seekhead has a 4-byte header + 8-byte length
2292 * - each entry is 2-byte master, 2-byte ID pointer,
2293 * 2-byte length pointer, all 8/1-byte length, 4-
2294 * byte ID and 8-byte length pointer, where the
2295 * length pointer starts at 20.
2296 * - all entries are local to the segment (so pos - segment_master).
2297 * - so each entry is at 12 + 20 + num * 28. */
2298 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2299 mux->info_pos - mux->segment_master);
2300 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2301 mux->tracks_pos - mux->segment_master);
2302 if (mux->index != NULL) {
2303 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2304 mux->cues_pos - mux->segment_master);
2307 guint64 my_pos = ebml->pos;
2309 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2310 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2311 gst_ebml_write_seek (ebml, my_pos);
2314 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2315 mux->tags_pos - mux->segment_master);
2318 guint64 my_pos = ebml->pos;
2320 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2321 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2322 gst_ebml_write_seek (ebml, my_pos);
2325 /* update duration */
2326 /* first get the overall duration */
2327 /* a released track may have left a duration in here */
2328 duration = mux->duration;
2329 for (collected = mux->collect->data; collected;
2330 collected = g_slist_next (collected)) {
2331 GstMatroskaPad *collect_pad;
2332 GstClockTime min_duration; /* observed minimum duration */
2334 collect_pad = (GstMatroskaPad *) collected->data;
2336 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2337 " end ts %" GST_TIME_FORMAT, collect_pad,
2338 GST_TIME_ARGS (collect_pad->start_ts),
2339 GST_TIME_ARGS (collect_pad->end_ts));
2341 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2342 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2344 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2345 if (collect_pad->duration < min_duration)
2346 collect_pad->duration = min_duration;
2347 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2348 GST_TIME_ARGS (collect_pad->duration));
2351 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2352 duration < collect_pad->duration)
2353 duration = collect_pad->duration;
2355 if (duration != 0) {
2356 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2357 GST_TIME_ARGS (duration));
2358 pos = mux->ebml_write->pos;
2359 gst_ebml_write_seek (ebml, mux->duration_pos);
2360 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2361 gst_guint64_to_gdouble (duration) /
2362 gst_guint64_to_gdouble (mux->time_scale));
2363 gst_ebml_write_seek (ebml, pos);
2366 guint64 my_pos = ebml->pos;
2368 gst_ebml_write_seek (ebml, mux->duration_pos);
2369 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2370 gst_ebml_write_seek (ebml, my_pos);
2373 /* finish segment - this also writes element length */
2374 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2379 * gst_matroska_mux_best_pad:
2380 * @mux: #GstMatroskaMux
2381 * @popped: True if at least one buffer was popped from #GstCollectPads
2383 * Find a pad with the oldest data
2384 * (data from this pad should be written first).
2386 * Returns: Selected pad.
2388 static GstMatroskaPad *
2389 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2392 GstMatroskaPad *best = NULL;
2395 for (collected = mux->collect->data; collected;
2396 collected = g_slist_next (collected)) {
2397 GstMatroskaPad *collect_pad;
2399 collect_pad = (GstMatroskaPad *) collected->data;
2400 /* fetch a new buffer if needed */
2401 if (collect_pad->buffer == NULL) {
2402 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2403 (GstCollectData *) collect_pad);
2405 if (collect_pad->buffer != NULL)
2409 /* if we have a buffer check if it is better then the current best one */
2410 if (collect_pad->buffer != NULL) {
2411 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2412 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2413 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2414 GST_BUFFER_TIMESTAMP (best->buffer))) {
2424 * gst_matroska_mux_buffer_header:
2425 * @track: Track context.
2426 * @relative_timestamp: relative timestamp of the buffer
2427 * @flags: Buffer flags.
2429 * Create a buffer containing buffer header.
2431 * Returns: New buffer.
2434 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2435 gint16 relative_timestamp, int flags)
2439 hdr = gst_buffer_new_and_alloc (4);
2440 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2441 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2442 /* time relative to clustertime */
2443 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2446 GST_BUFFER_DATA (hdr)[3] = flags;
2451 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2452 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2453 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2456 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2457 GstMatroskaPad * collect_pad, GstBuffer * buf)
2459 GstMatroskaTrackVideoContext *ctx =
2460 (GstMatroskaTrackVideoContext *) collect_pad->track;
2461 const guint8 *data = GST_BUFFER_DATA (buf);
2462 guint size = GST_BUFFER_SIZE (buf);
2464 guint32 next_parse_offset;
2465 GstBuffer *ret = NULL;
2466 gboolean is_muxing_unit = FALSE;
2468 if (GST_BUFFER_SIZE (buf) < 13) {
2469 gst_buffer_unref (buf);
2473 /* Check if this buffer contains a picture or end-of-sequence packet */
2474 while (size >= 13) {
2475 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2476 gst_buffer_unref (buf);
2480 parse_code = GST_READ_UINT8 (data + 4);
2481 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2482 if (ctx->dirac_unit) {
2483 gst_buffer_unref (ctx->dirac_unit);
2484 ctx->dirac_unit = NULL;
2486 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2487 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2488 is_muxing_unit = TRUE;
2492 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2494 if (G_UNLIKELY (next_parse_offset == 0))
2497 data += next_parse_offset;
2498 size -= next_parse_offset;
2501 if (ctx->dirac_unit)
2502 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2504 ctx->dirac_unit = gst_buffer_ref (buf);
2506 if (is_muxing_unit) {
2507 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2508 ctx->dirac_unit = NULL;
2509 gst_buffer_copy_metadata (ret, buf,
2510 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2511 GST_BUFFER_COPY_CAPS);
2512 gst_buffer_unref (buf);
2514 gst_buffer_unref (buf);
2522 * gst_matroska_mux_write_data:
2523 * @mux: #GstMatroskaMux
2524 * @collect_pad: #GstMatroskaPad with the data
2526 * Write collected data (called from gst_matroska_mux_collected).
2528 * Returns: Result of the gst_pad_push issued to write the data.
2530 static GstFlowReturn
2531 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2533 GstEbmlWrite *ebml = mux->ebml_write;
2534 GstBuffer *buf, *hdr;
2536 gboolean write_duration;
2537 gint16 relative_timestamp;
2538 gint64 relative_timestamp64;
2539 guint64 block_duration;
2540 gboolean is_video_keyframe = FALSE;
2543 buf = collect_pad->buffer;
2544 collect_pad->buffer = NULL;
2546 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2547 if (collect_pad->track->xiph_headers_to_skip > 0) {
2548 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2549 gst_buffer_unref (buf);
2550 --collect_pad->track->xiph_headers_to_skip;
2554 /* for dirac we have to queue up everything up to a picture unit */
2555 if (collect_pad->track->codec_id != NULL &&
2556 strcmp (collect_pad->track->codec_id,
2557 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2558 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2563 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2564 * this would wreak havoc with time stored in matroska file */
2565 /* TODO: maybe calculate a timestamp by using the previous timestamp
2566 * and default duration */
2567 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2568 GST_WARNING_OBJECT (collect_pad->collect.pad,
2569 "Invalid buffer timestamp; dropping buffer");
2570 gst_buffer_unref (buf);
2574 /* set the timestamp for outgoing buffers */
2575 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2577 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2578 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2579 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2580 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2581 is_video_keyframe = TRUE;
2585 /* start a new cluster every two seconds or at keyframe */
2586 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2587 || is_video_keyframe) {
2589 gst_ebml_write_master_finish (ebml, mux->cluster);
2590 mux->cluster_pos = ebml->pos;
2592 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2593 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2594 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2595 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2600 mux->cluster_pos = ebml->pos;
2601 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2602 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2603 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2604 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2607 /* update duration of this track */
2608 if (GST_BUFFER_DURATION_IS_VALID (buf))
2609 collect_pad->duration += GST_BUFFER_DURATION (buf);
2611 /* We currently write an index entry for each keyframe in a
2612 * video track or one entry for each cluster in an audio track
2613 * for audio only files. This can be largely improved, such as doing
2614 * one for each keyframe or each second (for all-keyframe
2615 * streams), only the *first* video track. But that'll come later... */
2617 /* TODO: index is useful for every track, should contain the number of
2618 * the block in the cluster which contains the timestamp
2620 if (is_video_keyframe) {
2621 GstMatroskaIndex *idx;
2623 if (mux->num_indexes % 32 == 0) {
2624 mux->index = g_renew (GstMatroskaIndex, mux->index,
2625 mux->num_indexes + 32);
2627 idx = &mux->index[mux->num_indexes++];
2629 idx->pos = mux->cluster_pos;
2630 idx->time = GST_BUFFER_TIMESTAMP (buf);
2631 idx->track = collect_pad->track->num;
2632 } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2633 (mux->num_streams == 1)) {
2634 GstMatroskaIndex *idx;
2636 if (mux->num_indexes % 32 == 0) {
2637 mux->index = g_renew (GstMatroskaIndex, mux->index,
2638 mux->num_indexes + 32);
2640 idx = &mux->index[mux->num_indexes++];
2642 idx->pos = mux->cluster_pos;
2643 idx->time = GST_BUFFER_TIMESTAMP (buf);
2644 idx->track = collect_pad->track->num;
2647 /* Check if the duration differs from the default duration. */
2648 write_duration = FALSE;
2649 block_duration = GST_BUFFER_DURATION (buf);
2650 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2651 if (block_duration != collect_pad->track->default_duration) {
2652 write_duration = TRUE;
2656 /* write the block, for matroska v2 use SimpleBlock if possible
2657 * one slice (*breath*).
2658 * FIXME: Need to do correct lacing! */
2659 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2660 if (relative_timestamp64 >= 0) {
2661 /* round the timestamp */
2662 relative_timestamp64 += mux->time_scale / 2;
2664 /* round the timestamp */
2665 relative_timestamp64 -= mux->time_scale / 2;
2667 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2668 if (mux->matroska_version > 1 && !write_duration) {
2670 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2673 gst_matroska_mux_create_buffer_header (collect_pad->track,
2674 relative_timestamp, flags);
2675 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2676 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2677 gst_ebml_write_buffer (ebml, hdr);
2678 gst_ebml_write_buffer (ebml, buf);
2680 return gst_ebml_last_write_result (ebml);
2682 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2684 gst_matroska_mux_create_buffer_header (collect_pad->track,
2685 relative_timestamp, 0);
2686 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2687 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2688 gst_ebml_write_buffer (ebml, hdr);
2689 gst_ebml_write_buffer (ebml, buf);
2690 if (write_duration) {
2691 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2692 block_duration / mux->time_scale);
2694 gst_ebml_write_master_finish (ebml, blockgroup);
2695 return gst_ebml_last_write_result (ebml);
2701 * gst_matroska_mux_collected:
2702 * @pads: #GstCollectPads
2703 * @uuser_data: #GstMatroskaMux
2705 * Collectpads callback.
2707 * Returns: #GstFlowReturn
2709 static GstFlowReturn
2710 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2712 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2713 GstMatroskaPad *best;
2717 GST_DEBUG_OBJECT (mux, "Collected pads");
2719 /* start with a header */
2720 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2721 if (mux->collect->data == NULL) {
2722 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2723 ("No input streams configured"));
2724 return GST_FLOW_ERROR;
2726 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2727 gst_matroska_mux_start (mux);
2728 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2732 /* which stream to write from? */
2733 best = gst_matroska_mux_best_pad (mux, &popped);
2735 /* if there is no best pad, we have reached EOS */
2737 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2738 gst_matroska_mux_finish (mux);
2739 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2740 ret = GST_FLOW_UNEXPECTED;
2743 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2744 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2745 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2746 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2748 /* make note of first and last encountered timestamps, so we can calculate
2749 * the actual duration later when we send an updated header on eos */
2750 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2751 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2752 GstClockTime end_ts = start_ts;
2754 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2755 end_ts += GST_BUFFER_DURATION (best->buffer);
2756 else if (best->track->default_duration)
2757 end_ts += best->track->default_duration;
2759 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2760 best->end_ts = end_ts;
2762 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2763 start_ts < best->start_ts))
2764 best->start_ts = start_ts;
2767 /* write one buffer */
2768 ret = gst_matroska_mux_write_data (mux, best);
2769 } while (ret == GST_FLOW_OK && !popped);
2776 * gst_matroska_mux_change_state:
2777 * @element: #GstMatroskaMux
2778 * @transition: State change transition.
2780 * Change the muxer state.
2782 * Returns: #GstStateChangeReturn
2784 static GstStateChangeReturn
2785 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2787 GstStateChangeReturn ret;
2788 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2790 switch (transition) {
2791 case GST_STATE_CHANGE_NULL_TO_READY:
2793 case GST_STATE_CHANGE_READY_TO_PAUSED:
2794 gst_collect_pads_start (mux->collect);
2796 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2798 case GST_STATE_CHANGE_PAUSED_TO_READY:
2799 gst_collect_pads_stop (mux->collect);
2805 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2807 switch (transition) {
2808 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2810 case GST_STATE_CHANGE_PAUSED_TO_READY:
2811 gst_matroska_mux_reset (GST_ELEMENT (mux));
2813 case GST_STATE_CHANGE_READY_TO_NULL:
2823 gst_matroska_mux_set_property (GObject * object,
2824 guint prop_id, const GValue * value, GParamSpec * pspec)
2826 GstMatroskaMux *mux;
2828 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2829 mux = GST_MATROSKA_MUX (object);
2832 case ARG_WRITING_APP:
2833 if (!g_value_get_string (value)) {
2834 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2837 g_free (mux->writing_app);
2838 mux->writing_app = g_value_dup_string (value);
2840 case ARG_MATROSKA_VERSION:
2841 mux->matroska_version = g_value_get_int (value);
2844 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2850 gst_matroska_mux_get_property (GObject * object,
2851 guint prop_id, GValue * value, GParamSpec * pspec)
2853 GstMatroskaMux *mux;
2855 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2856 mux = GST_MATROSKA_MUX (object);
2859 case ARG_WRITING_APP:
2860 g_value_set_string (value, mux->writing_app);
2862 case ARG_MATROSKA_VERSION:
2863 g_value_set_int (value, mux->matroska_version);
2866 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2872 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2874 return gst_element_register (plugin, "matroskamux",
2875 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);