1 /* GStreamer Matroska muxer/demuxer
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 * (c) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * matroska-mux.c: matroska file/stream muxer
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 /* TODO: - check everywhere that we don't write invalid values
25 * - make sure timestamps are correctly scaled everywhere
29 * SECTION:element-matroskamux
31 * matroskamux muxes different input streams into a Matroska file.
34 * <title>Example launch line</title>
36 * gst-launch -v filesrc location=/path/to/mp3 ! mp3parse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
37 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
39 * gst-launch -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
40 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
51 #include <gst/riff/riff-media.h>
52 #include <gst/tag/tag.h>
54 #include "matroska-mux.h"
55 #include "matroska-ids.h"
57 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
58 #define GST_CAT_DEFAULT matroskamux_debug
65 ARG_MIN_INDEX_INTERVAL
68 #define DEFAULT_DOCTYPE_VERSION 2
69 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
70 #define DEFAULT_MIN_INDEX_INTERVAL 0
72 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
73 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
75 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
78 GST_STATIC_CAPS ("video/x-matroska")
81 #define COMMON_VIDEO_CAPS \
82 "width = (int) [ 16, 4096 ], " \
83 "height = (int) [ 16, 4096 ], " \
84 "framerate = (fraction) [ 0, MAX ]"
86 #define COMMON_VIDEO_CAPS_NO_FRAMERATE \
87 "width = (int) [ 16, 4096 ], " \
88 "height = (int) [ 16, 4096 ] "
91 * * require codec data, etc as needed
94 static GstStaticPadTemplate videosink_templ =
95 GST_STATIC_PAD_TEMPLATE ("video_%d",
98 GST_STATIC_CAPS ("video/mpeg, "
99 "mpegversion = (int) { 1, 2, 4 }, "
100 "systemstream = (boolean) false, "
101 COMMON_VIDEO_CAPS "; "
103 COMMON_VIDEO_CAPS "; "
105 COMMON_VIDEO_CAPS "; "
107 COMMON_VIDEO_CAPS "; "
109 COMMON_VIDEO_CAPS "; "
111 COMMON_VIDEO_CAPS "; "
113 COMMON_VIDEO_CAPS "; "
115 COMMON_VIDEO_CAPS "; "
117 COMMON_VIDEO_CAPS_NO_FRAMERATE "; "
120 COMMON_VIDEO_CAPS "; "
121 "video/x-pn-realvideo, "
122 "rmversion = (int) [1, 4], "
123 COMMON_VIDEO_CAPS "; "
125 COMMON_VIDEO_CAPS "; "
127 "format = (fourcc) { YUY2, I420, YV12, UYVY, AYUV }, "
128 COMMON_VIDEO_CAPS "; "
129 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS)
132 #define COMMON_AUDIO_CAPS \
133 "channels = (int) [ 1, MAX ], " \
134 "rate = (int) [ 1, MAX ]"
137 * * require codec data, etc as needed
139 static GstStaticPadTemplate audiosink_templ =
140 GST_STATIC_PAD_TEMPLATE ("audio_%d",
143 GST_STATIC_CAPS ("audio/mpeg, "
144 "mpegversion = (int) 1, "
145 "layer = (int) [ 1, 3 ], "
146 "stream-format = (string) { raw }, "
147 COMMON_AUDIO_CAPS "; "
149 "mpegversion = (int) { 2, 4 }, "
150 COMMON_AUDIO_CAPS "; "
152 COMMON_AUDIO_CAPS "; "
154 COMMON_AUDIO_CAPS "; "
156 COMMON_AUDIO_CAPS "; "
158 COMMON_AUDIO_CAPS "; "
162 "signed = (boolean) false, "
163 COMMON_AUDIO_CAPS ";"
167 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
168 "signed = (boolean) true, "
169 COMMON_AUDIO_CAPS ";"
173 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
174 "signed = (boolean) true, "
175 COMMON_AUDIO_CAPS ";"
179 "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, "
180 "signed = (boolean) true, "
181 COMMON_AUDIO_CAPS ";"
182 "audio/x-raw-float, "
183 "width = (int) [ 32, 64 ], "
184 "endianness = (int) LITTLE_ENDIAN, "
185 COMMON_AUDIO_CAPS ";"
187 "width = (int) { 8, 16, 24 }, "
188 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
189 "audio/x-pn-realaudio, "
190 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
191 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
192 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
196 static GstStaticPadTemplate subtitlesink_templ =
197 GST_STATIC_PAD_TEMPLATE ("subtitle_%d",
200 GST_STATIC_CAPS_ANY);
202 static GArray *used_uids;
203 G_LOCK_DEFINE_STATIC (used_uids);
205 static void gst_matroska_mux_add_interfaces (GType type);
207 GST_BOILERPLATE_FULL (GstMatroskaMux, gst_matroska_mux, GstElement,
208 GST_TYPE_ELEMENT, gst_matroska_mux_add_interfaces);
210 /* Matroska muxer destructor */
211 static void gst_matroska_mux_finalize (GObject * object);
213 /* Pads collected callback */
215 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data);
218 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
220 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
221 GstPadTemplate * templ, const gchar * name);
222 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
224 /* gst internal change state handler */
225 static GstStateChangeReturn
226 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
228 /* gobject bla bla */
229 static void gst_matroska_mux_set_property (GObject * object,
230 guint prop_id, const GValue * value, GParamSpec * pspec);
231 static void gst_matroska_mux_get_property (GObject * object,
232 guint prop_id, GValue * value, GParamSpec * pspec);
235 static void gst_matroska_mux_reset (GstElement * element);
238 static guint64 gst_matroska_mux_create_uid ();
240 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
241 GstMatroskaTrackContext * context);
242 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
243 GstMatroskaTrackContext * context);
244 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
245 GstMatroskaTrackContext * context);
246 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
247 GstMatroskaTrackContext * context);
248 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
249 GstMatroskaTrackContext * context);
252 gst_matroska_mux_add_interfaces (GType type)
254 static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
256 g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
260 gst_matroska_mux_base_init (gpointer g_class)
265 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
267 GObjectClass *gobject_class;
268 GstElementClass *gstelement_class;
270 gobject_class = (GObjectClass *) klass;
271 gstelement_class = (GstElementClass *) klass;
273 gst_element_class_add_pad_template (gstelement_class,
274 gst_static_pad_template_get (&videosink_templ));
275 gst_element_class_add_pad_template (gstelement_class,
276 gst_static_pad_template_get (&audiosink_templ));
277 gst_element_class_add_pad_template (gstelement_class,
278 gst_static_pad_template_get (&subtitlesink_templ));
279 gst_element_class_add_pad_template (gstelement_class,
280 gst_static_pad_template_get (&src_templ));
281 gst_element_class_set_details_simple (gstelement_class, "Matroska muxer",
283 "Muxes video/audio/subtitle streams into a matroska stream",
284 "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
286 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
289 gobject_class->finalize = gst_matroska_mux_finalize;
291 gobject_class->get_property = gst_matroska_mux_get_property;
292 gobject_class->set_property = gst_matroska_mux_set_property;
294 g_object_class_install_property (gobject_class, ARG_WRITING_APP,
295 g_param_spec_string ("writing-app", "Writing application.",
296 "The name the application that creates the matroska file.",
297 NULL, G_PARAM_READWRITE));
298 g_object_class_install_property (gobject_class, ARG_DOCTYPE_VERSION,
299 g_param_spec_int ("version", "DocType version",
300 "This parameter determines what Matroska features can be used.",
301 1, 2, DEFAULT_DOCTYPE_VERSION, G_PARAM_READWRITE));
302 g_object_class_install_property (gobject_class, ARG_MIN_INDEX_INTERVAL,
303 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
304 "entries", "An index entry is created every so many nanoseconds.",
305 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL, G_PARAM_READWRITE));
307 gstelement_class->change_state =
308 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
309 gstelement_class->request_new_pad =
310 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
311 gstelement_class->release_pad =
312 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
317 * gst_matroska_mux_init:
318 * @mux: #GstMatroskaMux that should be initialized.
319 * @g_class: Class of the muxer.
321 * Matroska muxer constructor.
324 gst_matroska_mux_init (GstMatroskaMux * mux, GstMatroskaMuxClass * g_class)
326 GstPadTemplate *templ;
329 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
330 mux->srcpad = gst_pad_new_from_template (templ, "src");
331 g_object_unref (templ);
333 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
334 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
336 mux->collect = gst_collect_pads_new ();
337 gst_collect_pads_set_function (mux->collect,
338 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_matroska_mux_collected),
341 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
342 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
344 /* property defaults */
345 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
346 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
347 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
349 /* initialize internal variables */
351 mux->num_streams = 0;
352 mux->num_a_streams = 0;
353 mux->num_t_streams = 0;
354 mux->num_v_streams = 0;
356 /* initialize remaining variables */
357 gst_matroska_mux_reset (GST_ELEMENT (mux));
362 * gst_matroska_mux_finalize:
363 * @object: #GstMatroskaMux that should be finalized.
365 * Finalize matroska muxer.
368 gst_matroska_mux_finalize (GObject * object)
370 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
372 gst_object_unref (mux->collect);
373 gst_object_unref (mux->ebml_write);
374 if (mux->writing_app)
375 g_free (mux->writing_app);
377 G_OBJECT_CLASS (parent_class)->finalize (object);
382 * gst_matroska_mux_create_uid:
384 * Generate new unused track UID.
386 * Returns: New track UID.
389 gst_matroska_mux_create_uid (void)
396 used_uids = g_array_sized_new (FALSE, FALSE, sizeof (guint64), 10);
401 uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
402 for (i = 0; i < used_uids->len; i++) {
403 if (g_array_index (used_uids, guint64, i) == uid) {
408 g_array_append_val (used_uids, uid);
411 G_UNLOCK (used_uids);
417 * gst_matroska_pad_reset:
418 * @collect_pad: the #GstMatroskaPad
420 * Reset and/or release resources of a matroska collect pad.
423 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
426 GstMatroskaTrackType type = 0;
428 /* free track information */
429 if (collect_pad->track != NULL) {
430 /* retrieve for optional later use */
431 name = collect_pad->track->name;
432 type = collect_pad->track->type;
433 /* extra for video */
434 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
435 GstMatroskaTrackVideoContext *ctx =
436 (GstMatroskaTrackVideoContext *) collect_pad->track;
438 if (ctx->dirac_unit) {
439 gst_buffer_unref (ctx->dirac_unit);
440 ctx->dirac_unit = NULL;
443 g_free (collect_pad->track->codec_id);
444 g_free (collect_pad->track->codec_name);
446 g_free (collect_pad->track->name);
447 g_free (collect_pad->track->language);
448 g_free (collect_pad->track->codec_priv);
449 g_free (collect_pad->track);
450 collect_pad->track = NULL;
453 /* free cached buffer */
454 if (collect_pad->buffer != NULL) {
455 gst_buffer_unref (collect_pad->buffer);
456 collect_pad->buffer = NULL;
459 if (!full && type != 0) {
460 GstMatroskaTrackContext *context;
462 /* create a fresh context */
464 case GST_MATROSKA_TRACK_TYPE_VIDEO:
465 context = (GstMatroskaTrackContext *)
466 g_new0 (GstMatroskaTrackVideoContext, 1);
468 case GST_MATROSKA_TRACK_TYPE_AUDIO:
469 context = (GstMatroskaTrackContext *)
470 g_new0 (GstMatroskaTrackAudioContext, 1);
472 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
473 context = (GstMatroskaTrackContext *)
474 g_new0 (GstMatroskaTrackSubtitleContext, 1);
477 g_assert_not_reached ();
481 context->type = type;
482 context->name = name;
483 /* TODO: check default values for the context */
484 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
485 collect_pad->track = context;
486 collect_pad->buffer = NULL;
487 collect_pad->duration = 0;
488 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
489 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
494 * gst_matroska_pad_free:
495 * @collect_pad: the #GstMatroskaPad
497 * Release resources of a matroska collect pad.
500 gst_matroska_pad_free (GstMatroskaPad * collect_pad)
502 gst_matroska_pad_reset (collect_pad, TRUE);
507 * gst_matroska_mux_reset:
508 * @element: #GstMatroskaMux that should be reseted.
510 * Reset matroska muxer back to initial state.
513 gst_matroska_mux_reset (GstElement * element)
515 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
518 /* reset EBML write */
519 gst_ebml_write_reset (mux->ebml_write);
522 mux->state = GST_MATROSKA_MUX_STATE_START;
524 /* clean up existing streams */
526 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
527 GstMatroskaPad *collect_pad;
529 collect_pad = (GstMatroskaPad *) walk->data;
531 /* reset collect pad to pristine state */
532 gst_matroska_pad_reset (collect_pad, FALSE);
536 mux->num_indexes = 0;
541 mux->time_scale = GST_MSECOND;
546 mux->cluster_time = 0;
547 mux->cluster_pos = 0;
548 mux->prev_cluster_size = 0;
551 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
555 * gst_matroska_mux_handle_src_event:
556 * @pad: Pad which received the event.
557 * @event: Received event.
559 * handle events - copied from oggmux without understanding
561 * Returns: #TRUE on success.
564 gst_matroska_mux_handle_src_event (GstPad * pad, GstEvent * event)
568 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
572 /* disable seeking for now */
578 return gst_pad_event_default (pad, event);
582 * gst_matroska_mux_handle_sink_event:
583 * @pad: Pad which received the event.
584 * @event: Received event.
586 * handle events - informational ones like tags
588 * Returns: #TRUE on success.
591 gst_matroska_mux_handle_sink_event (GstPad * pad, GstEvent * event)
593 GstMatroskaTrackContext *context;
594 GstMatroskaPad *collect_pad;
599 mux = GST_MATROSKA_MUX (gst_pad_get_parent (pad));
601 /* FIXME: aren't we either leaking events here or doing a wrong unref? */
602 switch (GST_EVENT_TYPE (event)) {
606 GST_DEBUG_OBJECT (mux, "received tag event");
607 gst_event_parse_tag (event, &list);
609 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
610 g_assert (collect_pad);
611 context = collect_pad->track;
614 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
615 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
616 const gchar *lang_code;
618 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
620 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
621 context->language = g_strdup (lang_code);
623 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
628 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
629 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
632 case GST_EVENT_NEWSEGMENT:
633 /* We don't support NEWSEGMENT events */
635 gst_event_unref (event);
641 /* now GstCollectPads can take care of the rest, e.g. EOS */
643 ret = mux->collect_event (pad, event);
644 gst_object_unref (mux);
651 * gst_matroska_mux_video_pad_setcaps:
652 * @pad: Pad which got the caps.
655 * Setcaps function for video sink pad.
657 * Returns: #TRUE on success.
660 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
662 GstMatroskaTrackContext *context = NULL;
663 GstMatroskaTrackVideoContext *videocontext;
665 GstMatroskaPad *collect_pad;
666 GstStructure *structure;
667 const gchar *mimetype;
668 const GValue *value = NULL;
669 const GstBuffer *codec_buf = NULL;
670 gint width, height, pixel_width, pixel_height;
672 gboolean interlaced = FALSE;
674 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
677 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
678 g_assert (collect_pad);
679 context = collect_pad->track;
681 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
682 videocontext = (GstMatroskaTrackVideoContext *) context;
684 /* gst -> matroska ID'ing */
685 structure = gst_caps_get_structure (caps, 0);
687 mimetype = gst_structure_get_name (structure);
689 if (gst_structure_get_boolean (structure, "interlaced", &interlaced)
691 context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED;
693 if (!strcmp (mimetype, "video/x-theora")) {
694 /* we'll extract the details later from the theora identification header */
698 /* get general properties */
699 /* spec says it is mandatory */
700 if (!gst_structure_get_int (structure, "width", &width) ||
701 !gst_structure_get_int (structure, "height", &height))
704 videocontext->pixel_width = width;
705 videocontext->pixel_height = height;
706 if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
708 context->default_duration =
709 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
710 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
711 GST_TIME_ARGS (context->default_duration));
713 context->default_duration = 0;
715 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
716 &pixel_width, &pixel_height)) {
717 if (pixel_width > pixel_height) {
718 videocontext->display_width = width * pixel_width / pixel_height;
719 videocontext->display_height = height;
720 } else if (pixel_width < pixel_height) {
721 videocontext->display_width = width;
722 videocontext->display_height = height * pixel_height / pixel_width;
724 videocontext->display_width = 0;
725 videocontext->display_height = 0;
728 videocontext->display_width = 0;
729 videocontext->display_height = 0;
734 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
735 videocontext->fourcc = 0;
737 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
738 * data and other settings
742 /* extract codec_data, may turn out needed */
743 value = gst_structure_get_value (structure, "codec_data");
745 codec_buf = gst_value_get_buffer (value);
748 if (!strcmp (mimetype, "video/x-raw-yuv")) {
749 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
750 gst_structure_get_fourcc (structure, "format", &videocontext->fourcc);
751 } else if (!strcmp (mimetype, "image/jpeg")) {
752 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MJPEG);
753 } else if (!strcmp (mimetype, "video/x-xvid") /* MS/VfW compatibility cases */
754 ||!strcmp (mimetype, "video/x-huffyuv")
755 || !strcmp (mimetype, "video/x-divx")
756 || !strcmp (mimetype, "video/x-dv")
757 || !strcmp (mimetype, "video/x-h263")
758 || !strcmp (mimetype, "video/x-msmpeg")
759 || !strcmp (mimetype, "video/x-wmv")) {
760 gst_riff_strf_vids *bih;
761 gint size = sizeof (gst_riff_strf_vids);
764 if (!strcmp (mimetype, "video/x-xvid"))
765 fourcc = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
766 else if (!strcmp (mimetype, "video/x-huffyuv"))
767 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
768 else if (!strcmp (mimetype, "video/x-dv"))
769 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
770 else if (!strcmp (mimetype, "video/x-h263"))
771 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
772 else if (!strcmp (mimetype, "video/x-divx")) {
775 gst_structure_get_int (structure, "divxversion", &divxversion);
776 switch (divxversion) {
778 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
781 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
784 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
787 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
790 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
791 switch (msmpegversion) {
793 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
796 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
802 } else if (!strcmp (mimetype, "video/x-wmv")) {
805 if (gst_structure_get_fourcc (structure, "format", &format)) {
807 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
808 if (wmvversion == 2) {
809 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
810 } else if (wmvversion == 1) {
811 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
812 } else if (wmvversion == 3) {
813 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
821 bih = g_new0 (gst_riff_strf_vids, 1);
822 GST_WRITE_UINT32_LE (&bih->size, size);
823 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
824 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
825 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
826 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
827 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
828 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
829 videocontext->pixel_height * 3);
831 /* process codec private/initialization data, if any */
833 size += GST_BUFFER_SIZE (codec_buf);
834 bih = g_realloc (bih, size);
835 GST_WRITE_UINT32_LE (&bih->size, size);
836 memcpy ((guint8 *) bih + sizeof (gst_riff_strf_vids),
837 GST_BUFFER_DATA (codec_buf), GST_BUFFER_SIZE (codec_buf));
840 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
841 context->codec_priv = (gpointer) bih;
842 context->codec_priv_size = size;
843 } else if (!strcmp (mimetype, "video/x-h264")) {
844 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
846 if (context->codec_priv != NULL) {
847 g_free (context->codec_priv);
848 context->codec_priv = NULL;
849 context->codec_priv_size = 0;
852 /* Create avcC header */
853 if (codec_buf != NULL) {
854 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
855 context->codec_priv = g_malloc0 (context->codec_priv_size);
856 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
857 context->codec_priv_size);
859 } else if (!strcmp (mimetype, "video/x-theora")) {
860 const GValue *streamheader;
862 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
864 if (context->codec_priv != NULL) {
865 g_free (context->codec_priv);
866 context->codec_priv = NULL;
867 context->codec_priv_size = 0;
870 streamheader = gst_structure_get_value (structure, "streamheader");
871 if (!theora_streamheader_to_codecdata (streamheader, context)) {
872 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
873 ("theora stream headers missing or malformed"));
876 } else if (!strcmp (mimetype, "video/x-dirac")) {
877 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
878 } else if (!strcmp (mimetype, "video/x-vp8")) {
879 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_VP8);
880 } else if (!strcmp (mimetype, "video/mpeg")) {
883 gst_structure_get_int (structure, "mpegversion", &mpegversion);
884 switch (mpegversion) {
886 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
889 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
892 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
898 /* global headers may be in codec data */
899 if (codec_buf != NULL) {
900 context->codec_priv_size = GST_BUFFER_SIZE (codec_buf);
901 context->codec_priv = g_malloc0 (context->codec_priv_size);
902 memcpy (context->codec_priv, GST_BUFFER_DATA (codec_buf),
903 context->codec_priv_size);
905 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
907 /* can only make it here if preceding case verified it was version 3 */
908 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
909 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
911 const GValue *mdpr_data;
913 gst_structure_get_int (structure, "rmversion", &rmversion);
916 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
919 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
922 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
925 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
931 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
932 if (mdpr_data != NULL) {
933 guint8 *priv_data = NULL;
934 guint priv_data_size = 0;
936 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
938 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
939 priv_data = g_malloc0 (priv_data_size);
941 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
943 context->codec_priv = priv_data;
944 context->codec_priv_size = priv_data_size;
953 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
954 GST_PAD_NAME (pad), caps);
959 /* N > 0 to expect a particular number of headers, negative if the
960 number of headers is variable */
962 xiphN_streamheader_to_codecdata (const GValue * streamheader,
963 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
965 GstBuffer **buf = NULL;
968 guint bufi, i, offset, priv_data_size;
970 if (streamheader == NULL)
971 goto no_stream_headers;
973 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
976 bufarr = g_value_peek_pointer (streamheader);
977 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
979 if (N > 0 && bufarr->len != N)
982 context->xiph_headers_to_skip = bufarr->len;
984 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
985 for (i = 0; i < bufarr->len; i++) {
986 GValue *bufval = &g_array_index (bufarr, GValue, i);
988 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
990 goto wrong_content_type;
993 buf[i] = g_value_peek_pointer (bufval);
997 if (bufarr->len > 0) {
998 for (i = 0; i < bufarr->len - 1; i++) {
999 priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
1003 for (i = 0; i < bufarr->len; ++i) {
1004 priv_data_size += GST_BUFFER_SIZE (buf[i]);
1007 priv_data = g_malloc0 (priv_data_size);
1009 priv_data[0] = bufarr->len - 1;
1012 if (bufarr->len > 0) {
1013 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1014 for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
1015 priv_data[offset++] = 0xff;
1017 priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
1021 for (i = 0; i < bufarr->len; ++i) {
1022 memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
1023 GST_BUFFER_SIZE (buf[i]));
1024 offset += GST_BUFFER_SIZE (buf[i]);
1027 context->codec_priv = priv_data;
1028 context->codec_priv_size = priv_data_size;
1031 *p_buf0 = gst_buffer_ref (buf[0]);
1040 GST_WARNING ("required streamheaders missing in sink caps!");
1045 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1046 G_VALUE_TYPE_NAME (streamheader));
1051 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1056 GST_WARNING ("streamheaders array does not contain GstBuffers");
1062 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1063 GstMatroskaTrackContext * context)
1065 GstBuffer *buf0 = NULL;
1067 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1070 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 4) {
1071 GST_WARNING ("First vorbis header too small, ignoring");
1073 if (memcmp (GST_BUFFER_DATA (buf0) + 1, "vorbis", 6) == 0) {
1074 GstMatroskaTrackAudioContext *audiocontext;
1077 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 4;
1078 audiocontext = (GstMatroskaTrackAudioContext *) context;
1079 audiocontext->channels = GST_READ_UINT8 (hdr);
1080 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1085 gst_buffer_unref (buf0);
1091 theora_streamheader_to_codecdata (const GValue * streamheader,
1092 GstMatroskaTrackContext * context)
1094 GstBuffer *buf0 = NULL;
1096 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1099 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 1 + 6 + 26) {
1100 GST_WARNING ("First theora header too small, ignoring");
1101 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200theora\003\002", 9) != 0) {
1102 GST_WARNING ("First header not a theora identification header, ignoring");
1104 GstMatroskaTrackVideoContext *videocontext;
1105 guint fps_num, fps_denom, par_num, par_denom;
1108 hdr = GST_BUFFER_DATA (buf0) + 1 + 6 + 3 + 2 + 2;
1110 videocontext = (GstMatroskaTrackVideoContext *) context;
1111 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1112 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1113 hdr += 3 + 3 + 1 + 1;
1114 fps_num = GST_READ_UINT32_BE (hdr);
1115 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1116 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1117 fps_denom, fps_num);
1119 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1120 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1121 if (par_num > 0 && par_num > 0) {
1122 if (par_num > par_denom) {
1123 videocontext->display_width =
1124 videocontext->pixel_width * par_num / par_denom;
1125 videocontext->display_height = videocontext->pixel_height;
1126 } else if (par_num < par_denom) {
1127 videocontext->display_width = videocontext->pixel_width;
1128 videocontext->display_height =
1129 videocontext->pixel_height * par_denom / par_num;
1131 videocontext->display_width = 0;
1132 videocontext->display_height = 0;
1135 videocontext->display_width = 0;
1136 videocontext->display_height = 0;
1142 gst_buffer_unref (buf0);
1148 kate_streamheader_to_codecdata (const GValue * streamheader,
1149 GstMatroskaTrackContext * context)
1151 GstBuffer *buf0 = NULL;
1153 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1156 if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
1157 GST_WARNING ("First kate header too small, ignoring");
1158 } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
1159 GST_WARNING ("First header not a kate identification header, ignoring");
1163 gst_buffer_unref (buf0);
1169 flac_streamheader_to_codecdata (const GValue * streamheader,
1170 GstMatroskaTrackContext * context)
1177 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1178 GST_WARNING ("No or invalid streamheader field in the caps");
1182 bufarr = g_value_peek_pointer (streamheader);
1183 if (bufarr->len < 2) {
1184 GST_WARNING ("Too few headers in streamheader field");
1188 context->xiph_headers_to_skip = bufarr->len + 1;
1190 bufval = &g_array_index (bufarr, GValue, 0);
1191 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1192 GST_WARNING ("streamheaders array does not contain GstBuffers");
1196 buffer = g_value_peek_pointer (bufval);
1198 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1199 if (GST_BUFFER_SIZE (buffer) < 9 + 4 + 4 + 34
1200 || memcmp (GST_BUFFER_DATA (buffer) + 1, "FLAC", 4) != 0
1201 || memcmp (GST_BUFFER_DATA (buffer) + 9, "fLaC", 4) != 0) {
1202 GST_WARNING ("Invalid streamheader for FLAC");
1206 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer) - 9);
1207 context->codec_priv_size = GST_BUFFER_SIZE (buffer) - 9;
1208 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer) + 9,
1209 GST_BUFFER_SIZE (buffer) - 9);
1211 for (i = 1; i < bufarr->len; i++) {
1212 bufval = &g_array_index (bufarr, GValue, i);
1214 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1215 g_free (context->codec_priv);
1216 context->codec_priv = NULL;
1217 context->codec_priv_size = 0;
1218 GST_WARNING ("streamheaders array does not contain GstBuffers");
1222 buffer = g_value_peek_pointer (bufval);
1224 context->codec_priv =
1225 g_realloc (context->codec_priv,
1226 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1227 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1228 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1229 context->codec_priv_size =
1230 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1237 speex_streamheader_to_codecdata (const GValue * streamheader,
1238 GstMatroskaTrackContext * context)
1244 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1245 GST_WARNING ("No or invalid streamheader field in the caps");
1249 bufarr = g_value_peek_pointer (streamheader);
1250 if (bufarr->len != 2) {
1251 GST_WARNING ("Too few headers in streamheader field");
1255 context->xiph_headers_to_skip = bufarr->len + 1;
1257 bufval = &g_array_index (bufarr, GValue, 0);
1258 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1259 GST_WARNING ("streamheaders array does not contain GstBuffers");
1263 buffer = g_value_peek_pointer (bufval);
1265 if (GST_BUFFER_SIZE (buffer) < 80
1266 || memcmp (GST_BUFFER_DATA (buffer), "Speex ", 8) != 0) {
1267 GST_WARNING ("Invalid streamheader for Speex");
1271 context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
1272 context->codec_priv_size = GST_BUFFER_SIZE (buffer);
1273 memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
1274 GST_BUFFER_SIZE (buffer));
1276 bufval = &g_array_index (bufarr, GValue, 1);
1278 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1279 g_free (context->codec_priv);
1280 context->codec_priv = NULL;
1281 context->codec_priv_size = 0;
1282 GST_WARNING ("streamheaders array does not contain GstBuffers");
1286 buffer = g_value_peek_pointer (bufval);
1288 context->codec_priv =
1289 g_realloc (context->codec_priv,
1290 context->codec_priv_size + GST_BUFFER_SIZE (buffer));
1291 memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
1292 GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1293 context->codec_priv_size =
1294 context->codec_priv_size + GST_BUFFER_SIZE (buffer);
1299 static const gchar *
1300 aac_codec_data_to_codec_id (const GstBuffer * buf)
1302 const gchar *result;
1305 /* default to MAIN */
1308 if (GST_BUFFER_SIZE (buf) >= 2) {
1309 profile = GST_READ_UINT8 (GST_BUFFER_DATA (buf));
1327 GST_WARNING ("unknown AAC profile, defaulting to MAIN");
1336 * gst_matroska_mux_audio_pad_setcaps:
1337 * @pad: Pad which got the caps.
1340 * Setcaps function for audio sink pad.
1342 * Returns: #TRUE on success.
1345 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1347 GstMatroskaTrackContext *context = NULL;
1348 GstMatroskaTrackAudioContext *audiocontext;
1349 GstMatroskaMux *mux;
1350 GstMatroskaPad *collect_pad;
1351 const gchar *mimetype;
1352 gint samplerate = 0, channels = 0;
1353 GstStructure *structure;
1354 const GValue *codec_data = NULL;
1355 const GstBuffer *buf = NULL;
1356 const gchar *stream_format = NULL;
1358 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1361 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1362 g_assert (collect_pad);
1363 context = collect_pad->track;
1365 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1366 audiocontext = (GstMatroskaTrackAudioContext *) context;
1368 structure = gst_caps_get_structure (caps, 0);
1369 mimetype = gst_structure_get_name (structure);
1372 gst_structure_get_int (structure, "rate", &samplerate);
1373 gst_structure_get_int (structure, "channels", &channels);
1375 audiocontext->samplerate = samplerate;
1376 audiocontext->channels = channels;
1377 audiocontext->bitdepth = 0;
1378 context->default_duration = 0;
1380 codec_data = gst_structure_get_value (structure, "codec_data");
1382 buf = gst_value_get_buffer (codec_data);
1384 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1385 * data and other settings
1389 if (!strcmp (mimetype, "audio/mpeg")) {
1390 gint mpegversion = 0;
1392 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1393 switch (mpegversion) {
1399 gst_structure_get_int (structure, "layer", &layer);
1401 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1402 GST_WARNING_OBJECT (mux,
1403 "Unable to determine MPEG audio version, assuming 1");
1409 else if (layer == 2)
1411 else if (version == 2)
1416 context->default_duration =
1417 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1421 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1424 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1427 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1436 stream_format = gst_structure_get_string (structure, "stream-format");
1437 /* check this is raw aac */
1438 if (stream_format) {
1439 if (strcmp (stream_format, "raw") != 0) {
1440 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1444 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1449 if (mpegversion == 2)
1451 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG2 "%s",
1452 aac_codec_data_to_codec_id (buf));
1453 else if (mpegversion == 4)
1455 g_strdup_printf (GST_MATROSKA_CODEC_ID_AUDIO_AAC_MPEG4 "%s",
1456 aac_codec_data_to_codec_id (buf));
1458 g_assert_not_reached ();
1460 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1467 } else if (!strcmp (mimetype, "audio/x-raw-int")) {
1469 gint endianness = G_LITTLE_ENDIAN;
1470 gboolean signedness = TRUE;
1472 if (!gst_structure_get_int (structure, "width", &width) ||
1473 !gst_structure_get_int (structure, "depth", &depth) ||
1474 !gst_structure_get_boolean (structure, "signed", &signedness)) {
1475 GST_DEBUG_OBJECT (mux, "broken caps, width/depth/signed field missing");
1480 !gst_structure_get_int (structure, "endianness", &endianness)) {
1481 GST_DEBUG_OBJECT (mux, "broken caps, no endianness specified");
1485 if (width != depth) {
1486 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1490 /* FIXME: where is this spec'ed out? (tpm) */
1491 if ((width == 8 && signedness) || (width >= 16 && !signedness)) {
1492 GST_DEBUG_OBJECT (mux, "8-bit PCM must be unsigned, 16-bit PCM signed");
1496 audiocontext->bitdepth = depth;
1497 if (endianness == G_BIG_ENDIAN)
1498 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1500 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1502 } else if (!strcmp (mimetype, "audio/x-raw-float")) {
1505 if (!gst_structure_get_int (structure, "width", &width)) {
1506 GST_DEBUG_OBJECT (mux, "broken caps, width field missing");
1510 audiocontext->bitdepth = width;
1511 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1513 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1514 const GValue *streamheader;
1516 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1518 if (context->codec_priv != NULL) {
1519 g_free (context->codec_priv);
1520 context->codec_priv = NULL;
1521 context->codec_priv_size = 0;
1524 streamheader = gst_structure_get_value (structure, "streamheader");
1525 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1526 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1527 ("vorbis stream headers missing or malformed"));
1530 } else if (!strcmp (mimetype, "audio/x-flac")) {
1531 const GValue *streamheader;
1533 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1534 if (context->codec_priv != NULL) {
1535 g_free (context->codec_priv);
1536 context->codec_priv = NULL;
1537 context->codec_priv_size = 0;
1540 streamheader = gst_structure_get_value (structure, "streamheader");
1541 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1542 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1543 ("flac stream headers missing or malformed"));
1546 } else if (!strcmp (mimetype, "audio/x-speex")) {
1547 const GValue *streamheader;
1549 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1550 if (context->codec_priv != NULL) {
1551 g_free (context->codec_priv);
1552 context->codec_priv = NULL;
1553 context->codec_priv_size = 0;
1556 streamheader = gst_structure_get_value (structure, "streamheader");
1557 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1558 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1559 ("speex stream headers missing or malformed"));
1562 } else if (!strcmp (mimetype, "audio/x-ac3")) {
1563 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);
1564 } else if (!strcmp (mimetype, "audio/x-tta")) {
1567 /* TTA frame duration */
1568 context->default_duration = 1.04489795918367346939 * GST_SECOND;
1570 gst_structure_get_int (structure, "width", &width);
1571 audiocontext->bitdepth = width;
1572 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_TTA);
1574 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
1576 const GValue *mdpr_data;
1578 gst_structure_get_int (structure, "raversion", &raversion);
1579 switch (raversion) {
1581 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
1584 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
1587 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
1593 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1594 if (mdpr_data != NULL) {
1595 guint8 *priv_data = NULL;
1596 guint priv_data_size = 0;
1598 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1600 priv_data_size = GST_BUFFER_SIZE (codec_data_buf);
1601 priv_data = g_malloc0 (priv_data_size);
1603 memcpy (priv_data, GST_BUFFER_DATA (codec_data_buf), priv_data_size);
1605 context->codec_priv = priv_data;
1606 context->codec_priv_size = priv_data_size;
1609 } else if (!strcmp (mimetype, "audio/x-wma")) {
1611 guint codec_priv_size;
1618 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
1619 || !gst_structure_get_int (structure, "block_align", &block_align)
1620 || !gst_structure_get_int (structure, "bitrate", &bitrate)
1621 || samplerate == 0 || channels == 0) {
1622 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate/"
1623 "channels/rate on WMA caps");
1627 switch (wmaversion) {
1629 format = GST_RIFF_WAVE_FORMAT_WMAV1;
1632 format = GST_RIFF_WAVE_FORMAT_WMAV2;
1635 format = GST_RIFF_WAVE_FORMAT_WMAV3;
1638 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
1642 if (gst_structure_get_int (structure, "depth", &depth))
1643 audiocontext->bitdepth = depth;
1645 codec_priv_size = WAVEFORMATEX_SIZE;
1647 codec_priv_size += GST_BUFFER_SIZE (buf);
1649 /* serialize waveformatex structure */
1650 codec_priv = g_malloc0 (codec_priv_size);
1651 GST_WRITE_UINT16_LE (codec_priv, format);
1652 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
1653 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
1654 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
1655 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
1656 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
1658 GST_WRITE_UINT16_LE (codec_priv + 16, GST_BUFFER_SIZE (buf));
1660 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
1662 /* process codec private/initialization data, if any */
1664 memcpy ((guint8 *) codec_priv + WAVEFORMATEX_SIZE,
1665 GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1668 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_ACM);
1669 context->codec_priv = (gpointer) codec_priv;
1670 context->codec_priv_size = codec_priv_size;
1678 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1679 GST_PAD_NAME (pad), caps);
1686 * gst_matroska_mux_subtitle_pad_setcaps:
1687 * @pad: Pad which got the caps.
1690 * Setcaps function for subtitle sink pad.
1692 * Returns: #TRUE on success.
1695 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
1698 * Consider this as boilerplate code for now. There is
1699 * no single subtitle creation element in GStreamer,
1700 * neither do I know how subtitling works at all. */
1702 /* There is now (at least) one such alement (kateenc), and I'm going
1703 to handle it here and claim it works when it can be piped back
1704 through GStreamer and VLC */
1706 GstMatroskaTrackContext *context = NULL;
1707 GstMatroskaTrackSubtitleContext *scontext;
1708 GstMatroskaMux *mux;
1709 GstMatroskaPad *collect_pad;
1710 const gchar *mimetype;
1711 GstStructure *structure;
1713 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1716 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1717 g_assert (collect_pad);
1718 context = collect_pad->track;
1720 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
1721 scontext = (GstMatroskaTrackSubtitleContext *) context;
1723 structure = gst_caps_get_structure (caps, 0);
1724 mimetype = gst_structure_get_name (structure);
1727 scontext->check_utf8 = 1;
1728 scontext->invalid_utf8 = 0;
1729 context->default_duration = 0;
1731 /* TODO: - other format than Kate */
1733 if (!strcmp (mimetype, "subtitle/x-kate")) {
1734 const GValue *streamheader;
1736 context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
1738 if (context->codec_priv != NULL) {
1739 g_free (context->codec_priv);
1740 context->codec_priv = NULL;
1741 context->codec_priv_size = 0;
1744 streamheader = gst_structure_get_value (structure, "streamheader");
1745 if (!kate_streamheader_to_codecdata (streamheader, context)) {
1746 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1747 ("kate stream headers missing or malformed"));
1758 * gst_matroska_mux_request_new_pad:
1759 * @element: #GstMatroskaMux.
1760 * @templ: #GstPadTemplate.
1761 * @pad_name: New pad name.
1763 * Request pad function for sink templates.
1765 * Returns: New #GstPad.
1768 gst_matroska_mux_request_new_pad (GstElement * element,
1769 GstPadTemplate * templ, const gchar * pad_name)
1771 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1772 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
1773 GstMatroskaPad *collect_pad;
1774 GstPad *newpad = NULL;
1776 GstPadSetCapsFunction setcapsfunc = NULL;
1777 GstMatroskaTrackContext *context = NULL;
1779 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
1780 name = g_strdup_printf ("audio_%d", mux->num_a_streams++);
1781 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
1782 context = (GstMatroskaTrackContext *)
1783 g_new0 (GstMatroskaTrackAudioContext, 1);
1784 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
1785 context->name = g_strdup ("Audio");
1786 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
1787 name = g_strdup_printf ("video_%d", mux->num_v_streams++);
1788 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
1789 context = (GstMatroskaTrackContext *)
1790 g_new0 (GstMatroskaTrackVideoContext, 1);
1791 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
1792 context->name = g_strdup ("Video");
1793 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%d")) {
1794 name = g_strdup_printf ("subtitle_%d", mux->num_t_streams++);
1795 setcapsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
1796 context = (GstMatroskaTrackContext *)
1797 g_new0 (GstMatroskaTrackSubtitleContext, 1);
1798 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
1799 context->name = g_strdup ("Subtitle");
1801 GST_WARNING_OBJECT (mux, "This is not our template!");
1805 newpad = gst_pad_new_from_template (templ, name);
1807 collect_pad = (GstMatroskaPad *)
1808 gst_collect_pads_add_pad_full (mux->collect, newpad,
1809 sizeof (GstMatroskaPad),
1810 (GstCollectDataDestroyNotify) gst_matroska_pad_free);
1812 collect_pad->track = context;
1813 gst_matroska_pad_reset (collect_pad, FALSE);
1815 /* FIXME: hacked way to override/extend the event function of
1816 * GstCollectPads; because it sets its own event function giving the
1817 * element no access to events.
1818 * TODO GstCollectPads should really give its 'users' a clean chance to
1819 * properly handle events that are not meant for collectpads itself.
1820 * Perhaps a callback or so, though rejected (?) in #340060.
1821 * This would allow (clean) transcoding of info from demuxer/streams
1822 * to another muxer */
1823 mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
1824 gst_pad_set_event_function (newpad,
1825 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event));
1827 gst_pad_set_setcaps_function (newpad, setcapsfunc);
1828 gst_pad_set_active (newpad, TRUE);
1829 gst_element_add_pad (element, newpad);
1836 * gst_matroska_mux_release_pad:
1837 * @element: #GstMatroskaMux.
1838 * @pad: Pad to release.
1840 * Release a previously requested pad.
1843 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
1845 GstMatroskaMux *mux;
1848 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1850 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
1851 GstCollectData *cdata = (GstCollectData *) walk->data;
1852 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
1854 if (cdata->pad == pad) {
1855 GstClockTime min_dur; /* observed minimum duration */
1857 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
1858 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
1859 min_dur = GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
1860 if (collect_pad->duration < min_dur)
1861 collect_pad->duration = min_dur;
1864 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
1865 mux->duration < collect_pad->duration)
1866 mux->duration = collect_pad->duration;
1872 gst_collect_pads_remove_pad (mux->collect, pad);
1873 if (gst_element_remove_pad (element, pad))
1879 * gst_matroska_mux_track_header:
1880 * @mux: #GstMatroskaMux
1881 * @context: Tack context.
1883 * Write a track header.
1886 gst_matroska_mux_track_header (GstMatroskaMux * mux,
1887 GstMatroskaTrackContext * context)
1889 GstEbmlWrite *ebml = mux->ebml_write;
1892 /* TODO: check if everything necessary is written and check default values */
1894 /* track type goes before the type-specific stuff */
1895 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
1896 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
1898 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID,
1899 gst_matroska_mux_create_uid ());
1900 if (context->default_duration) {
1901 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
1902 context->default_duration);
1904 if (context->language) {
1905 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
1909 /* type-specific stuff */
1910 switch (context->type) {
1911 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
1912 GstMatroskaTrackVideoContext *videocontext =
1913 (GstMatroskaTrackVideoContext *) context;
1915 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
1916 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
1917 videocontext->pixel_width);
1918 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
1919 videocontext->pixel_height);
1920 if (videocontext->display_width && videocontext->display_height) {
1921 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
1922 videocontext->display_width);
1923 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
1924 videocontext->display_height);
1926 if (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED)
1927 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
1928 if (videocontext->fourcc) {
1929 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
1931 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
1932 (gpointer) & fcc_le, 4);
1934 gst_ebml_write_master_finish (ebml, master);
1939 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
1940 GstMatroskaTrackAudioContext *audiocontext =
1941 (GstMatroskaTrackAudioContext *) context;
1943 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
1944 if (audiocontext->samplerate != 8000)
1945 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
1946 audiocontext->samplerate);
1947 if (audiocontext->channels != 1)
1948 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
1949 audiocontext->channels);
1950 if (audiocontext->bitdepth) {
1951 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
1952 audiocontext->bitdepth);
1954 gst_ebml_write_master_finish (ebml, master);
1960 /* doesn't need type-specific data */
1964 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
1965 if (context->codec_priv)
1966 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
1967 context->codec_priv, context->codec_priv_size);
1968 /* FIXME: until we have a nice way of getting the codecname
1969 * out of the caps, I'm not going to enable this. Too much
1970 * (useless, double, boring) work... */
1971 /* TODO: Use value from tags if any */
1972 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
1973 context->codec_name); */
1974 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
1979 * gst_matroska_mux_start:
1980 * @mux: #GstMatroskaMux
1982 * Start a new matroska file (write headers etc...)
1985 gst_matroska_mux_start (GstMatroskaMux * mux)
1987 GstEbmlWrite *ebml = mux->ebml_write;
1988 const gchar *doctype;
1989 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
1990 GST_MATROSKA_ID_TRACKS,
1991 GST_MATROSKA_ID_CUES,
1992 GST_MATROSKA_ID_TAGS,
1995 guint64 master, child;
1999 GstClockTime duration = 0;
2000 guint32 segment_uid[4];
2001 GTimeVal time = { 0, 0 };
2003 /* we start with a EBML header */
2004 doctype = mux->doctype;
2005 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
2006 doctype, mux->doctype_version);
2007 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
2009 /* start a segment */
2011 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
2012 mux->segment_master = ebml->pos;
2014 /* the rest of the header is cached */
2015 gst_ebml_write_set_cache (ebml, 0x1000);
2017 /* seekhead (table of contents) - we set the positions later */
2018 mux->seekhead_pos = ebml->pos;
2019 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
2020 for (i = 0; seekhead_id[i] != 0; i++) {
2021 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
2022 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
2023 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
2024 gst_ebml_write_master_finish (ebml, child);
2026 gst_ebml_write_master_finish (ebml, master);
2029 mux->info_pos = ebml->pos;
2030 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
2031 for (i = 0; i < 4; i++) {
2032 segment_uid[i] = g_random_int ();
2034 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
2035 (guint8 *) segment_uid, 16);
2036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
2037 mux->duration_pos = ebml->pos;
2039 for (collected = mux->collect->data; collected;
2040 collected = g_slist_next (collected)) {
2041 GstMatroskaPad *collect_pad;
2042 GstFormat format = GST_FORMAT_TIME;
2044 gint64 trackduration;
2046 collect_pad = (GstMatroskaPad *) collected->data;
2047 thepad = collect_pad->collect.pad;
2049 /* Query the total length of the track. */
2050 GST_DEBUG_OBJECT (thepad, "querying peer duration");
2051 if (gst_pad_query_peer_duration (thepad, &format, &trackduration)) {
2052 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
2053 GST_TIME_ARGS (trackduration));
2054 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
2055 duration = (GstClockTime) trackduration;
2059 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2060 gst_guint64_to_gdouble (duration) /
2061 gst_guint64_to_gdouble (mux->time_scale));
2063 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
2064 "GStreamer plugin version " PACKAGE_VERSION);
2065 if (mux->writing_app && mux->writing_app[0]) {
2066 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
2068 g_get_current_time (&time);
2069 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
2070 gst_ebml_write_master_finish (ebml, master);
2073 mux->tracks_pos = ebml->pos;
2074 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
2076 for (collected = mux->collect->data; collected;
2077 collected = g_slist_next (collected)) {
2078 GstMatroskaPad *collect_pad;
2081 collect_pad = (GstMatroskaPad *) collected->data;
2082 thepad = collect_pad->collect.pad;
2084 if (gst_pad_is_linked (thepad) && gst_pad_is_active (thepad) &&
2085 collect_pad->track->codec_id != 0) {
2086 collect_pad->track->num = tracknum++;
2087 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
2088 gst_matroska_mux_track_header (mux, collect_pad->track);
2089 gst_ebml_write_master_finish (ebml, child);
2092 gst_ebml_write_master_finish (ebml, master);
2094 /* lastly, flush the cache */
2095 gst_ebml_write_flush_cache (ebml);
2099 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
2102 /* TODO: more sensible tag mappings */
2105 const gchar *matroska_tagname;
2106 const gchar *gstreamer_tagname;
2110 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
2111 GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, {
2112 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
2113 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
2114 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
2115 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
2116 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
2117 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
2118 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
2119 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
2120 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
2121 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
2122 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
2123 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
2124 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
2126 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
2128 guint64 simpletag_master;
2130 for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) {
2131 const gchar *tagname_gst = tag_conv[i].gstreamer_tagname;
2132 const gchar *tagname_mkv = tag_conv[i].matroska_tagname;
2134 if (strcmp (tagname_gst, tag) == 0) {
2135 GValue src = { 0, };
2138 if (!gst_tag_list_copy_value (&src, list, tag))
2140 if ((dest = gst_value_serialize (&src))) {
2142 simpletag_master = gst_ebml_write_master_start (ebml,
2143 GST_MATROSKA_ID_SIMPLETAG);
2144 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
2145 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
2146 gst_ebml_write_master_finish (ebml, simpletag_master);
2149 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
2151 g_value_unset (&src);
2159 * gst_matroska_mux_finish:
2160 * @mux: #GstMatroskaMux
2162 * Finish a new matroska file (write index etc...)
2165 gst_matroska_mux_finish (GstMatroskaMux * mux)
2167 GstEbmlWrite *ebml = mux->ebml_write;
2169 guint64 duration = 0;
2171 const GstTagList *tags;
2173 /* finish last cluster */
2175 gst_ebml_write_master_finish (ebml, mux->cluster);
2179 if (mux->index != NULL) {
2181 guint64 master, pointentry_master, trackpos_master;
2183 mux->cues_pos = ebml->pos;
2184 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
2185 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
2187 for (n = 0; n < mux->num_indexes; n++) {
2188 GstMatroskaIndex *idx = &mux->index[n];
2190 pointentry_master = gst_ebml_write_master_start (ebml,
2191 GST_MATROSKA_ID_POINTENTRY);
2192 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
2193 idx->time / mux->time_scale);
2194 trackpos_master = gst_ebml_write_master_start (ebml,
2195 GST_MATROSKA_ID_CUETRACKPOSITIONS);
2196 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
2197 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
2198 idx->pos - mux->segment_master);
2199 gst_ebml_write_master_finish (ebml, trackpos_master);
2200 gst_ebml_write_master_finish (ebml, pointentry_master);
2203 gst_ebml_write_master_finish (ebml, master);
2204 gst_ebml_write_flush_cache (ebml);
2208 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
2210 if (tags != NULL && !gst_tag_list_is_empty (tags)) {
2211 guint64 master_tags, master_tag;
2213 GST_DEBUG ("Writing tags");
2215 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
2216 mux->tags_pos = ebml->pos;
2217 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
2218 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
2219 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
2220 gst_ebml_write_master_finish (ebml, master_tag);
2221 gst_ebml_write_master_finish (ebml, master_tags);
2224 /* update seekhead. We know that:
2225 * - a seekhead contains 4 entries.
2226 * - order of entries is as above.
2227 * - a seekhead has a 4-byte header + 8-byte length
2228 * - each entry is 2-byte master, 2-byte ID pointer,
2229 * 2-byte length pointer, all 8/1-byte length, 4-
2230 * byte ID and 8-byte length pointer, where the
2231 * length pointer starts at 20.
2232 * - all entries are local to the segment (so pos - segment_master).
2233 * - so each entry is at 12 + 20 + num * 28. */
2234 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
2235 mux->info_pos - mux->segment_master);
2236 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
2237 mux->tracks_pos - mux->segment_master);
2238 if (mux->index != NULL) {
2239 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
2240 mux->cues_pos - mux->segment_master);
2243 guint64 my_pos = ebml->pos;
2245 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
2246 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2247 gst_ebml_write_seek (ebml, my_pos);
2250 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
2251 mux->tags_pos - mux->segment_master);
2254 guint64 my_pos = ebml->pos;
2256 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
2257 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
2258 gst_ebml_write_seek (ebml, my_pos);
2261 /* update duration */
2262 /* first get the overall duration */
2263 /* a released track may have left a duration in here */
2264 duration = mux->duration;
2265 for (collected = mux->collect->data; collected;
2266 collected = g_slist_next (collected)) {
2267 GstMatroskaPad *collect_pad;
2268 GstClockTime min_duration; /* observed minimum duration */
2270 collect_pad = (GstMatroskaPad *) collected->data;
2272 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
2273 " end ts %" GST_TIME_FORMAT, collect_pad,
2274 GST_TIME_ARGS (collect_pad->start_ts),
2275 GST_TIME_ARGS (collect_pad->end_ts));
2277 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2278 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2280 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2281 if (collect_pad->duration < min_duration)
2282 collect_pad->duration = min_duration;
2283 GST_DEBUG_OBJECT (collect_pad, "final track duration: %" GST_TIME_FORMAT,
2284 GST_TIME_ARGS (collect_pad->duration));
2287 if (GST_CLOCK_TIME_IS_VALID (collect_pad->duration) &&
2288 duration < collect_pad->duration)
2289 duration = collect_pad->duration;
2291 if (duration != 0) {
2292 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
2293 GST_TIME_ARGS (duration));
2294 pos = mux->ebml_write->pos;
2295 gst_ebml_write_seek (ebml, mux->duration_pos);
2296 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
2297 gst_guint64_to_gdouble (duration) /
2298 gst_guint64_to_gdouble (mux->time_scale));
2299 gst_ebml_write_seek (ebml, pos);
2302 guint64 my_pos = ebml->pos;
2304 gst_ebml_write_seek (ebml, mux->duration_pos);
2305 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
2306 gst_ebml_write_seek (ebml, my_pos);
2309 /* finish segment - this also writes element length */
2310 gst_ebml_write_master_finish (ebml, mux->segment_pos);
2315 * gst_matroska_mux_best_pad:
2316 * @mux: #GstMatroskaMux
2317 * @popped: True if at least one buffer was popped from #GstCollectPads
2319 * Find a pad with the oldest data
2320 * (data from this pad should be written first).
2322 * Returns: Selected pad.
2324 static GstMatroskaPad *
2325 gst_matroska_mux_best_pad (GstMatroskaMux * mux, gboolean * popped)
2328 GstMatroskaPad *best = NULL;
2331 for (collected = mux->collect->data; collected;
2332 collected = g_slist_next (collected)) {
2333 GstMatroskaPad *collect_pad;
2335 collect_pad = (GstMatroskaPad *) collected->data;
2336 /* fetch a new buffer if needed */
2337 if (collect_pad->buffer == NULL) {
2338 collect_pad->buffer = gst_collect_pads_pop (mux->collect,
2339 (GstCollectData *) collect_pad);
2341 if (collect_pad->buffer != NULL)
2345 /* if we have a buffer check if it is better then the current best one */
2346 if (collect_pad->buffer != NULL) {
2347 if (best == NULL || !GST_BUFFER_TIMESTAMP_IS_VALID (collect_pad->buffer)
2348 || (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)
2349 && GST_BUFFER_TIMESTAMP (collect_pad->buffer) <
2350 GST_BUFFER_TIMESTAMP (best->buffer))) {
2360 * gst_matroska_mux_buffer_header:
2361 * @track: Track context.
2362 * @relative_timestamp: relative timestamp of the buffer
2363 * @flags: Buffer flags.
2365 * Create a buffer containing buffer header.
2367 * Returns: New buffer.
2370 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
2371 gint16 relative_timestamp, int flags)
2375 hdr = gst_buffer_new_and_alloc (4);
2376 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
2377 GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
2378 /* time relative to clustertime */
2379 GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
2382 GST_BUFFER_DATA (hdr)[3] = flags;
2387 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
2388 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
2389 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
2392 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
2393 GstMatroskaPad * collect_pad, GstBuffer * buf)
2395 GstMatroskaTrackVideoContext *ctx =
2396 (GstMatroskaTrackVideoContext *) collect_pad->track;
2397 const guint8 *data = GST_BUFFER_DATA (buf);
2398 guint size = GST_BUFFER_SIZE (buf);
2400 guint32 next_parse_offset;
2401 GstBuffer *ret = NULL;
2402 gboolean is_muxing_unit = FALSE;
2404 if (GST_BUFFER_SIZE (buf) < 13) {
2405 gst_buffer_unref (buf);
2409 /* Check if this buffer contains a picture or end-of-sequence packet */
2410 while (size >= 13) {
2411 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
2412 gst_buffer_unref (buf);
2416 parse_code = GST_READ_UINT8 (data + 4);
2417 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
2418 if (ctx->dirac_unit) {
2419 gst_buffer_unref (ctx->dirac_unit);
2420 ctx->dirac_unit = NULL;
2422 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
2423 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
2424 is_muxing_unit = TRUE;
2428 next_parse_offset = GST_READ_UINT32_BE (data + 5);
2430 if (G_UNLIKELY (next_parse_offset == 0))
2433 data += next_parse_offset;
2434 size -= next_parse_offset;
2437 if (ctx->dirac_unit)
2438 ctx->dirac_unit = gst_buffer_join (ctx->dirac_unit, gst_buffer_ref (buf));
2440 ctx->dirac_unit = gst_buffer_ref (buf);
2442 if (is_muxing_unit) {
2443 ret = gst_buffer_make_metadata_writable (ctx->dirac_unit);
2444 ctx->dirac_unit = NULL;
2445 gst_buffer_copy_metadata (ret, buf,
2446 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
2447 GST_BUFFER_COPY_CAPS);
2448 gst_buffer_unref (buf);
2450 gst_buffer_unref (buf);
2458 * gst_matroska_mux_write_data:
2459 * @mux: #GstMatroskaMux
2460 * @collect_pad: #GstMatroskaPad with the data
2462 * Write collected data (called from gst_matroska_mux_collected).
2464 * Returns: Result of the gst_pad_push issued to write the data.
2466 static GstFlowReturn
2467 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad)
2469 GstEbmlWrite *ebml = mux->ebml_write;
2470 GstBuffer *buf, *hdr;
2472 gboolean write_duration;
2473 gint16 relative_timestamp;
2474 gint64 relative_timestamp64;
2475 guint64 block_duration;
2476 gboolean is_video_keyframe = FALSE;
2479 buf = collect_pad->buffer;
2480 collect_pad->buffer = NULL;
2482 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
2483 if (collect_pad->track->xiph_headers_to_skip > 0) {
2484 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
2485 gst_buffer_unref (buf);
2486 --collect_pad->track->xiph_headers_to_skip;
2490 /* for dirac we have to queue up everything up to a picture unit */
2491 if (collect_pad->track->codec_id != NULL &&
2492 strcmp (collect_pad->track->codec_id,
2493 GST_MATROSKA_CODEC_ID_VIDEO_DIRAC) == 0) {
2494 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
2499 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
2500 * this would wreak havoc with time stored in matroska file */
2501 /* TODO: maybe calculate a timestamp by using the previous timestamp
2502 * and default duration */
2503 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2504 GST_WARNING_OBJECT (collect_pad->collect.pad,
2505 "Invalid buffer timestamp; dropping buffer");
2506 gst_buffer_unref (buf);
2510 /* set the timestamp for outgoing buffers */
2511 ebml->timestamp = GST_BUFFER_TIMESTAMP (buf);
2513 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
2514 !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
2515 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
2516 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
2517 is_video_keyframe = TRUE;
2521 /* start a new cluster every two seconds or at keyframe */
2522 if (mux->cluster_time + GST_SECOND * 2 < GST_BUFFER_TIMESTAMP (buf)
2523 || is_video_keyframe) {
2525 gst_ebml_write_master_finish (ebml, mux->cluster);
2526 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
2527 mux->cluster_pos = ebml->pos;
2529 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2530 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2531 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2532 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2533 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
2534 mux->prev_cluster_size);
2539 mux->cluster_pos = ebml->pos;
2540 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
2541 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
2542 GST_BUFFER_TIMESTAMP (buf) / mux->time_scale);
2543 mux->cluster_time = GST_BUFFER_TIMESTAMP (buf);
2546 /* update duration of this track */
2547 if (GST_BUFFER_DURATION_IS_VALID (buf))
2548 collect_pad->duration += GST_BUFFER_DURATION (buf);
2550 /* We currently write index entries for all video tracks or for the audio
2551 * track in a single-track audio file. This could be improved by keeping the
2552 * index only for the *first* video track. */
2554 /* TODO: index is useful for every track, should contain the number of
2555 * the block in the cluster which contains the timestamp, should also work
2556 * for files with multiple audio tracks.
2558 if (is_video_keyframe ||
2559 ((collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
2560 (mux->num_streams == 1))) {
2563 if (mux->min_index_interval != 0) {
2564 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
2565 if (mux->index[last_idx].track == collect_pad->track->num)
2570 if (last_idx < 0 || mux->min_index_interval == 0 ||
2571 (GST_CLOCK_DIFF (mux->index[last_idx].time, GST_BUFFER_TIMESTAMP (buf))
2572 >= mux->min_index_interval)) {
2573 GstMatroskaIndex *idx;
2575 if (mux->num_indexes % 32 == 0) {
2576 mux->index = g_renew (GstMatroskaIndex, mux->index,
2577 mux->num_indexes + 32);
2579 idx = &mux->index[mux->num_indexes++];
2581 idx->pos = mux->cluster_pos;
2582 idx->time = GST_BUFFER_TIMESTAMP (buf);
2583 idx->track = collect_pad->track->num;
2587 /* Check if the duration differs from the default duration. */
2588 write_duration = FALSE;
2589 block_duration = GST_BUFFER_DURATION (buf);
2590 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2591 if (block_duration != collect_pad->track->default_duration) {
2592 write_duration = TRUE;
2596 /* write the block, for doctype v2 use SimpleBlock if possible
2597 * one slice (*breath*).
2598 * FIXME: Need to do correct lacing! */
2599 relative_timestamp64 = GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time;
2600 if (relative_timestamp64 >= 0) {
2601 /* round the timestamp */
2602 relative_timestamp64 += mux->time_scale / 2;
2604 /* round the timestamp */
2605 relative_timestamp64 -= mux->time_scale / 2;
2607 relative_timestamp = relative_timestamp64 / (gint64) mux->time_scale;
2608 if (mux->doctype_version > 1 && !write_duration) {
2610 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
2613 gst_matroska_mux_create_buffer_header (collect_pad->track,
2614 relative_timestamp, flags);
2615 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
2616 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2617 gst_ebml_write_buffer (ebml, hdr);
2618 gst_ebml_write_buffer (ebml, buf);
2620 return gst_ebml_last_write_result (ebml);
2622 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
2624 gst_matroska_mux_create_buffer_header (collect_pad->track,
2625 relative_timestamp, 0);
2626 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
2627 GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
2628 gst_ebml_write_buffer (ebml, hdr);
2629 gst_ebml_write_buffer (ebml, buf);
2630 if (write_duration) {
2631 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
2632 block_duration / mux->time_scale);
2634 gst_ebml_write_master_finish (ebml, blockgroup);
2635 return gst_ebml_last_write_result (ebml);
2641 * gst_matroska_mux_collected:
2642 * @pads: #GstCollectPads
2643 * @uuser_data: #GstMatroskaMux
2645 * Collectpads callback.
2647 * Returns: #GstFlowReturn
2649 static GstFlowReturn
2650 gst_matroska_mux_collected (GstCollectPads * pads, gpointer user_data)
2652 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
2653 GstMatroskaPad *best;
2657 GST_DEBUG_OBJECT (mux, "Collected pads");
2659 /* start with a header */
2660 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
2661 if (mux->collect->data == NULL) {
2662 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2663 ("No input streams configured"));
2664 return GST_FLOW_ERROR;
2666 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
2667 gst_matroska_mux_start (mux);
2668 mux->state = GST_MATROSKA_MUX_STATE_DATA;
2672 /* which stream to write from? */
2673 best = gst_matroska_mux_best_pad (mux, &popped);
2675 /* if there is no best pad, we have reached EOS */
2677 GST_DEBUG_OBJECT (mux, "No best pad finishing...");
2678 gst_matroska_mux_finish (mux);
2679 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
2680 ret = GST_FLOW_UNEXPECTED;
2683 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
2684 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
2685 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (best->buffer)),
2686 GST_TIME_ARGS (GST_BUFFER_DURATION (best->buffer)));
2688 /* make note of first and last encountered timestamps, so we can calculate
2689 * the actual duration later when we send an updated header on eos */
2690 if (GST_BUFFER_TIMESTAMP_IS_VALID (best->buffer)) {
2691 GstClockTime start_ts = GST_BUFFER_TIMESTAMP (best->buffer);
2692 GstClockTime end_ts = start_ts;
2694 if (GST_BUFFER_DURATION_IS_VALID (best->buffer))
2695 end_ts += GST_BUFFER_DURATION (best->buffer);
2696 else if (best->track->default_duration)
2697 end_ts += best->track->default_duration;
2699 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
2700 best->end_ts = end_ts;
2702 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
2703 start_ts < best->start_ts))
2704 best->start_ts = start_ts;
2707 /* write one buffer */
2708 ret = gst_matroska_mux_write_data (mux, best);
2709 } while (ret == GST_FLOW_OK && !popped);
2716 * gst_matroska_mux_change_state:
2717 * @element: #GstMatroskaMux
2718 * @transition: State change transition.
2720 * Change the muxer state.
2722 * Returns: #GstStateChangeReturn
2724 static GstStateChangeReturn
2725 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
2727 GstStateChangeReturn ret;
2728 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2730 switch (transition) {
2731 case GST_STATE_CHANGE_NULL_TO_READY:
2733 case GST_STATE_CHANGE_READY_TO_PAUSED:
2734 gst_collect_pads_start (mux->collect);
2736 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2738 case GST_STATE_CHANGE_PAUSED_TO_READY:
2739 gst_collect_pads_stop (mux->collect);
2745 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2747 switch (transition) {
2748 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2750 case GST_STATE_CHANGE_PAUSED_TO_READY:
2751 gst_matroska_mux_reset (GST_ELEMENT (mux));
2753 case GST_STATE_CHANGE_READY_TO_NULL:
2763 gst_matroska_mux_set_property (GObject * object,
2764 guint prop_id, const GValue * value, GParamSpec * pspec)
2766 GstMatroskaMux *mux;
2768 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2769 mux = GST_MATROSKA_MUX (object);
2772 case ARG_WRITING_APP:
2773 if (!g_value_get_string (value)) {
2774 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
2777 g_free (mux->writing_app);
2778 mux->writing_app = g_value_dup_string (value);
2780 case ARG_DOCTYPE_VERSION:
2781 mux->doctype_version = g_value_get_int (value);
2783 case ARG_MIN_INDEX_INTERVAL:
2784 mux->min_index_interval = g_value_get_int64 (value);
2787 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2793 gst_matroska_mux_get_property (GObject * object,
2794 guint prop_id, GValue * value, GParamSpec * pspec)
2796 GstMatroskaMux *mux;
2798 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
2799 mux = GST_MATROSKA_MUX (object);
2802 case ARG_WRITING_APP:
2803 g_value_set_string (value, mux->writing_app);
2805 case ARG_DOCTYPE_VERSION:
2806 g_value_set_int (value, mux->doctype_version);
2808 case ARG_MIN_INDEX_INTERVAL:
2809 g_value_set_int64 (value, mux->min_index_interval);
2812 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);