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 COMMON_AUDIO_CAPS "; "
141 "mpegversion = (int) { 2, 4 }, "
142 COMMON_AUDIO_CAPS "; "
144 COMMON_AUDIO_CAPS "; "
146 COMMON_AUDIO_CAPS "; "
148 COMMON_AUDIO_CAPS "; "
150 COMMON_AUDIO_CAPS "; "
154 "signed = (boolean) false, "
155 COMMON_AUDIO_CAPS ";"
159 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
160 "signed = (boolean) true, "
161 COMMON_AUDIO_CAPS ";"
165 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
166 "signed = (boolean) true, "
167 COMMON_AUDIO_CAPS ";"
171 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
172 "signed = (boolean) true, "
173 COMMON_AUDIO_CAPS ";"
174 "audio/x-raw-float, "
175 "width = (int) [ 32, 64 ], "
176 "endianness = (int) LITTLE_ENDIAN, "
177 COMMON_AUDIO_CAPS ";"
179 "width = (int) { 8, 16, 24 }, "
180 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
181 "audio/x-pn-realaudio, "
182 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
183 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
184 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
188 static GstStaticPadTemplate subtitlesink_templ =
189 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
192 GST_STATIC_CAPS_ANY);
194 static GArray *used_uids;
195 G_LOCK_DEFINE_STATIC (used_uids);
197 static void gst_matroska_mux_add_interfaces (GType type);
199 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
200 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
202 /* Matroska muxer destructor */
203 static void gst_matroska_mux_finalize (GObject * object);
205 /* Pads collected callback */
207 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
210 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
212 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
213 GstPadTemplate * templ, const gchar * name);
214 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
216 /* gst internal change state handler */
217 static GstStateChangeReturn
218 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
220 /* gobject bla bla */
221 static void gst_matroska_mux_set_property (GObject * object,
222 guint prop_id, const GValue * value, GParamSpec * pspec);
223 static void gst_matroska_mux_get_property (GObject * object,
224 guint prop_id, GValue * value, GParamSpec * pspec);
227 static void gst_matroska_mux_reset (GstElement * element);
230 static guint64 gst_matroska_mux_create_uid ();
232 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
233 GstMatroskaTrackContext * context);
234 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
235 GstMatroskaTrackContext * context);
236 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
237 GstMatroskaTrackContext * context);
238 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
239 GstMatroskaTrackContext * context);
240 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
241 GstMatroskaTrackContext * context);
244 gst_matroska_mux_add_interfaces (GType type)
246 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
248 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
252 gst_matroska_mux_base_init (gpointer g_class)
254 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
256 gst_element_class_add_pad_template (element_class,
257 gst_static_pad_template_get (&videosink_templ));
258 gst_element_class_add_pad_template (element_class,
259 gst_static_pad_template_get (&audiosink_templ));
260 gst_element_class_add_pad_template (element_class,
261 gst_static_pad_template_get (&subtitlesink_templ));
262 gst_element_class_add_pad_template (element_class,
263 gst_static_pad_template_get (&src_templ));
264 gst_element_class_set_details_simple (element_class, "Matroska muxer",
266 "Muxes video/audio/subtitle streams into a matroska stream",
267 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
269 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
274 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
276 GObjectClass *gobject_class;
277 GstElementClass *gstelement_class;
279 gobject_class = (GObjectClass *) klass;
280 gstelement_class = (GstElementClass *) klass;
282 gobject_class->finalize = gst_matroska_mux_finalize;
284 gobject_class->get_property = gst_matroska_mux_get_property;
285 gobject_class->set_property = gst_matroska_mux_set_property;
287 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
288 g_param_spec_string ("writing-app", "Writing application.",
289 "The name the application that creates the matroska file.",
290 NULL, G_PARAM_READWRITE));
291 g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
292 g_param_spec_int ("version", "Matroska version",
293 "This parameter determines what matroska features can be used.",
294 1, 2, DEFAULT_MATROSKA_VERSION, G_PARAM_READWRITE));
296 gstelement_class->change_state =
297 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
298 gstelement_class->request_new_pad =
299 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
300 gstelement_class->release_pad =
301 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
306 * gst_matroska_mux_init:
307 * @mux: #GstMatroskaMux that should be initialized.
308 * @g_class: Class of the muxer.
310 * Matroska muxer constructor.
313 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
315 mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
316 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
317 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
319 mux->collect = gst_collect_pads_new ();
320 gst_collect_pads_set_function (mux->collect,
321 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
324 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
326 /* property defaults */
327 mux->matroska_version = DEFAULT_MATROSKA_VERSION;
328 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
330 /* initialize internal variables */
332 mux->num_streams = 0;
333 mux->num_a_streams = 0;
334 mux->num_t_streams = 0;
335 mux->num_v_streams = 0;
337 /* initialize remaining variables */
338 gst_matroska_mux_reset (GST_ELEMENT (mux));
343 * gst_matroska_mux_finalize:
344 * @object: #GstMatroskaMux that should be finalized.
346 * Finalize matroska muxer.
349 gst_matroska_mux_finalize (GObject * object)
351 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
353 gst_object_unref (mux->collect);
354 gst_object_unref (mux->ebml_write);
355 if (mux->writing_app)
356 g_free (mux->writing_app);
358 G_OBJECT_CLASS (parent_class)->finalize (object);
363 * gst_matroska_mux_create_uid:
365 * Generate new unused track UID.
367 * Returns: New track UID.
370 gst_matroska_mux_create_uid (void)
377 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
382 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
383 for (i = 0; i < used_uids->len; i++) {
384 if (g_array_index (used_uids, guint64, i) == uid) {
389 g_array_append_val (used_uids, uid);
392 G_UNLOCK (used_uids);
398 * gst_matroska_pad_reset:
399 * @collect_pad: the #GstMatroskaPad
401 * Reset and/or release resources of a matroska collect pad.
404 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
407 GstMatroskaTrackType type = 0;
409 /* free track information */
410 if (collect_pad->track != NULL) {
411 /* retrieve for optional later use */
412 name = collect_pad->track->name;
413 type = collect_pad->track->type;
414 /* extra for video */
415 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
416 GstMatroskaTrackVideoContext *ctx =
417 (GstMatroskaTrackVideoContext *) collect_pad->track;
419 if (ctx->dirac_unit) {
420 gst_buffer_unref (ctx->dirac_unit);
421 ctx->dirac_unit = NULL;
424 g_free (collect_pad->track->codec_id);
425 g_free (collect_pad->track->codec_name);
427 g_free (collect_pad->track->name);
428 g_free (collect_pad->track->language);
429 g_free (collect_pad->track->codec_priv);
430 g_free (collect_pad->track);
431 collect_pad->track = NULL;
434 /* free cached buffer */
435 if (collect_pad->buffer != NULL) {
436 gst_buffer_unref (collect_pad->buffer);
437 collect_pad->buffer = NULL;
440 if (!full && type != 0) {
441 GstMatroskaTrackContext *context;
443 /* create a fresh context */
445 case GST_MATROSKA_TRACK_TYPE_VIDEO:
446 context = (GstMatroskaTrackContext *)
447 g_new0 (GstMatroskaTrackVideoContext, 1);
449 case GST_MATROSKA_TRACK_TYPE_AUDIO:
450 context = (GstMatroskaTrackContext *)
451 g_new0 (GstMatroskaTrackAudioContext, 1);
453 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
454 context = (GstMatroskaTrackContext *)
455 g_new0 (GstMatroskaTrackSubtitleContext, 1);
458 g_assert_not_reached ();
462 context->type = type;
463 context->name = name;
464 /* TODO: check default values for the context */
465 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
466 collect_pad->track = context;
467 collect_pad->buffer = NULL;
468 collect_pad->duration = 0;
469 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
470 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
475 * gst_matroska_pad_free:
476 * @collect_pad: the #GstMatroskaPad
478 * Release resources of a matroska collect pad.
481 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
483 gst_matroska_pad_reset (collect_pad, TRUE);
488 * gst_matroska_mux_reset:
489 * @element: #GstMatroskaMux that should be reseted.
491 * Reset matroska muxer back to initial state.
494 gst_matroska_mux_reset (GstElement * element)
496 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
499 /* reset EBML write */
500 gst_ebml_write_reset (mux->ebml_write);
503 mux->state = GST_MATROSKA_MUX_STATE_START;
505 /* clean up existing streams */
507 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
508 GstMatroskaPad *collect_pad;
510 collect_pad = (GstMatroskaPad *) walk->data;
512 /* reset collect pad to pristine state */
513 gst_matroska_pad_reset (collect_pad, FALSE);
517 mux->num_indexes = 0;
522 mux->time_scale = GST_MSECOND;
527 mux->cluster_time = 0;
528 mux->cluster_pos = 0;
531 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
535 * gst_matroska_mux_handle_src_event:
536 * @pad: Pad which received the event.
537 * @event: Received event.
539 * handle events - copied from oggmux without understanding
541 * Returns: #TRUE on success.
544 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
548 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
552 /* disable seeking for now */
558 return gst_pad_event_default (pad, event);
562 * gst_matroska_mux_handle_sink_event:
563 * @pad: Pad which received the event.
564 * @event: Received event.
566 * handle events - informational ones like tags
568 * Returns: #TRUE on success.
571 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
573 GstMatroskaTrackContext *context;
574 GstMatroskaPad *collect_pad;
579 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
581 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
582 switch (GST_EVENT_TYPE (event)) {
586 GST_DEBUG_OBJECT (mux, "received tag event");
587 gst_event_parse_tag (event, &list);
589 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
590 g_assert (collect_pad);
591 context = collect_pad->track;
594 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
595 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
596 const gchar *lang_code;
598 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
600 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
601 context->language = g_strdup (lang_code);
603 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
608 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
609 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
612 case GST_EVENT_NEWSEGMENT:
613 /* We don't support NEWSEGMENT events */
615 gst_event_unref (event);
621 /* now GstCollectPads can take care of the rest, e.g. EOS */
623 ret = mux->collect_event (pad, event);
624 gst_object_unref (mux);
631 * gst_matroska_mux_video_pad_setcaps:
632 * @pad: Pad which got the caps.
635 * Setcaps function for video sink pad.
637 * Returns: #TRUE on success.
640 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
642 GstMatroskaTrackContext *context = NULL;
643 GstMatroskaTrackVideoContext *videocontext;
645 GstMatroskaPad *collect_pad;
646 GstStructure *structure;
647 const gchar *mimetype;
648 const GValue *value = NULL;
649 const GstBuffer *codec_buf = NULL;
650 gint width, height, pixel_width, pixel_height;
653 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
656 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
657 g_assert (collect_pad);
658 context = collect_pad->track;
660 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
661 videocontext = (GstMatroskaTrackVideoContext *) context;
663 /* gst -> matroska ID'ing */
664 structure = gst_caps_get_structure (caps, 0);
666 mimetype = gst_structure_get_name (structure);
668 if (!strcmp (mimetype, "video/x-theora")) {
669 /* we'll extract the details later from the theora identification header */
673 /* get general properties */
674 gst_structure_get_int (structure, "width", &width);
675 gst_structure_get_int (structure, "height", &height);
676 videocontext->pixel_width = width;
677 videocontext->pixel_height = height;
678 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
680 context->default_duration =
681 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
682 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
683 GST_TIME_ARGS (context->default_duration));
685 context->default_duration = 0;
687 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
688 &pixel_width, &pixel_height)) {
689 if (pixel_width > pixel_height) {
690 videocontext->display_width = width * pixel_width / pixel_height;
691 videocontext->display_height = height;
692 } else if (pixel_width < pixel_height) {
693 videocontext->display_width = width;
694 videocontext->display_height = height * pixel_height / pixel_width;
696 videocontext->display_width = 0;
697 videocontext->display_height = 0;
700 videocontext->display_width = 0;
701 videocontext->display_height = 0;
706 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
707 videocontext->fourcc = 0;
709 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
710 * data and other settings
714 /* extract codec_data, may turn out needed */
715 value = gst_structure_get_value (structure, "codec_data");
717 codec_buf = gst_value_get_buffer (value);
720 if (!strcmp (mimetype, "video/x-raw-yuv")) {
721 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
722 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
725 } else if (!strcmp (mimetype, "image/jpeg")) {
726 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;
821 } else if (!strcmp (mimetype, "video/x-h264")) {
822 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
824 if (context->codec_priv != NULL) {
825 g_free (context->codec_priv);
826 context->codec_priv = NULL;
827 context->codec_priv_size = 0;
830 /* Create avcC header */
831 if (codec_buf != NULL) {
832 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
833 context->codec_priv = g_malloc0 (context->codec_priv_size);
834 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
835 context->codec_priv_size);
839 } else if (!strcmp (mimetype, "video/x-theora")) {
840 const GValue *streamheader;
842 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
844 if (context->codec_priv != NULL) {
845 g_free (context->codec_priv);
846 context->codec_priv = NULL;
847 context->codec_priv_size = 0;
850 streamheader = gst_structure_get_value (structure, "streamheader");
851 if (!theora_streamheader_to_codecdata (streamheader, context)) {
852 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
853 ("theora stream headers missing or malformed"));
857 } else if (!strcmp (mimetype, "video/x-dirac")) {
858 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
861 } else if (!strcmp (mimetype, "video/mpeg")) {
864 gst_structure_get_int (structure, "mpegversion", &mpegversion);
865 switch (mpegversion) {
867 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
870 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
873 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
879 /* global headers may be in codec data */
880 if (codec_buf != NULL) {
881 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
882 context->codec_priv = g_malloc0 (context->codec_priv_size);
883 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
884 context->codec_priv_size);
888 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
890 /* can only make it here if preceding case verified it was version 3 */
891 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
894 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
896 const GValue *mdpr_data;
898 gst_structure_get_int (structure, "rmversion", &rmversion);
901 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
904 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
907 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
910 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
916 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
917 if (mdpr_data != NULL) {
918 guint8 *priv_data = NULL;
919 guint priv_data_size = 0;
921 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
923 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
924 priv_data = g_malloc0 (priv_data_size);
926 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
928 context->codec_priv = priv_data;
929 context->codec_priv_size = priv_data_size;
938 /* N > 0 to expect a particular number of headers, negative if the
939 number of headers is variable */
941 xiphN_streamheader_to_codecdata (const GValue * streamheader,
942 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
944 GstBuffer **buf = NULL;
947 guint bufi, i, offset, priv_data_size;
949 if (streamheader == NULL)
950 goto no_stream_headers;
952 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
955 bufarr = g_value_peek_pointer (streamheader);
956 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
958 if (N > 0 && bufarr->len != N)
961 context->xiph_headers_to_skip = bufarr->len;
963 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
964 for (i = 0; i < bufarr->len; i++) {
965 GValue *bufval = &g_array_index (bufarr, GValue, i);
967 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
969 goto wrong_content_type;
972 buf[i] = g_value_peek_pointer (bufval);
976 if (bufarr->len > 0) {
977 for (i = 0; i < bufarr->len - 1; i++) {
978 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
982 for (i = 0; i < bufarr->len; ++i) {
983 priv_data_size += GST_BUFFER_SIZE (buf[i]);
986 priv_data = g_malloc0 (priv_data_size);
988 priv_data[0] = bufarr->len - 1;
991 if (bufarr->len > 0) {
992 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
993 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
994 priv_data[offset++] = 0xff;
996 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1000 for (i = 0; i < bufarr->len; ++i) {
1001 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1002 GST_BUFFER_SIZE (buf[i]));
1003 offset += GST_BUFFER_SIZE (buf[i]);
1006 context->codec_priv = priv_data;
1007 context->codec_priv_size = priv_data_size;
1010 *p_buf0 = gst_buffer_ref (buf[0]);
1019 GST_WARNING ("required streamheaders missing in sink caps!");
1024 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1025 G_VALUE_TYPE_NAME (streamheader));
1030 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1035 GST_WARNING ("streamheaders array does not contain GstBuffers");
1040 /* FIXME: after release make all code use xiph3_streamheader_to_codecdata() */
1042 xiph3_streamheader_to_codecdata (const GValue * streamheader,
1043 GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
1048 guint i, offset, priv_data_size;
1050 if (streamheader == NULL)
1051 goto no_stream_headers;
1053 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1056 bufarr = g_value_peek_pointer (streamheader);
1057 if (bufarr->len != 3)
1060 context->xiph_headers_to_skip = bufarr->len;
1062 for (i = 0; i < 3; i++) {
1063 GValue *bufval = &g_array_index (bufarr, GValue, i);
1065 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER)
1066 goto wrong_content_type;
1068 buf[i] = g_value_peek_pointer (bufval);
1072 priv_data_size += GST_BUFFER_SIZE (buf[0]) / 0xff + 1;
1073 priv_data_size += GST_BUFFER_SIZE (buf[1]) / 0xff + 1;
1075 for (i = 0; i < 3; ++i) {
1076 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1079 priv_data = g_malloc0 (priv_data_size);
1084 for (i = 0; i < GST_BUFFER_SIZE (buf[0]) / 0xff; ++i) {
1085 priv_data[offset++] = 0xff;
1087 priv_data[offset++] = GST_BUFFER_SIZE (buf[0]) % 0xff;
1089 for (i = 0; i < GST_BUFFER_SIZE (buf[1]) / 0xff; ++i) {
1090 priv_data[offset++] = 0xff;
1092 priv_data[offset++] = GST_BUFFER_SIZE (buf[1]) % 0xff;
1094 for (i = 0; i < 3; ++i) {
1095 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1096 GST_BUFFER_SIZE (buf[i]));
1097 offset += GST_BUFFER_SIZE (buf[i]);
1100 context->codec_priv = priv_data;
1101 context->codec_priv_size = priv_data_size;
1104 *p_buf0 = gst_buffer_ref (buf[0]);
1111 GST_WARNING ("required streamheaders missing in sink caps!");
1116 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1117 G_VALUE_TYPE_NAME (streamheader));
1122 GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
1127 GST_WARNING ("streamheaders array does not contain GstBuffers");
1133 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1134 GstMatroskaTrackContext * context)
1136 GstBuffer *buf0 = NULL;
1138 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1139 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1142 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1143 GST_WARNING ("First vorbis header too small, ignoring");
1145 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1146 GstMatroskaTrackAudioContext *audiocontext;
1149 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1150 audiocontext = (GstMatroskaTrackAudioContext *) context;
1151 audiocontext->channels = GST_READ_UINT8 (hdr);
1152 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1157 gst_buffer_unref (buf0);
1163 theora_streamheader_to_codecdata (const GValue * streamheader,
1164 GstMatroskaTrackContext * context)
1166 GstBuffer *buf0 = NULL;
1168 /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
1169 if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
1172 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1173 GST_WARNING ("First theora header too small, ignoring");
1174 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1175 GST_WARNING ("First header not a theora identification header, ignoring");
1177 GstMatroskaTrackVideoContext *videocontext;
1178 guint fps_num, fps_denom, par_num, par_denom;
1181 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1183 videocontext = (GstMatroskaTrackVideoContext *) context;
1184 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1185 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1186 hdr += 3 + 3 + 1 + 1;
1187 fps_num = GST_READ_UINT32_BE (hdr);
1188 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1189 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1190 fps_denom, fps_num);
1192 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1193 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1194 if (par_num > 0 && par_num > 0) {
1195 if (par_num > par_denom) {
1196 videocontext->display_width =
1197 videocontext->pixel_width * par_num / par_denom;
1198 videocontext->display_height = videocontext->pixel_height;
1199 } else if (par_num < par_denom) {
1200 videocontext->display_width = videocontext->pixel_width;
1201 videocontext->display_height =
1202 videocontext->pixel_height * par_denom / par_num;
1204 videocontext->display_width = 0;
1205 videocontext->display_height = 0;
1208 videocontext->display_width = 0;
1209 videocontext->display_height = 0;
1215 gst_buffer_unref (buf0);
1221 kate_streamheader_to_codecdata (const GValue * streamheader,
1222 GstMatroskaTrackContext * context)
1224 GstBuffer *buf0 = NULL;
1226 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1229 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1230 GST_WARNING ("First kate header too small, ignoring");
1231 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1232 GST_WARNING ("First header not a kate identification header, ignoring");
1236 gst_buffer_unref (buf0);
1242 flac_streamheader_to_codecdata (const GValue * streamheader,
1243 GstMatroskaTrackContext * context)
1250 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1251 GST_WARNING ("No or invalid streamheader field in the caps");
1255 bufarr = g_value_peek_pointer (streamheader);
1256 if (bufarr->len < 2) {
1257 GST_WARNING ("Too few headers in streamheader field");
1261 context->xiph_headers_to_skip = bufarr->len + 1;
1263 bufval = &g_array_index (bufarr, GValue, 0);
1264 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1265 GST_WARNING ("streamheaders array does not contain GstBuffers");
1269 buffer = g_value_peek_pointer (bufval);
1271 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1272 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1273 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1274 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1275 GST_WARNING ("Invalid streamheader for FLAC");
1279 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1280 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1281 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1282 GST_BUFFER_SIZE (buffer) - 9);
1284 for (i = 1; i < bufarr->len; i++) {
1285 bufval = &g_array_index (bufarr, GValue, i);
1287 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1288 g_free (context->codec_priv);
1289 context->codec_priv = NULL;
1290 context->codec_priv_size = 0;
1291 GST_WARNING ("streamheaders array does not contain GstBuffers");
1295 buffer = g_value_peek_pointer (bufval);
1297 context->codec_priv =
1298 g_realloc (context->codec_priv,
1299 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1300 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1301 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1302 context->codec_priv_size =
1303 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1310 speex_streamheader_to_codecdata (const GValue * streamheader,
1311 GstMatroskaTrackContext * context)
1317 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1318 GST_WARNING ("No or invalid streamheader field in the caps");
1322 bufarr = g_value_peek_pointer (streamheader);
1323 if (bufarr->len != 2) {
1324 GST_WARNING ("Too few headers in streamheader field");
1328 context->xiph_headers_to_skip = bufarr->len + 1;
1330 bufval = &g_array_index (bufarr, GValue, 0);
1331 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1332 GST_WARNING ("streamheaders array does not contain GstBuffers");
1336 buffer = g_value_peek_pointer (bufval);
1338 if (GST_BUFFER_SIZE (buffer) < 80
1339 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1340 GST_WARNING ("Invalid streamheader for Speex");
1344 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1345 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1346 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1347 GST_BUFFER_SIZE (buffer));
1349 bufval = &g_array_index (bufarr, GValue, 1);
1351 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1352 g_free (context->codec_priv);
1353 context->codec_priv = NULL;
1354 context->codec_priv_size = 0;
1355 GST_WARNING ("streamheaders array does not contain GstBuffers");
1359 buffer = g_value_peek_pointer (bufval);
1361 context->codec_priv =
1362 g_realloc (context->codec_priv,
1363 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1364 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1365 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1366 context->codec_priv_size =
1367 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1373 aac_codec_data_to_codec_id (const GstBuffer * buf)
1378 /* default to MAIN */
1381 if (GST_BUFFER_SIZE (buf) >= 2) {
1382 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1400 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1409 * gst_matroska_mux_audio_pad_setcaps:
1410 * @pad: Pad which got the caps.
1413 * Setcaps function for audio sink pad.
1415 * Returns: #TRUE on success.
1418 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1420 GstMatroskaTrackContext *context = NULL;
1421 GstMatroskaTrackAudioContext *audiocontext;
1422 GstMatroskaMux *mux;
1423 GstMatroskaPad *collect_pad;
1424 const gchar *mimetype;
1425 gint samplerate = 0, channels = 0;
1426 GstStructure *structure;
1427 const GValue *codec_data = NULL;
1428 const GstBuffer *buf = NULL;
1430 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1433 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1434 g_assert (collect_pad);
1435 context = collect_pad->track;
1437 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1438 audiocontext = (GstMatroskaTrackAudioContext *) context;
1440 structure = gst_caps_get_structure (caps, 0);
1441 mimetype = gst_structure_get_name (structure);
1444 gst_structure_get_int (structure, "rate", &samplerate);
1445 gst_structure_get_int (structure, "channels", &channels);
1447 audiocontext->samplerate = samplerate;
1448 audiocontext->channels = channels;
1449 audiocontext->bitdepth = 0;
1450 context->default_duration = 0;
1452 codec_data = gst_structure_get_value (structure, "codec_data");
1454 buf = gst_value_get_buffer (codec_data);
1456 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1457 * data and other settings
1461 if (!strcmp (mimetype, "audio/mpeg")) {
1462 gint mpegversion = 0;
1464 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1465 switch (mpegversion) {
1471 gst_structure_get_int (structure, "layer", &layer);
1473 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1474 GST_WARNING_OBJECT (mux,
1475 "Unable to determine MPEG audio version, assuming 1");
1481 else if (layer == 2)
1483 else if (version == 2)
1488 context->default_duration =
1489 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1493 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1496 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1499 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1509 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1510 aac_codec_data_to_codec_id (buf));
1512 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1519 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1520 aac_codec_data_to_codec_id (buf));
1522 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1531 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1533 gint endianness = G_LITTLE_ENDIAN;
1534 gboolean signedness = TRUE;
1536 if (!gst_structure_get_int (structure, "width", &width) ||
1537 !gst_structure_get_int (structure, "depth", &depth) ||
1538 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1539 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1544 !gst_structure_get_int (structure, "endianness", &endianness)) {
1545 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1549 if (width != depth) {
1550 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1554 /* FIXME: where is this spec'ed out? (tpm) */
1555 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1556 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1560 audiocontext->bitdepth = depth;
1561 if (endianness == G_BIG_ENDIAN)
1562 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1564 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1567 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1570 if (!gst_structure_get_int (structure, "width", &width)) {
1571 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1575 audiocontext->bitdepth = width;
1576 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1579 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1580 const GValue *streamheader;
1582 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1584 if (context->codec_priv != NULL) {
1585 g_free (context->codec_priv);
1586 context->codec_priv = NULL;
1587 context->codec_priv_size = 0;
1590 streamheader = gst_structure_get_value (structure, "streamheader");
1591 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1592 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1593 ("vorbis stream headers missing or malformed"));
1597 } else if (!strcmp (mimetype, "audio/x-flac")) {
1598 const GValue *streamheader;
1600 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1601 if (context->codec_priv != NULL) {
1602 g_free (context->codec_priv);
1603 context->codec_priv = NULL;
1604 context->codec_priv_size = 0;
1607 streamheader = gst_structure_get_value (structure, "streamheader");
1608 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1609 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1610 ("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"));
1631 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1632 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1635 } else if (!strcmp (mimetype, "audio/x-tta")) {
1638 /* TTA frame duration */
1639 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1641 gst_structure_get_int (structure, "width", &width);
1642 audiocontext->bitdepth = width;
1643 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1646 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1648 const GValue *mdpr_data;
1650 gst_structure_get_int (structure, "raversion", &raversion);
1651 switch (raversion) {
1653 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1656 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1659 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1665 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1666 if (mdpr_data != NULL) {
1667 guint8 *priv_data = NULL;
1668 guint priv_data_size = 0;
1670 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1672 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1673 priv_data = g_malloc0 (priv_data_size);
1675 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1677 context->codec_priv = priv_data;
1678 context->codec_priv_size = priv_data_size;
1682 } else if (!strcmp (mimetype, "audio/x-wma")) {
1684 guint codec_priv_size;
1691 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1692 || !gst_structure_get_int (structure, "block_align", &block_align)
1693 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1694 || samplerate == 0 || channels == 0) {
1695 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1696 "channels/rate on WMA caps");
1700 switch (wmaversion) {
1702 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1705 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1708 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1711 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1715 if (gst_structure_get_int (structure, "depth", &depth))
1716 audiocontext->bitdepth = depth;
1718 codec_priv_size = WAVEFORMATEX_SIZE;
1720 codec_priv_size += GST_BUFFER_SIZE (buf);
1722 /* serialize waveformatex structure */
1723 codec_priv = g_malloc0 (codec_priv_size);
1724 GST_WRITE_UINT16_LE (codec_priv, format);
1725 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1726 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1727 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1728 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1729 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1731 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1733 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1735 /* process codec private/initialization data, if any */
1737 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1738 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1741 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1742 context->codec_priv = (gpointer) codec_priv;
1743 context->codec_priv_size = codec_priv_size;
1752 * gst_matroska_mux_subtitle_pad_setcaps:
1753 * @pad: Pad which got the caps.
1756 * Setcaps function for subtitle sink pad.
1758 * Returns: #TRUE on success.
1761 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1764 * Consider this as boilerplate code for now. There is
1765 * no single subtitle creation element in GStreamer,
1766 * neither do I know how subtitling works at all. */
1768 /* There is now (at least) one such alement (kateenc), and I'm going
1769 to handle it here and claim it works when it can be piped back
1770 through GStreamer and VLC */
1772 GstMatroskaTrackContext *context = NULL;
1773 GstMatroskaTrackSubtitleContext *scontext;
1774 GstMatroskaMux *mux;
1775 GstMatroskaPad *collect_pad;
1776 const gchar *mimetype;
1777 GstStructure *structure;
1779 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1782 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1783 g_assert (collect_pad);
1784 context = collect_pad->track;
1786 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1787 scontext = (GstMatroskaTrackSubtitleContext *) context;
1789 structure = gst_caps_get_structure (caps, 0);
1790 mimetype = gst_structure_get_name (structure);
1793 scontext->check_utf8 = 1;
1794 scontext->invalid_utf8 = 0;
1795 context->default_duration = 0;
1797 /* TODO: - other format than Kate */
1799 if (!strcmp (mimetype, "subtitle/x-kate")) {
1800 const GValue *streamheader;
1802 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1804 if (context->codec_priv != NULL) {
1805 g_free (context->codec_priv);
1806 context->codec_priv = NULL;
1807 context->codec_priv_size = 0;
1810 streamheader = gst_structure_get_value (structure, "streamheader");
1811 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1812 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1813 ("kate stream headers missing or malformed"));
1824 * gst_matroska_mux_request_new_pad:
1825 * @element: #GstMatroskaMux.
1826 * @templ: #GstPadTemplate.
1827 * @pad_name: New pad name.
1829 * Request pad function for sink templates.
1831 * Returns: New #GstPad.
1834 gst_matroska_mux_request_new_pad (GstElement * element,
1835 GstPadTemplate * templ, const gchar * pad_name)
1837 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1838 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1839 GstMatroskaPad *collect_pad;
1840 GstPad *newpad = NULL;
1842 GstPadSetCapsFunction setcapsfunc = NULL;
1843 GstMatroskaTrackContext *context = NULL;
1845 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1846 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1847 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1848 context = (GstMatroskaTrackContext *)
1849 g_new0 (GstMatroskaTrackAudioContext, 1);
1850 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1851 context->name = g_strdup ("Audio");
1852 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1853 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1854 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1855 context = (GstMatroskaTrackContext *)
1856 g_new0 (GstMatroskaTrackVideoContext, 1);
1857 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1858 context->name = g_strdup ("Video");
1859 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1860 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1861 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1862 context = (GstMatroskaTrackContext *)
1863 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1864 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1865 context->name = g_strdup ("Subtitle");
1867 GST_WARNING_OBJECT (mux, "This is not our template!");
1871 newpad = gst_pad_new_from_template (templ, name);
1873 collect_pad = (GstMatroskaPad *)
1874 gst_collect_pads_add_pad_full (mux->collect, newpad,
1875 sizeof (GstMatroskaPad),
1876 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1878 collect_pad->track = context;
1879 gst_matroska_pad_reset (collect_pad, FALSE);
1881 /* FIXME: hacked way to override/extend the event function of
1882 * GstCollectPads; because it sets its own event function giving the
1883 * element no access to events.
1884 * TODO GstCollectPads should really give its 'users' a clean chance to
1885 * properly handle events that are not meant for collectpads itself.
1886 * Perhaps a callback or so, though rejected (?) in #340060.
1887 * This would allow (clean) transcoding of info from demuxer/streams
1888 * to another muxer */
1889 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1890 gst_pad_set_event_function (newpad,
1891 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1893 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1894 gst_pad_set_active (newpad, TRUE);
1895 gst_element_add_pad (element, newpad);
1902 * gst_matroska_mux_release_pad:
1903 * @element: #GstMatroskaMux.
1904 * @pad: Pad to release.
1906 * Release a previously requested pad.
1909 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1911 GstMatroskaMux *mux;
1914 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1916 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1917 GstCollectData *cdata = (GstCollectData *) walk->data;
1918 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1920 if (cdata->pad == pad) {
1921 GstClockTime min_dur; /* observed minimum duration */
1923 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1924 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1925 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1926 if (collect_pad->duration < min_dur)
1927 collect_pad->duration = min_dur;
1930 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1931 mux->duration < collect_pad->duration)
1932 mux->duration = collect_pad->duration;
1938 gst_collect_pads_remove_pad (mux->collect, pad);
1939 if (gst_element_remove_pad (element, pad))
1945 * gst_matroska_mux_track_header:
1946 * @mux: #GstMatroskaMux
1947 * @context: Tack context.
1949 * Write a track header.
1952 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1953 GstMatroskaTrackContext * context)
1955 GstEbmlWrite *ebml = mux->ebml_write;
1958 /* TODO: check if everything necessary is written and check default values */
1960 /* track type goes before the type-specific stuff */
1961 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1962 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1964 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1965 gst_matroska_mux_create_uid ());
1966 if (context->default_duration) {
1967 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1968 context->default_duration);
1970 if (context->language) {
1971 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1975 /* type-specific stuff */
1976 switch (context->type) {
1977 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1978 GstMatroskaTrackVideoContext *videocontext =
1979 (GstMatroskaTrackVideoContext *) context;
1981 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1982 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1983 videocontext->pixel_width);
1984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1985 videocontext->pixel_height);
1986 if (videocontext->display_width && videocontext->display_height) {
1987 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1988 videocontext->display_width);
1989 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1990 videocontext->display_height);
1992 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1993 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1994 if (videocontext->fourcc) {
1995 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1997 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1998 (gpointer) & fcc_le, 4);
2000 gst_ebml_write_master_finish (ebml, master);
2005 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2006 GstMatroskaTrackAudioContext *audiocontext =
2007 (GstMatroskaTrackAudioContext *) context;
2009 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2010 if (audiocontext->samplerate != 8000)
2011 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2012 audiocontext->samplerate);
2013 if (audiocontext->channels != 1)
2014 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2015 audiocontext->channels);
2016 if (audiocontext->bitdepth) {
2017 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2018 audiocontext->bitdepth);
2020 gst_ebml_write_master_finish (ebml, master);
2026 /* doesn't need type-specific data */
2030 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2031 if (context->codec_priv)
2032 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2033 context->codec_priv, context->codec_priv_size);
2034 /* FIXME: until we have a nice way of getting the codecname
2035 * out of the caps, I'm not going to enable this. Too much
2036 * (useless, double, boring) work... */
2037 /* TODO: Use value from tags if any */
2038 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2039 context->codec_name); */
2040 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2045 * gst_matroska_mux_start:
2046 * @mux: #GstMatroskaMux
2048 * Start a new matroska file (write headers etc...)
2051 gst_matroska_mux_start (GstMatroskaMux * mux)
2053 GstEbmlWrite *ebml = mux->ebml_write;
2054 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2055 GST_MATROSKA_ID_TRACKS,
2056 GST_MATROSKA_ID_CUES,
2057 GST_MATROSKA_ID_TAGS,
2060 guint64 master, child;
2064 GstClockTime duration = 0;
2065 guint32 segment_uid[4];
2066 GTimeVal time = { 0, 0 };
2068 /* we start with a EBML header */
2069 gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
2071 /* start a segment */
2073 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2074 mux->segment_master = ebml->pos;
2076 /* the rest of the header is cached */
2077 gst_ebml_write_set_cache (ebml, 0x1000);
2079 /* seekhead (table of contents) - we set the positions later */
2080 mux->seekhead_pos = ebml->pos;
2081 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2082 for (i = 0; seekhead_id[i] != 0; i++) {
2083 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2084 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2085 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2086 gst_ebml_write_master_finish (ebml, child);
2088 gst_ebml_write_master_finish (ebml, master);
2091 mux->info_pos = ebml->pos;
2092 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2093 for (i = 0; i < 4; i++) {
2094 segment_uid[i] = g_random_int ();
2096 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2097 (guint8 *) segment_uid, 16);
2098 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2099 mux->duration_pos = ebml->pos;
2101 for (collected = mux->collect->data; collected;
2102 collected = g_slist_next (collected)) {
2103 GstMatroskaPad *collect_pad;
2104 GstFormat format = GST_FORMAT_TIME;
2106 gint64 trackduration;
2108 collect_pad = (GstMatroskaPad *) collected->data;
2109 thepad = collect_pad->collect.pad;
2111 /* Query the total length of the track. */
2112 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2113 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2114 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2115 GST_TIME_ARGS (trackduration));
2116 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2117 duration = (GstClockTime) trackduration;
2121 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2122 gst_guint64_to_gdouble (duration) /
2123 gst_guint64_to_gdouble (mux->time_scale));
2125 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2126 "GStreamer plugin version " PACKAGE_VERSION);
2127 if (mux->writing_app && mux->writing_app[0]) {
2128 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2130 g_get_current_time (&time);
2131 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2132 gst_ebml_write_master_finish (ebml, master);
2135 mux->tracks_pos = ebml->pos;
2136 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2138 for (collected = mux->collect->data; collected;
2139 collected = g_slist_next (collected)) {
2140 GstMatroskaPad *collect_pad;
2143 collect_pad = (GstMatroskaPad *) collected->data;
2144 thepad = collect_pad->collect.pad;
2146 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2147 collect_pad->track->codec_id != 0) {
2148 collect_pad->track->num = tracknum++;
2149 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2150 gst_matroska_mux_track_header (mux, collect_pad->track);
2151 gst_ebml_write_master_finish (ebml, child);
2154 gst_ebml_write_master_finish (ebml, master);
2156 /* lastly, flush the cache */
2157 gst_ebml_write_flush_cache (ebml);
2161 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2164 /* TODO: more sensible tag mappings */
2167 gchar *matroska_tagname;
2168 gchar *gstreamer_tagname;
2172 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2173 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2174 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2175 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2176 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2177 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2178 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2179 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2180 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2181 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2182 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2183 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2184 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2185 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2186 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2188 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2190 guint64 simpletag_master;
2192 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2193 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2194 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2196 if (strcmp (tagname_gst, tag) == 0) {
2197 GValue src = { 0, };
2200 if (!gst_tag_list_copy_value (&src, list, tag))
2202 if ((dest = gst_value_serialize (&src))) {
2204 simpletag_master = gst_ebml_write_master_start (ebml,
2205 GST_MATROSKA_ID_SIMPLETAG);
2206 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2207 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2208 gst_ebml_write_master_finish (ebml, simpletag_master);
2211 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2213 g_value_unset (&src);
2221 * gst_matroska_mux_finish:
2222 * @mux: #GstMatroskaMux
2224 * Finish a new matroska file (write index etc...)
2227 gst_matroska_mux_finish (GstMatroskaMux * mux)
2229 GstEbmlWrite *ebml = mux->ebml_write;
2231 guint64 duration = 0;
2233 const GstTagList *tags;
2235 /* finish last cluster */
2237 gst_ebml_write_master_finish (ebml, mux->cluster);
2241 if (mux->index != NULL) {
2243 guint64 master, pointentry_master, trackpos_master;
2245 mux->cues_pos = ebml->pos;
2246 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2247 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2249 for (n = 0; n < mux->num_indexes; n++) {
2250 GstMatroskaIndex *idx = &mux->index[n];
2252 pointentry_master = gst_ebml_write_master_start (ebml,
2253 GST_MATROSKA_ID_POINTENTRY);
2254 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2255 idx->time / mux->time_scale);
2256 trackpos_master = gst_ebml_write_master_start (ebml,
2257 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2258 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2259 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2260 idx->pos - mux->segment_master);
2261 gst_ebml_write_master_finish (ebml, trackpos_master);
2262 gst_ebml_write_master_finish (ebml, pointentry_master);
2265 gst_ebml_write_master_finish (ebml, master);
2266 gst_ebml_write_flush_cache (ebml);
2270 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2272 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2273 guint64 master_tags, master_tag;
2275 GST_DEBUG ("Writing tags");
2277 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2278 mux->tags_pos = ebml->pos;
2279 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2280 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2281 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2282 gst_ebml_write_master_finish (ebml, master_tag);
2283 gst_ebml_write_master_finish (ebml, master_tags);
2286 /* update seekhead. We know that:
2287 * - a seekhead contains 4 entries.
2288 * - order of entries is as above.
2289 * - a seekhead has a 4-byte header + 8-byte length
2290 * - each entry is 2-byte master, 2-byte ID pointer,
2291 * 2-byte length pointer, all 8/1-byte length, 4-
2292 * byte ID and 8-byte length pointer, where the
2293 * length pointer starts at 20.
2294 * - all entries are local to the segment (so pos - segment_master).
2295 * - so each entry is at 12 + 20 + num * 28. */
2296 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2297 mux->info_pos - mux->segment_master);
2298 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2299 mux->tracks_pos - mux->segment_master);
2300 if (mux->index != NULL) {
2301 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2302 mux->cues_pos - mux->segment_master);
2305 guint64 my_pos = ebml->pos;
2307 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2308 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2309 gst_ebml_write_seek (ebml, my_pos);
2312 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2313 mux->tags_pos - mux->segment_master);
2316 guint64 my_pos = ebml->pos;
2318 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2319 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2320 gst_ebml_write_seek (ebml, my_pos);
2323 /* update duration */
2324 /* first get the overall duration */
2325 /* a released track may have left a duration in here */
2326 duration = mux->duration;
2327 for (collected = mux->collect->data; collected;
2328 collected = g_slist_next (collected)) {
2329 GstMatroskaPad *collect_pad;
2330 GstClockTime min_duration; /* observed minimum duration */
2332 collect_pad = (GstMatroskaPad *) collected->data;
2334 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2335 " end ts %" GST_TIME_FORMAT, collect_pad,
2336 GST_TIME_ARGS (collect_pad->start_ts),
2337 GST_TIME_ARGS (collect_pad->end_ts));
2339 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2340 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2342 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2343 if (collect_pad->duration < min_duration)
2344 collect_pad->duration = min_duration;
2345 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2346 GST_TIME_ARGS (collect_pad->duration));
2349 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2350 duration < collect_pad->duration)
2351 duration = collect_pad->duration;
2353 if (duration != 0) {
2354 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2355 GST_TIME_ARGS (duration));
2356 pos = mux->ebml_write->pos;
2357 gst_ebml_write_seek (ebml, mux->duration_pos);
2358 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2359 gst_guint64_to_gdouble (duration) /
2360 gst_guint64_to_gdouble (mux->time_scale));
2361 gst_ebml_write_seek (ebml, pos);
2364 guint64 my_pos = ebml->pos;
2366 gst_ebml_write_seek (ebml, mux->duration_pos);
2367 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2368 gst_ebml_write_seek (ebml, my_pos);
2371 /* finish segment - this also writes element length */
2372 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2377 * gst_matroska_mux_best_pad:
2378 * @mux: #GstMatroskaMux
2379 * @popped: True if at least one buffer was popped from #GstCollectPads
2381 * Find a pad with the oldest data
2382 * (data from this pad should be written first).
2384 * Returns: Selected pad.
2386 static GstMatroskaPad *
2387 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2390 GstMatroskaPad *best = NULL;
2393 for (collected = mux->collect->data; collected;
2394 collected = g_slist_next (collected)) {
2395 GstMatroskaPad *collect_pad;
2397 collect_pad = (GstMatroskaPad *) collected->data;
2398 /* fetch a new buffer if needed */
2399 if (collect_pad->buffer == NULL) {
2400 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2401 (GstCollectData *) collect_pad);
2403 if (collect_pad->buffer != NULL)
2407 /* if we have a buffer check if it is better then the current best one */
2408 if (collect_pad->buffer != NULL) {
2409 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2410 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2411 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2412 GST_BUFFER_TIMESTAMP (best->buffer))) {
2422 * gst_matroska_mux_buffer_header:
2423 * @track: Track context.
2424 * @relative_timestamp: relative timestamp of the buffer
2425 * @flags: Buffer flags.
2427 * Create a buffer containing buffer header.
2429 * Returns: New buffer.
2432 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2433 gint16 relative_timestamp, int flags)
2437 hdr = gst_buffer_new_and_alloc (4);
2438 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2439 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2440 /* time relative to clustertime */
2441 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2444 GST_BUFFER_DATA (hdr)[3] = flags;
2449 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2450 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2451 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2454 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2455 GstMatroskaPad * collect_pad, GstBuffer * buf)
2457 GstMatroskaTrackVideoContext *ctx =
2458 (GstMatroskaTrackVideoContext *) collect_pad->track;
2459 const guint8 *data = GST_BUFFER_DATA (buf);
2460 guint size = GST_BUFFER_SIZE (buf);
2462 guint32 next_parse_offset;
2463 GstBuffer *ret = NULL;
2464 gboolean is_muxing_unit = FALSE;
2466 if (GST_BUFFER_SIZE (buf) < 13) {
2467 gst_buffer_unref (buf);
2471 /* Check if this buffer contains a picture or end-of-sequence packet */
2472 while (size >= 13) {
2473 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2474 gst_buffer_unref (buf);
2478 parse_code = GST_READ_UINT8 (data + 4);
2479 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2480 if (ctx->dirac_unit) {
2481 gst_buffer_unref (ctx->dirac_unit);
2482 ctx->dirac_unit = NULL;
2484 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2485 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2486 is_muxing_unit = TRUE;
2490 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2492 if (G_UNLIKELY (next_parse_offset == 0))
2495 data += next_parse_offset;
2496 size -= next_parse_offset;
2499 if (ctx->dirac_unit)
2500 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2502 ctx->dirac_unit = gst_buffer_ref (buf);
2504 if (is_muxing_unit) {
2505 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2506 ctx->dirac_unit = NULL;
2507 gst_buffer_copy_metadata (ret, buf,
2508 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2509 GST_BUFFER_COPY_CAPS);
2510 gst_buffer_unref (buf);
2512 gst_buffer_unref (buf);
2520 * gst_matroska_mux_write_data:
2521 * @mux: #GstMatroskaMux
2522 * @collect_pad: #GstMatroskaPad with the data
2524 * Write collected data (called from gst_matroska_mux_collected).
2526 * Returns: Result of the gst_pad_push issued to write the data.
2528 static GstFlowReturn
2529 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2531 GstEbmlWrite *ebml = mux->ebml_write;
2532 GstBuffer *buf, *hdr;
2534 gboolean write_duration;
2535 gint16 relative_timestamp;
2536 gint64 relative_timestamp64;
2537 guint64 block_duration;
2538 gboolean is_video_keyframe = FALSE;
2541 buf = collect_pad->buffer;
2542 collect_pad->buffer = NULL;
2544 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2545 if (collect_pad->track->xiph_headers_to_skip > 0) {
2546 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2547 gst_buffer_unref (buf);
2548 --collect_pad->track->xiph_headers_to_skip;
2552 /* for dirac we have to queue up everything up to a picture unit */
2553 if (collect_pad->track->codec_id != NULL &&
2554 strcmp (collect_pad->track->codec_id,
2555 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2556 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2561 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2562 * this would wreak havoc with time stored in matroska file */
2563 /* TODO: maybe calculate a timestamp by using the previous timestamp
2564 * and default duration */
2565 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2566 GST_WARNING_OBJECT (collect_pad->collect.pad,
2567 "Invalid buffer timestamp; dropping buffer");
2568 gst_buffer_unref (buf);
2572 /* set the timestamp for outgoing buffers */
2573 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2575 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2576 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2577 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2578 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2579 is_video_keyframe = TRUE;
2583 /* start a new cluster every two seconds or at keyframe */
2584 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2585 || is_video_keyframe) {
2587 gst_ebml_write_master_finish (ebml, mux->cluster);
2588 mux->cluster_pos = ebml->pos;
2590 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2591 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2592 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2593 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2598 mux->cluster_pos = ebml->pos;
2599 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2600 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2601 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2602 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2605 /* update duration of this track */
2606 if (GST_BUFFER_DURATION_IS_VALID (buf))
2607 collect_pad->duration += GST_BUFFER_DURATION (buf);
2609 /* We currently write an index entry for each keyframe in a
2610 * video track or one entry for each cluster in an audio track
2611 * for audio only files. This can be largely improved, such as doing
2612 * one for each keyframe or each second (for all-keyframe
2613 * streams), only the *first* video track. But that'll come later... */
2615 /* TODO: index is useful for every track, should contain the number of
2616 * the block in the cluster which contains the timestamp
2618 if (is_video_keyframe) {
2619 GstMatroskaIndex *idx;
2621 if (mux->num_indexes % 32 == 0) {
2622 mux->index = g_renew (GstMatroskaIndex, mux->index,
2623 mux->num_indexes + 32);
2625 idx = &mux->index[mux->num_indexes++];
2627 idx->pos = mux->cluster_pos;
2628 idx->time = GST_BUFFER_TIMESTAMP (buf);
2629 idx->track = collect_pad->track->num;
2630 } else if ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2631 (mux->num_streams == 1)) {
2632 GstMatroskaIndex *idx;
2634 if (mux->num_indexes % 32 == 0) {
2635 mux->index = g_renew (GstMatroskaIndex, mux->index,
2636 mux->num_indexes + 32);
2638 idx = &mux->index[mux->num_indexes++];
2640 idx->pos = mux->cluster_pos;
2641 idx->time = GST_BUFFER_TIMESTAMP (buf);
2642 idx->track = collect_pad->track->num;
2645 /* Check if the duration differs from the default duration. */
2646 write_duration = FALSE;
2647 block_duration = GST_BUFFER_DURATION (buf);
2648 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2649 if (block_duration != collect_pad->track->default_duration) {
2650 write_duration = TRUE;
2654 /* write the block, for matroska v2 use SimpleBlock if possible
2655 * one slice (*breath*).
2656 * FIXME: Need to do correct lacing! */
2657 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2658 if (relative_timestamp64 >= 0) {
2659 /* round the timestamp */
2660 relative_timestamp64 += mux->time_scale / 2;
2662 /* round the timestamp */
2663 relative_timestamp64 -= mux->time_scale / 2;
2665 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2666 if (mux->matroska_version > 1 && !write_duration) {
2668 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2671 gst_matroska_mux_create_buffer_header (collect_pad->track,
2672 relative_timestamp, flags);
2673 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2674 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2675 gst_ebml_write_buffer (ebml, hdr);
2676 gst_ebml_write_buffer (ebml, buf);
2678 return gst_ebml_last_write_result (ebml);
2680 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2682 gst_matroska_mux_create_buffer_header (collect_pad->track,
2683 relative_timestamp, 0);
2684 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2685 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2686 gst_ebml_write_buffer (ebml, hdr);
2687 gst_ebml_write_buffer (ebml, buf);
2688 if (write_duration) {
2689 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2690 block_duration / mux->time_scale);
2692 gst_ebml_write_master_finish (ebml, blockgroup);
2693 return gst_ebml_last_write_result (ebml);
2699 * gst_matroska_mux_collected:
2700 * @pads: #GstCollectPads
2701 * @uuser_data: #GstMatroskaMux
2703 * Collectpads callback.
2705 * Returns: #GstFlowReturn
2707 static GstFlowReturn
2708 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2710 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2711 GstMatroskaPad *best;
2715 GST_DEBUG_OBJECT (mux, "Collected pads");
2717 /* start with a header */
2718 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2719 if (mux->collect->data == NULL) {
2720 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2721 ("No input streams configured"));
2722 return GST_FLOW_ERROR;
2724 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2725 gst_matroska_mux_start (mux);
2726 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2730 /* which stream to write from? */
2731 best = gst_matroska_mux_best_pad (mux, &popped);
2733 /* if there is no best pad, we have reached EOS */
2735 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2736 gst_matroska_mux_finish (mux);
2737 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2738 ret = GST_FLOW_UNEXPECTED;
2741 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2742 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2743 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2744 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2746 /* make note of first and last encountered timestamps, so we can calculate
2747 * the actual duration later when we send an updated header on eos */
2748 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2749 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2750 GstClockTime end_ts = start_ts;
2752 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2753 end_ts += GST_BUFFER_DURATION (best->buffer);
2754 else if (best->track->default_duration)
2755 end_ts += best->track->default_duration;
2757 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2758 best->end_ts = end_ts;
2760 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2761 start_ts < best->start_ts))
2762 best->start_ts = start_ts;
2765 /* write one buffer */
2766 ret = gst_matroska_mux_write_data (mux, best);
2767 } while (ret == GST_FLOW_OK && !popped);
2774 * gst_matroska_mux_change_state:
2775 * @element: #GstMatroskaMux
2776 * @transition: State change transition.
2778 * Change the muxer state.
2780 * Returns: #GstStateChangeReturn
2782 static GstStateChangeReturn
2783 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2785 GstStateChangeReturn ret;
2786 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2788 switch (transition) {
2789 case GST_STATE_CHANGE_NULL_TO_READY:
2791 case GST_STATE_CHANGE_READY_TO_PAUSED:
2792 gst_collect_pads_start (mux->collect);
2794 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2796 case GST_STATE_CHANGE_PAUSED_TO_READY:
2797 gst_collect_pads_stop (mux->collect);
2803 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2805 switch (transition) {
2806 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2808 case GST_STATE_CHANGE_PAUSED_TO_READY:
2809 gst_matroska_mux_reset (GST_ELEMENT (mux));
2811 case GST_STATE_CHANGE_READY_TO_NULL:
2821 gst_matroska_mux_set_property (GObject * object,
2822 guint prop_id, const GValue * value, GParamSpec * pspec)
2824 GstMatroskaMux *mux;
2826 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2827 mux = GST_MATROSKA_MUX (object);
2830 case ARG_WRITING_APP:
2831 if (!g_value_get_string (value)) {
2832 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2835 g_free (mux->writing_app);
2836 mux->writing_app = g_value_dup_string (value);
2838 case ARG_MATROSKA_VERSION:
2839 mux->matroska_version = g_value_get_int (value);
2842 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2848 gst_matroska_mux_get_property (GObject * object,
2849 guint prop_id, GValue * value, GParamSpec * pspec)
2851 GstMatroskaMux *mux;
2853 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2854 mux = GST_MATROSKA_MUX (object);
2857 case ARG_WRITING_APP:
2858 g_value_set_string (value, mux->writing_app);
2860 case ARG_MATROSKA_VERSION:
2861 g_value_set_int (value, mux->matroska_version);
2864 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2870 gst_matroska_mux_plugin_init (GstPlugin * plugin)
2872 return gst_element_register (plugin, "matroskamux",
2873 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX);