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>
5 * (c) 2011 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
7 * matroska-mux.c: matroska file/stream muxer
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
30 * SECTION:element-matroskamux
33 * matroskamux muxes different input streams into a Matroska file.
35 * ## Example launch line
37 * gst-launch-1.0 -v filesrc location=/path/to/mp3 ! mpegaudioparse ! matroskamux name=mux ! filesink location=test.mkv filesrc location=/path/to/theora.ogg ! oggdemux ! theoraparse ! mux.
38 * ]| This pipeline muxes an MP3 file and a Ogg Theora video into a Matroska file.
40 * gst-launch-1.0 -v audiotestsrc num-buffers=100 ! audioconvert ! vorbisenc ! matroskamux ! filesink location=test.mka
41 * ]| This pipeline muxes a 440Hz sine wave encoded with the Vorbis codec into a Matroska file.
53 #include <gst/audio/audio.h>
54 #include <gst/riff/riff-media.h>
55 #include <gst/tag/tag.h>
56 #include <gst/pbutils/codec-utils.h>
58 #include "gstmatroskaelements.h"
59 #include "matroska-mux.h"
60 #include "matroska-ids.h"
62 #define GST_MATROSKA_MUX_CHAPLANG "und"
64 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
65 #define GST_CAT_DEFAULT matroskamux_debug
72 PROP_MIN_INDEX_INTERVAL,
75 PROP_MIN_CLUSTER_DURATION,
76 PROP_MAX_CLUSTER_DURATION,
79 PROP_CLUSTER_TIMESTAMP_OFFSET,
82 #define DEFAULT_DOCTYPE_VERSION 2
83 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
84 #define DEFAULT_MIN_INDEX_INTERVAL 0
85 #define DEFAULT_STREAMABLE FALSE
86 #define DEFAULT_TIMECODESCALE GST_MSECOND
87 #define DEFAULT_MIN_CLUSTER_DURATION 500 * GST_MSECOND
88 #define DEFAULT_MAX_CLUSTER_DURATION 65535 * GST_MSECOND
89 #define DEFAULT_OFFSET_TO_ZERO FALSE
90 #define DEFAULT_CLUSTER_TIMESTAMP_OFFSET 0
92 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
93 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
95 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
98 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
101 #define COMMON_VIDEO_CAPS \
102 "width = (int) [ 1, MAX ], " \
103 "height = (int) [ 1, MAX ] "
106 * * require codec data, etc as needed
109 static GstStaticPadTemplate videosink_templ =
110 GST_STATIC_PAD_TEMPLATE ("video_%u",
113 GST_STATIC_CAPS ("video/mpeg, "
114 "mpegversion = (int) { 1, 2, 4 }, "
115 "systemstream = (boolean) false, "
116 COMMON_VIDEO_CAPS "; "
117 "video/x-h264, stream-format = (string) { avc, avc3 }, alignment=au, "
118 COMMON_VIDEO_CAPS "; "
119 "video/x-h265, stream-format = (string) { hvc1, hev1 }, alignment=au, "
120 COMMON_VIDEO_CAPS "; "
122 COMMON_VIDEO_CAPS "; "
124 COMMON_VIDEO_CAPS "; "
126 COMMON_VIDEO_CAPS "; "
128 COMMON_VIDEO_CAPS "; "
130 COMMON_VIDEO_CAPS "; "
132 COMMON_VIDEO_CAPS "; "
135 COMMON_VIDEO_CAPS "; "
136 "video/x-pn-realvideo, "
137 "rmversion = (int) [1, 4], "
138 COMMON_VIDEO_CAPS "; "
140 COMMON_VIDEO_CAPS "; "
142 COMMON_VIDEO_CAPS "; "
144 "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }, "
145 COMMON_VIDEO_CAPS "; "
147 COMMON_VIDEO_CAPS "; "
148 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS "; "
149 "video/x-av1, " COMMON_VIDEO_CAPS ";"
150 "video/x-ffv, ffversion = (int) 1, " COMMON_VIDEO_CAPS)
153 #define COMMON_AUDIO_CAPS \
154 "channels = (int) [ 1, MAX ], " \
155 "rate = (int) [ 1, MAX ]"
158 * * require codec data, etc as needed
160 static GstStaticPadTemplate audiosink_templ =
161 GST_STATIC_PAD_TEMPLATE ("audio_%u",
164 GST_STATIC_CAPS ("audio/mpeg, "
165 "mpegversion = (int) 1, "
166 "layer = (int) [ 1, 3 ], "
167 COMMON_AUDIO_CAPS "; "
169 "mpegversion = (int) { 2, 4 }, "
170 "stream-format = (string) raw, "
171 COMMON_AUDIO_CAPS "; "
173 COMMON_AUDIO_CAPS "; "
175 COMMON_AUDIO_CAPS "; "
177 COMMON_AUDIO_CAPS "; "
179 COMMON_AUDIO_CAPS "; "
181 COMMON_AUDIO_CAPS "; "
184 COMMON_AUDIO_CAPS "; "
186 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
187 "layout = (string) interleaved, "
188 COMMON_AUDIO_CAPS ";"
190 "width = (int) { 8, 16, 24 }, "
191 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
192 "audio/x-pn-realaudio, "
193 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
194 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
195 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
196 COMMON_AUDIO_CAPS ";"
198 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
200 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
202 "layout = (string)dvi, "
203 "block_align = (int)[64, 8192], "
204 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
206 "channels = (int)1," "rate = (int)16000; "
208 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
211 static GstStaticPadTemplate subtitlesink_templ =
212 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
215 GST_STATIC_CAPS ("subtitle/x-kate; "
216 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
217 "application/x-usf; subpicture/x-dvd; "
218 "application/x-subtitle-unknown")
221 static gpointer parent_class; /* NULL */
223 /* Matroska muxer destructor */
224 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
225 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
226 static void gst_matroska_mux_finalize (GObject * object);
228 /* Pads collected callback */
229 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
230 GstCollectData * data, GstBuffer * buf, gpointer user_data);
231 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
232 GstCollectData * data, GstEvent * event, gpointer user_data);
235 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
236 GstObject * parent, GstEvent * event);
237 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
238 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
239 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
241 /* gst internal change state handler */
242 static GstStateChangeReturn
243 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
245 /* gobject bla bla */
246 static void gst_matroska_mux_set_property (GObject * object,
247 guint prop_id, const GValue * value, GParamSpec * pspec);
248 static void gst_matroska_mux_get_property (GObject * object,
249 guint prop_id, GValue * value, GParamSpec * pspec);
252 static void gst_matroska_mux_reset (GstElement * element);
255 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
257 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
258 GstMatroskaTrackContext * context);
259 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
260 GstMatroskaTrackContext * context);
261 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
262 GstMatroskaTrackContext * context);
263 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
264 GstMatroskaTrackContext * context);
265 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
266 GstMatroskaTrackContext * context);
268 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
270 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
271 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
272 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
274 /* Cannot use boilerplate macros here because we need the full init function
275 * signature with the additional class argument, so we use the right template
276 * for the sink caps */
278 gst_matroska_mux_get_type (void)
280 static GType object_type; /* 0 */
282 if (object_type == 0) {
283 static const GTypeInfo object_info = {
284 sizeof (GstMatroskaMuxClass),
285 NULL, /* base_init */
286 NULL, /* base_finalize */
287 (GClassInitFunc) gst_matroska_mux_class_init,
288 NULL, /* class_finalize */
289 NULL, /* class_data */
290 sizeof (GstMatroskaMux),
292 (GInstanceInitFunc) gst_matroska_mux_init
294 const GInterfaceInfo iface_info = { NULL };
296 object_type = g_type_register_static (GST_TYPE_ELEMENT,
297 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
299 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
300 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
306 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (matroskamux, "matroskamux",
307 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX, matroska_element_init (plugin));
310 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
312 GObjectClass *gobject_class;
313 GstElementClass *gstelement_class;
315 gobject_class = (GObjectClass *) klass;
316 gstelement_class = (GstElementClass *) klass;
318 gst_element_class_add_static_pad_template (gstelement_class,
320 gst_element_class_add_static_pad_template (gstelement_class,
322 gst_element_class_add_static_pad_template (gstelement_class,
323 &subtitlesink_templ);
324 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
325 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
327 "Muxes video/audio/subtitle streams into a matroska stream",
328 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
330 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
333 gobject_class->finalize = gst_matroska_mux_finalize;
335 gobject_class->get_property = gst_matroska_mux_get_property;
336 gobject_class->set_property = gst_matroska_mux_set_property;
338 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
339 g_param_spec_string ("writing-app", "Writing application.",
340 "The name the application that creates the matroska file.",
341 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
343 g_param_spec_int ("version", "DocType version",
344 "This parameter determines what Matroska features can be used.",
345 1, 2, DEFAULT_DOCTYPE_VERSION,
346 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
347 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
348 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
349 "entries", "An index entry is created every so many nanoseconds.",
350 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
352 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
353 g_param_spec_boolean ("streamable", "Determines whether output should "
354 "be streamable", "If set to true, the output should be as if it is "
355 "to be streamed and hence no indexes written or duration written.",
356 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
357 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
358 g_param_spec_int64 ("timecodescale", "Timecode Scale",
359 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
360 GST_SECOND, DEFAULT_TIMECODESCALE,
361 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
362 g_object_class_install_property (gobject_class, PROP_MIN_CLUSTER_DURATION,
363 g_param_spec_int64 ("min-cluster-duration", "Minimum cluster duration",
364 "Desired cluster duration as nanoseconds. A new cluster will be "
365 "created irrespective of this property if a force key unit event "
366 "is received. 0 means create a new cluster for each video keyframe "
367 "or for each audio buffer in audio only streams.", 0,
368 G_MAXINT64, DEFAULT_MIN_CLUSTER_DURATION,
369 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 g_object_class_install_property (gobject_class, PROP_MAX_CLUSTER_DURATION,
371 g_param_spec_int64 ("max-cluster-duration", "Maximum cluster duration",
372 "A new cluster will be created if its duration exceeds this value. "
373 "0 means no maximum duration.", 0,
374 G_MAXINT64, DEFAULT_MAX_CLUSTER_DURATION,
375 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
376 g_object_class_install_property (gobject_class, PROP_OFFSET_TO_ZERO,
377 g_param_spec_boolean ("offset-to-zero", "Offset To Zero",
378 "Offsets all streams so that the " "earliest stream starts at 0.",
379 DEFAULT_OFFSET_TO_ZERO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380 g_object_class_install_property (gobject_class, PROP_CREATION_TIME,
381 g_param_spec_boxed ("creation-time", "Creation Time",
382 "Date and time of creation. This will be used for the DateUTC field."
383 " NULL means that the current time will be used.",
384 G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
387 * GstMatroskaMux:cluster-timestamp-offset:
389 * An offset to add to all clusters/blocks (in nanoseconds)
393 g_object_class_install_property (gobject_class, PROP_CLUSTER_TIMESTAMP_OFFSET,
394 g_param_spec_uint64 ("cluster-timestamp-offset",
395 "Cluster timestamp offset",
396 "An offset to add to all clusters/blocks (in nanoseconds)", 0,
397 G_MAXUINT64, DEFAULT_CLUSTER_TIMESTAMP_OFFSET,
398 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
400 gstelement_class->change_state =
401 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
402 gstelement_class->request_new_pad =
403 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
404 gstelement_class->release_pad =
405 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
407 parent_class = g_type_class_peek_parent (klass);
411 * Start of pad option handler code
413 #define DEFAULT_PAD_FRAME_DURATION TRUE
418 PROP_PAD_FRAME_DURATION
424 gboolean frame_duration;
425 gboolean frame_duration_user;
428 typedef GstPadClass GstMatroskamuxPadClass;
430 GType gst_matroskamux_pad_get_type (void);
431 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
433 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
434 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
435 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
436 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
439 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
440 GValue * value, GParamSpec * pspec)
442 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
445 case PROP_PAD_FRAME_DURATION:
446 g_value_set_boolean (value, pad->frame_duration);
449 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
455 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
456 const GValue * value, GParamSpec * pspec)
458 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
461 case PROP_PAD_FRAME_DURATION:
462 pad->frame_duration = g_value_get_boolean (value);
463 pad->frame_duration_user = TRUE;
466 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
472 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
474 GObjectClass *gobject_class = (GObjectClass *) klass;
476 gobject_class->set_property = gst_matroskamux_pad_set_property;
477 gobject_class->get_property = gst_matroskamux_pad_get_property;
479 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
480 g_param_spec_boolean ("frame-duration", "Frame duration",
481 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
482 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
486 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
488 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
489 pad->frame_duration_user = FALSE;
493 * End of pad option handler code
497 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
499 GstPadTemplate *templ;
502 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
503 mux->srcpad = gst_pad_new_from_template (templ, "src");
505 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
506 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
507 gst_pad_use_fixed_caps (mux->srcpad);
509 mux->collect = gst_collect_pads_new ();
510 gst_collect_pads_set_clip_function (mux->collect,
511 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
512 gst_collect_pads_set_buffer_function (mux->collect,
513 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
514 gst_collect_pads_set_event_function (mux->collect,
515 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
517 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
518 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
520 /* property defaults */
521 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
522 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
523 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
524 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
525 mux->time_scale = DEFAULT_TIMECODESCALE;
526 mux->min_cluster_duration = DEFAULT_MIN_CLUSTER_DURATION;
527 mux->max_cluster_duration = DEFAULT_MAX_CLUSTER_DURATION;
528 mux->cluster_timestamp_offset = DEFAULT_CLUSTER_TIMESTAMP_OFFSET;
530 /* initialize internal variables */
532 mux->num_streams = 0;
533 mux->num_a_streams = 0;
534 mux->num_t_streams = 0;
535 mux->num_v_streams = 0;
536 mux->internal_toc = NULL;
538 /* initialize remaining variables */
539 gst_matroska_mux_reset (GST_ELEMENT (mux));
544 * gst_matroska_mux_finalize:
545 * @object: #GstMatroskaMux that should be finalized.
547 * Finalize matroska muxer.
550 gst_matroska_mux_finalize (GObject * object)
552 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
554 gst_event_replace (&mux->force_key_unit_event, NULL);
556 gst_object_unref (mux->collect);
557 gst_object_unref (mux->ebml_write);
558 g_free (mux->writing_app);
559 g_clear_pointer (&mux->creation_time, g_date_time_unref);
561 if (mux->internal_toc) {
562 gst_toc_unref (mux->internal_toc);
563 mux->internal_toc = NULL;
566 G_OBJECT_CLASS (parent_class)->finalize (object);
571 * gst_matroska_mux_create_uid:
572 * @mux: #GstMatroskaMux to generate UID for.
574 * Generate new track UID.
576 * Returns: New track UID.
579 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
581 return (((guint64) g_random_int ()) << 32) | g_random_int ();
586 * gst_matroska_pad_reset:
587 * @collect_pad: the #GstMatroskaPad
589 * Reset and/or release resources of a matroska collect pad.
592 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
595 GstMatroskaTrackType type = 0;
597 /* free track information */
598 if (collect_pad->track != NULL) {
599 /* retrieve for optional later use */
600 name = collect_pad->track->name;
601 type = collect_pad->track->type;
602 /* extra for video */
603 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
604 GstMatroskaTrackVideoContext *ctx =
605 (GstMatroskaTrackVideoContext *) collect_pad->track;
607 if (ctx->dirac_unit) {
608 gst_buffer_unref (ctx->dirac_unit);
609 ctx->dirac_unit = NULL;
612 g_free (collect_pad->track->codec_id);
613 g_free (collect_pad->track->codec_name);
615 g_free (collect_pad->track->name);
616 g_free (collect_pad->track->language);
617 g_free (collect_pad->track->codec_priv);
618 g_free (collect_pad->track);
619 collect_pad->track = NULL;
620 if (collect_pad->tags) {
621 gst_tag_list_unref (collect_pad->tags);
622 collect_pad->tags = NULL;
626 if (!full && type != 0) {
627 GstMatroskaTrackContext *context;
629 /* create a fresh context */
631 case GST_MATROSKA_TRACK_TYPE_VIDEO:
632 context = (GstMatroskaTrackContext *)
633 g_new0 (GstMatroskaTrackVideoContext, 1);
635 case GST_MATROSKA_TRACK_TYPE_AUDIO:
636 context = (GstMatroskaTrackContext *)
637 g_new0 (GstMatroskaTrackAudioContext, 1);
639 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
640 context = (GstMatroskaTrackContext *)
641 g_new0 (GstMatroskaTrackSubtitleContext, 1);
644 g_assert_not_reached ();
648 context->type = type;
649 context->name = name;
650 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
651 /* TODO: check default values for the context */
652 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
653 collect_pad->track = context;
654 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
655 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
656 collect_pad->tags = gst_tag_list_new_empty ();
657 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
662 * gst_matroska_pad_free:
663 * @collect_pad: the #GstMatroskaPad
665 * Release resources of a matroska collect pad.
668 gst_matroska_pad_free (GstPad * collect_pad)
670 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
675 * gst_matroska_mux_reset:
676 * @element: #GstMatroskaMux that should be reset.
678 * Reset matroska muxer back to initial state.
681 gst_matroska_mux_reset (GstElement * element)
683 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
686 /* reset EBML write */
687 gst_ebml_write_reset (mux->ebml_write);
690 mux->state = GST_MATROSKA_MUX_STATE_START;
692 /* clean up existing streams */
694 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
695 GstMatroskaPad *collect_pad;
697 collect_pad = (GstMatroskaPad *) walk->data;
699 /* reset collect pad to pristine state */
700 gst_matroska_pad_reset (collect_pad, FALSE);
704 mux->num_indexes = 0;
713 mux->cluster_time = 0;
714 mux->cluster_pos = 0;
715 mux->prev_cluster_size = 0;
718 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
723 gst_toc_setter_reset (GST_TOC_SETTER (mux));
724 if (mux->internal_toc) {
725 gst_toc_unref (mux->internal_toc);
726 mux->internal_toc = NULL;
729 mux->chapters_pos = 0;
733 * gst_matroska_mux_handle_src_event:
734 * @pad: Pad which received the event.
735 * @event: Received event.
737 * handle events - copied from oggmux without understanding
739 * Returns: %TRUE on success.
742 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
747 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
751 /* disable seeking for now */
757 return gst_pad_event_default (pad, parent, event);
762 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
764 if (context->codec_priv != NULL) {
765 g_free (context->codec_priv);
766 context->codec_priv = NULL;
767 context->codec_priv_size = 0;
772 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
782 /* produce comma-separated list in hex format */
783 for (i = 0; i < 16; ++i) {
785 /* replicate vobsub's slightly off RGB conversion calculation */
786 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
787 u = ((col >> 8) & 0xff) - 128;
788 v = (col & 0xff) - 128;
789 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
790 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
791 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
792 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
795 sclut = g_strjoinv (",", clutv);
797 /* build codec private; only palette for now */
798 gst_matroska_mux_free_codec_priv (context);
799 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
800 /* include terminating 0 */
801 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
803 for (i = 0; i < 16; ++i) {
810 * gst_matroska_mux_handle_sink_event:
811 * @pad: Pad which received the event.
812 * @event: Received event.
814 * handle events - informational ones like tags
816 * Returns: %TRUE on success.
819 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
820 GstCollectData * data, GstEvent * event, gpointer user_data)
822 GstMatroskaPad *collect_pad;
823 GstMatroskaTrackContext *context;
829 mux = GST_MATROSKA_MUX (user_data);
830 collect_pad = (GstMatroskaPad *) data;
832 context = collect_pad->track;
835 switch (GST_EVENT_TYPE (event)) {
836 case GST_EVENT_CAPS:{
839 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
840 gst_event_parse_caps (event, &caps);
842 ret = collect_pad->capsfunc (pad, caps);
843 gst_event_unref (event);
850 GST_DEBUG_OBJECT (mux, "received tag event");
851 gst_event_parse_tag (event, &list);
853 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
854 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
855 const gchar *lang_code;
857 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
859 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
860 g_free (context->language);
861 context->language = g_strdup (lang_code);
863 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
868 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
869 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
870 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
874 /* Stream specific tags */
875 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
877 /* If the tags contain a title, update the context name to write it there */
878 if (gst_tag_list_get_string (list, GST_TAG_TITLE, &title)) {
879 GST_INFO_OBJECT (pad, "Setting track name to '%s'", title);
880 g_free (context->name);
881 context->name = g_strdup (title);
886 gst_event_unref (event);
887 /* handled this, don't want collectpads to forward it downstream */
893 GstToc *toc, *old_toc;
895 if (mux->chapters_pos > 0)
898 GST_DEBUG_OBJECT (mux, "received toc event");
899 gst_event_parse_toc (event, &toc, NULL);
902 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
903 if (old_toc != NULL) {
905 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
906 gst_toc_unref (old_toc);
909 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
913 gst_event_unref (event);
914 /* handled this, don't want collectpads to forward it downstream */
918 case GST_EVENT_CUSTOM_DOWNSTREAM:
919 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
920 const GstStructure *structure;
922 structure = gst_event_get_structure (event);
923 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
924 gst_event_replace (&mux->force_key_unit_event, NULL);
925 mux->force_key_unit_event = event;
927 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
928 !strcmp ("dvd-spu-clut-change",
929 gst_structure_get_string (structure, "event"))) {
934 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
935 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
936 GST_DEBUG_OBJECT (pad, "... discarding");
939 /* first transform event data into table form */
940 for (i = 0; i < 16; i++) {
941 g_snprintf (name, sizeof (name), "clut%02d", i);
942 if (!gst_structure_get_int (structure, name, &value)) {
943 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
944 "contain %s field", name);
950 /* transform into private data for stream; text form */
951 gst_matroska_mux_build_vobsub_private (context, clut);
961 return gst_collect_pads_event_default (pads, data, event, FALSE);
967 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
970 g_assert (context && id);
971 g_free (context->codec_id);
972 context->codec_id = g_strdup (id);
976 check_field (GQuark field_id, const GValue * value, gpointer user_data)
978 GstStructure *structure = (GstStructure *) user_data;
979 const gchar *name = gst_structure_get_name (structure);
981 if ((g_strcmp0 (name, "video/x-h264") == 0 &&
982 !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
983 "avc3")) || (g_strcmp0 (name, "video/x-h265") == 0
984 && !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
987 /* While in theory, matroska only supports avc1 / hvc1, and doesn't support codec_data
988 * changes, in practice most decoders will use in-band SPS / PPS (avc3 / hev1), if the
989 * input stream is avc3 / hev1 we let the new codec_data slide to support "smart" encoding.
991 * We don't warn here as we already warned elsewhere.
993 if (field_id == g_quark_from_static_string ("codec_data")) {
995 } else if (field_id == g_quark_from_static_string ("tier")) {
997 } else if (field_id == g_quark_from_static_string ("profile")) {
999 } else if (field_id == g_quark_from_static_string ("level")) {
1002 } else if (gst_structure_has_name (structure, "video/x-vp8")
1003 || gst_structure_has_name (structure, "video/x-vp9")) {
1004 /* We do not use profile and streamheader for VPX so let it change
1006 if (field_id == g_quark_from_static_string ("streamheader"))
1008 else if (field_id == g_quark_from_static_string ("profile"))
1012 /* This fields aren't used and are not retained into the bitstream so we can
1014 if (g_str_has_prefix (gst_structure_get_name (structure), "video/")) {
1015 if (field_id == g_quark_from_static_string ("chroma-site"))
1017 else if (field_id == g_quark_from_static_string ("chroma-format"))
1019 else if (field_id == g_quark_from_static_string ("bit-depth-luma"))
1027 check_new_caps (GstCaps * old_caps, GstCaps * new_caps)
1029 GstStructure *old_s, *new_s;
1032 old_caps = gst_caps_copy (old_caps);
1033 new_caps = gst_caps_copy (new_caps);
1035 new_s = gst_caps_get_structure (new_caps, 0);
1036 old_s = gst_caps_get_structure (old_caps, 0);
1038 gst_structure_filter_and_map_in_place (new_s,
1039 (GstStructureFilterMapFunc) check_field, new_s);
1040 gst_structure_filter_and_map_in_place (old_s,
1041 (GstStructureFilterMapFunc) check_field, old_s);
1043 ret = gst_caps_is_subset (new_caps, old_caps);
1045 gst_caps_unref (new_caps);
1046 gst_caps_unref (old_caps);
1052 * gst_matroska_mux_video_pad_setcaps:
1053 * @pad: Pad which got the caps.
1056 * Setcaps function for video sink pad.
1058 * Returns: %TRUE on success.
1061 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
1063 GstMatroskaTrackContext *context = NULL;
1064 GstMatroskaTrackVideoContext *videocontext;
1065 GstMatroskaMux *mux;
1066 GstMatroskaPad *collect_pad;
1067 GstStructure *structure;
1068 const gchar *mimetype;
1069 const gchar *interlace_mode, *s;
1070 const GValue *value = NULL;
1071 GstBuffer *codec_buf = NULL;
1072 gint width, height, pixel_width, pixel_height;
1074 guint multiview_flags;
1077 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1079 if ((old_caps = gst_pad_get_current_caps (pad))) {
1080 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1081 && !check_new_caps (old_caps, caps)) {
1082 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1083 ("Caps changes are not supported by Matroska\nCurrent: `%"
1084 GST_PTR_FORMAT "`\nNew: `%" GST_PTR_FORMAT "`", old_caps, caps));
1085 gst_caps_unref (old_caps);
1088 gst_caps_unref (old_caps);
1089 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
1090 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1091 ("Caps on pad %" GST_PTR_FORMAT
1092 " arrived late. Headers were already written", pad));
1097 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1098 g_assert (collect_pad);
1099 context = collect_pad->track;
1101 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
1102 videocontext = (GstMatroskaTrackVideoContext *) context;
1104 /* gst -> matroska ID'ing */
1105 structure = gst_caps_get_structure (caps, 0);
1107 mimetype = gst_structure_get_name (structure);
1109 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
1110 if (interlace_mode != NULL) {
1111 if (strcmp (interlace_mode, "progressive") == 0)
1112 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE;
1114 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_INTERLACED;
1116 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_UNKNOWN;
1119 if (!strcmp (mimetype, "video/x-theora")) {
1120 /* we'll extract the details later from the theora identification header */
1124 /* get general properties */
1125 /* spec says it is mandatory */
1126 if (!gst_structure_get_int (structure, "width", &width) ||
1127 !gst_structure_get_int (structure, "height", &height))
1130 videocontext->pixel_width = width;
1131 videocontext->pixel_height = height;
1133 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
1134 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
1136 context->default_duration =
1137 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
1138 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
1139 GST_TIME_ARGS (context->default_duration));
1141 context->default_duration = 0;
1143 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
1144 &pixel_width, &pixel_height)) {
1145 if (pixel_width > pixel_height) {
1146 videocontext->display_width = width * pixel_width / pixel_height;
1147 videocontext->display_height = height;
1148 } else if (pixel_width < pixel_height) {
1149 videocontext->display_width = width;
1150 videocontext->display_height = height * pixel_height / pixel_width;
1152 videocontext->display_width = 0;
1153 videocontext->display_height = 0;
1156 videocontext->display_width = 0;
1157 videocontext->display_height = 0;
1160 if ((s = gst_structure_get_string (structure, "colorimetry"))) {
1161 if (!gst_video_colorimetry_from_string (&videocontext->colorimetry, s)) {
1162 GST_WARNING_OBJECT (pad, "Could not parse colorimetry %s", s);
1166 if ((s = gst_structure_get_string (structure, "mastering-display-info"))) {
1167 if (!gst_video_mastering_display_info_from_string
1168 (&videocontext->mastering_display_info, s)) {
1169 GST_WARNING_OBJECT (pad, "Could not parse mastering-display-metadata %s",
1172 videocontext->mastering_display_info_present = TRUE;
1176 if ((s = gst_structure_get_string (structure, "content-light-level"))) {
1177 if (!gst_video_content_light_level_from_string
1178 (&videocontext->content_light_level, s))
1179 GST_WARNING_OBJECT (pad, "Could not parse content-light-level %s", s);
1182 /* Collect stereoscopic info, if any */
1183 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1184 videocontext->multiview_mode =
1185 gst_video_multiview_mode_from_caps_string (s);
1186 gst_structure_get_flagset (structure, "multiview-flags", &multiview_flags,
1188 videocontext->multiview_flags = multiview_flags;
1193 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1194 videocontext->fourcc = 0;
1196 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1197 * data and other settings
1201 /* extract codec_data, may turn out needed */
1202 value = gst_structure_get_value (structure, "codec_data");
1204 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1207 if (!strcmp (mimetype, "video/x-raw")) {
1209 gst_matroska_mux_set_codec_id (context,
1210 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1211 fstr = gst_structure_get_string (structure, "format");
1213 if (strlen (fstr) == 4)
1214 videocontext->fourcc = GST_STR_FOURCC (fstr);
1215 else if (!strcmp (fstr, "GRAY8"))
1216 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1217 else if (!strcmp (fstr, "BGR"))
1218 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1219 else if (!strcmp (fstr, "RGB"))
1220 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1222 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1223 ||!strcmp (mimetype, "video/x-divx")
1224 || !strcmp (mimetype, "video/x-dv")
1225 || !strcmp (mimetype, "video/x-h263")
1226 || !strcmp (mimetype, "video/x-msmpeg")
1227 || !strcmp (mimetype, "video/x-wmv")
1228 || !strcmp (mimetype, "image/jpeg")) {
1229 gst_riff_strf_vids *bih;
1230 gint size = sizeof (gst_riff_strf_vids);
1233 if (!strcmp (mimetype, "video/x-huffyuv"))
1234 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1235 else if (!strcmp (mimetype, "video/x-dv"))
1236 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1237 else if (!strcmp (mimetype, "video/x-h263"))
1238 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1239 else if (!strcmp (mimetype, "video/x-divx")) {
1242 gst_structure_get_int (structure, "divxversion", &divxversion);
1243 switch (divxversion) {
1245 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1248 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1251 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1254 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1257 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1258 switch (msmpegversion) {
1260 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1263 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1269 } else if (!strcmp (mimetype, "video/x-wmv")) {
1273 fstr = gst_structure_get_string (structure, "format");
1274 if (fstr && strlen (fstr) == 4) {
1275 fourcc = GST_STR_FOURCC (fstr);
1276 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1277 if (wmvversion == 2) {
1278 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1279 } else if (wmvversion == 1) {
1280 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1281 } else if (wmvversion == 3) {
1282 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1285 } else if (!strcmp (mimetype, "image/jpeg")) {
1286 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1292 bih = g_new0 (gst_riff_strf_vids, 1);
1293 GST_WRITE_UINT32_LE (&bih->size, size);
1294 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1295 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1296 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1297 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1298 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1299 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1300 videocontext->pixel_height * 3);
1302 /* process codec private/initialization data, if any */
1304 size += gst_buffer_get_size (codec_buf);
1305 bih = g_realloc (bih, size);
1306 GST_WRITE_UINT32_LE (&bih->size, size);
1307 gst_buffer_extract (codec_buf, 0,
1308 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1311 gst_matroska_mux_set_codec_id (context,
1312 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1313 gst_matroska_mux_free_codec_priv (context);
1314 context->codec_priv = (gpointer) bih;
1315 context->codec_priv_size = size;
1316 context->dts_only = TRUE;
1317 } else if (!strcmp (mimetype, "video/x-h264")) {
1318 gst_matroska_mux_set_codec_id (context,
1319 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1320 gst_matroska_mux_free_codec_priv (context);
1322 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1324 GST_WARNING_OBJECT (mux,
1325 "avc3 is not officially supported, only use this format for smart encoding");
1328 /* Create avcC header */
1329 if (codec_buf != NULL) {
1330 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1331 context->codec_priv = g_malloc0 (context->codec_priv_size);
1332 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1334 } else if (!strcmp (mimetype, "video/x-h265")) {
1335 gst_matroska_mux_set_codec_id (context,
1336 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1337 gst_matroska_mux_free_codec_priv (context);
1339 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1341 GST_WARNING_OBJECT (mux,
1342 "hev1 is not officially supported, only use this format for smart encoding");
1345 /* Create hvcC header */
1346 if (codec_buf != NULL) {
1347 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1348 context->codec_priv = g_malloc0 (context->codec_priv_size);
1349 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1351 } else if (!strcmp (mimetype, "video/x-theora")) {
1352 const GValue *streamheader;
1354 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1356 gst_matroska_mux_free_codec_priv (context);
1358 streamheader = gst_structure_get_value (structure, "streamheader");
1359 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1360 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1361 ("theora stream headers missing or malformed"));
1364 } else if (!strcmp (mimetype, "video/x-dirac")) {
1365 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1366 } else if (!strcmp (mimetype, "video/x-vp8")) {
1367 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1368 } else if (!strcmp (mimetype, "video/x-vp9")) {
1369 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1370 } else if (!strcmp (mimetype, "video/x-av1")) {
1371 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_AV1);
1372 gst_matroska_mux_free_codec_priv (context);
1373 /* Create av1C header */
1374 if (codec_buf != NULL)
1375 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1376 &context->codec_priv, &context->codec_priv_size);
1377 } else if (!strcmp (mimetype, "video/x-ffv")) {
1378 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_FFV1);
1379 gst_matroska_mux_free_codec_priv (context);
1380 if (codec_buf != NULL)
1381 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1382 &context->codec_priv, &context->codec_priv_size);
1383 } else if (!strcmp (mimetype, "video/mpeg")) {
1386 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1387 switch (mpegversion) {
1389 gst_matroska_mux_set_codec_id (context,
1390 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1393 gst_matroska_mux_set_codec_id (context,
1394 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1397 gst_matroska_mux_set_codec_id (context,
1398 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1404 /* global headers may be in codec data */
1405 if (codec_buf != NULL) {
1406 gst_matroska_mux_free_codec_priv (context);
1407 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1408 context->codec_priv = g_malloc0 (context->codec_priv_size);
1409 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1411 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1413 /* can only make it here if preceding case verified it was version 3 */
1414 gst_matroska_mux_set_codec_id (context,
1415 GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1416 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1418 const GValue *mdpr_data;
1420 gst_structure_get_int (structure, "rmversion", &rmversion);
1421 switch (rmversion) {
1423 gst_matroska_mux_set_codec_id (context,
1424 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1427 gst_matroska_mux_set_codec_id (context,
1428 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1431 gst_matroska_mux_set_codec_id (context,
1432 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1435 gst_matroska_mux_set_codec_id (context,
1436 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1442 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1443 if (mdpr_data != NULL) {
1444 guint8 *priv_data = NULL;
1445 guint priv_data_size = 0;
1447 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1449 priv_data_size = gst_buffer_get_size (codec_data_buf);
1450 priv_data = g_malloc0 (priv_data_size);
1452 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1454 gst_matroska_mux_free_codec_priv (context);
1455 context->codec_priv = priv_data;
1456 context->codec_priv_size = priv_data_size;
1458 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1459 const gchar *variant;
1461 gst_matroska_mux_free_codec_priv (context);
1463 variant = gst_structure_get_string (structure, "format");
1464 if (!variant || !g_strcmp0 (variant, "standard"))
1465 context->codec_priv = g_strdup ("apcn");
1466 else if (!g_strcmp0 (variant, "hq"))
1467 context->codec_priv = g_strdup ("apch");
1468 else if (!g_strcmp0 (variant, "lt"))
1469 context->codec_priv = g_strdup ("apcs");
1470 else if (!g_strcmp0 (variant, "proxy"))
1471 context->codec_priv = g_strdup ("apco");
1472 else if (!g_strcmp0 (variant, "4444"))
1473 context->codec_priv = g_strdup ("ap4h");
1475 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1480 context->codec_priv_size = sizeof (guint32);
1481 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1489 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1490 GST_PAD_NAME (pad), caps);
1495 /* N > 0 to expect a particular number of headers, negative if the
1496 number of headers is variable */
1498 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1499 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1501 GstBuffer **buf = NULL;
1504 guint bufi, i, offset, priv_data_size;
1506 if (streamheader == NULL)
1507 goto no_stream_headers;
1509 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1512 bufarr = g_value_peek_pointer (streamheader);
1513 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1515 if (N > 0 && bufarr->len != N)
1518 context->xiph_headers_to_skip = bufarr->len;
1520 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1521 for (i = 0; i < bufarr->len; i++) {
1522 GValue *bufval = &g_array_index (bufarr, GValue, i);
1524 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1526 goto wrong_content_type;
1529 buf[i] = g_value_peek_pointer (bufval);
1533 if (bufarr->len > 0) {
1534 for (i = 0; i < bufarr->len - 1; i++) {
1535 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1539 for (i = 0; i < bufarr->len; ++i) {
1540 priv_data_size += gst_buffer_get_size (buf[i]);
1543 priv_data = g_malloc0 (priv_data_size);
1545 priv_data[0] = bufarr->len - 1;
1548 if (bufarr->len > 0) {
1549 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1550 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1551 priv_data[offset++] = 0xff;
1553 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1557 for (i = 0; i < bufarr->len; ++i) {
1558 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1559 offset += gst_buffer_get_size (buf[i]);
1562 gst_matroska_mux_free_codec_priv (context);
1563 context->codec_priv = priv_data;
1564 context->codec_priv_size = priv_data_size;
1567 *p_buf0 = gst_buffer_ref (buf[0]);
1576 GST_WARNING ("required streamheaders missing in sink caps!");
1581 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1582 G_VALUE_TYPE_NAME (streamheader));
1587 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1592 GST_WARNING ("streamheaders array does not contain GstBuffers");
1598 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1599 GstMatroskaTrackContext * context)
1601 GstBuffer *buf0 = NULL;
1603 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1606 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1607 GST_WARNING ("First vorbis header too small, ignoring");
1609 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1610 GstMatroskaTrackAudioContext *audiocontext;
1614 gst_buffer_map (buf0, &map, GST_MAP_READ);
1615 hdr = map.data + 1 + 6 + 4;
1616 audiocontext = (GstMatroskaTrackAudioContext *) context;
1617 audiocontext->channels = GST_READ_UINT8 (hdr);
1618 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1619 gst_buffer_unmap (buf0, &map);
1624 gst_buffer_unref (buf0);
1630 theora_streamheader_to_codecdata (const GValue * streamheader,
1631 GstMatroskaTrackContext * context)
1633 GstBuffer *buf0 = NULL;
1635 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1638 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1639 GST_WARNING ("First theora header too small, ignoring");
1640 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1641 GST_WARNING ("First header not a theora identification header, ignoring");
1643 GstMatroskaTrackVideoContext *videocontext;
1644 guint fps_num, fps_denom, par_num, par_denom;
1648 gst_buffer_map (buf0, &map, GST_MAP_READ);
1649 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1651 videocontext = (GstMatroskaTrackVideoContext *) context;
1652 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1653 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1654 hdr += 3 + 3 + 1 + 1;
1655 fps_num = GST_READ_UINT32_BE (hdr);
1656 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1657 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1658 fps_denom, fps_num);
1660 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1661 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1662 if (par_num > 0 && par_denom > 0) {
1663 if (par_num > par_denom) {
1664 videocontext->display_width =
1665 videocontext->pixel_width * par_num / par_denom;
1666 videocontext->display_height = videocontext->pixel_height;
1667 } else if (par_num < par_denom) {
1668 videocontext->display_width = videocontext->pixel_width;
1669 videocontext->display_height =
1670 videocontext->pixel_height * par_denom / par_num;
1672 videocontext->display_width = 0;
1673 videocontext->display_height = 0;
1676 videocontext->display_width = 0;
1677 videocontext->display_height = 0;
1680 gst_buffer_unmap (buf0, &map);
1684 gst_buffer_unref (buf0);
1690 kate_streamheader_to_codecdata (const GValue * streamheader,
1691 GstMatroskaTrackContext * context)
1693 GstBuffer *buf0 = NULL;
1695 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1698 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1699 GST_WARNING ("First kate header too small, ignoring");
1700 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1701 GST_WARNING ("First header not a kate identification header, ignoring");
1705 gst_buffer_unref (buf0);
1711 flac_streamheader_to_codecdata (const GValue * streamheader,
1712 GstMatroskaTrackContext * context)
1719 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1720 GST_WARNING ("No or invalid streamheader field in the caps");
1724 bufarr = g_value_peek_pointer (streamheader);
1725 if (bufarr->len < 2) {
1726 GST_WARNING ("Too few headers in streamheader field");
1730 context->xiph_headers_to_skip = bufarr->len + 1;
1732 bufval = &g_array_index (bufarr, GValue, 0);
1733 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1734 GST_WARNING ("streamheaders array does not contain GstBuffers");
1738 buffer = g_value_peek_pointer (bufval);
1740 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1741 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1742 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1743 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1744 GST_WARNING ("Invalid streamheader for FLAC");
1748 gst_matroska_mux_free_codec_priv (context);
1749 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1750 context->codec_priv = g_malloc (context->codec_priv_size);
1751 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1753 for (i = 1; i < bufarr->len; i++) {
1755 bufval = &g_array_index (bufarr, GValue, i);
1757 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1758 gst_matroska_mux_free_codec_priv (context);
1759 GST_WARNING ("streamheaders array does not contain GstBuffers");
1763 buffer = g_value_peek_pointer (bufval);
1765 old_size = context->codec_priv_size;
1766 context->codec_priv_size += gst_buffer_get_size (buffer);
1768 context->codec_priv = g_realloc (context->codec_priv,
1769 context->codec_priv_size);
1770 gst_buffer_extract (buffer, 0,
1771 (guint8 *) context->codec_priv + old_size, -1);
1778 speex_streamheader_to_codecdata (const GValue * streamheader,
1779 GstMatroskaTrackContext * context)
1786 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1787 GST_WARNING ("No or invalid streamheader field in the caps");
1791 bufarr = g_value_peek_pointer (streamheader);
1792 if (bufarr->len != 2) {
1793 GST_WARNING ("Too few headers in streamheader field");
1797 context->xiph_headers_to_skip = bufarr->len + 1;
1799 bufval = &g_array_index (bufarr, GValue, 0);
1800 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1801 GST_WARNING ("streamheaders array does not contain GstBuffers");
1805 buffer = g_value_peek_pointer (bufval);
1807 if (gst_buffer_get_size (buffer) < 80
1808 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1809 GST_WARNING ("Invalid streamheader for Speex");
1813 gst_matroska_mux_free_codec_priv (context);
1814 context->codec_priv_size = gst_buffer_get_size (buffer);
1815 context->codec_priv = g_malloc (context->codec_priv_size);
1816 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1818 bufval = &g_array_index (bufarr, GValue, 1);
1820 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1821 gst_matroska_mux_free_codec_priv (context);
1822 GST_WARNING ("streamheaders array does not contain GstBuffers");
1826 buffer = g_value_peek_pointer (bufval);
1828 old_size = context->codec_priv_size;
1829 context->codec_priv_size += gst_buffer_get_size (buffer);
1830 context->codec_priv = g_realloc (context->codec_priv,
1831 context->codec_priv_size);
1832 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1838 opus_streamheader_to_codecdata (const GValue * streamheader,
1839 GstMatroskaTrackContext * context)
1845 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1848 bufarr = g_value_peek_pointer (streamheader);
1849 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1852 /* Opus headers are not in-band */
1853 context->xiph_headers_to_skip = 0;
1855 bufval = &g_array_index (bufarr, GValue, 0);
1856 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1857 goto wrong_content_type;
1859 buf = g_value_peek_pointer (bufval);
1861 gst_matroska_mux_free_codec_priv (context);
1863 context->codec_priv_size = gst_buffer_get_size (buf);
1864 context->codec_priv = g_malloc0 (context->codec_priv_size);
1865 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1867 context->codec_delay =
1868 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1869 context->codec_delay =
1870 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1871 context->seek_preroll = 80 * GST_MSECOND;
1878 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1879 G_VALUE_TYPE_NAME (streamheader));
1884 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1889 GST_WARNING ("streamheaders array does not contain GstBuffers");
1895 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1899 guint8 channel_mapping_family;
1900 guint8 stream_count, coupled_count, channel_mapping[256];
1904 /* Opus headers are not in-band */
1905 context->xiph_headers_to_skip = 0;
1907 context->codec_delay = 0;
1908 context->seek_preroll = 80 * GST_MSECOND;
1910 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1911 &channel_mapping_family, &stream_count, &coupled_count,
1913 GST_WARNING ("Failed to parse caps for Opus");
1918 gst_codec_utils_opus_create_header (rate, channels,
1919 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1922 GST_WARNING ("Failed to create Opus header from caps");
1926 gst_buffer_map (buffer, &map, GST_MAP_READ);
1927 context->codec_priv_size = map.size;
1928 context->codec_priv = g_malloc (context->codec_priv_size);
1929 memcpy (context->codec_priv, map.data, map.size);
1930 gst_buffer_unmap (buffer, &map);
1931 gst_buffer_unref (buffer);
1937 * gst_matroska_mux_audio_pad_setcaps:
1938 * @pad: Pad which got the caps.
1941 * Setcaps function for audio sink pad.
1943 * Returns: %TRUE on success.
1946 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1948 GstMatroskaTrackContext *context = NULL;
1949 GstMatroskaTrackAudioContext *audiocontext;
1950 GstMatroskaMux *mux;
1951 GstMatroskaPad *collect_pad;
1952 const gchar *mimetype;
1953 gint samplerate = 0, channels = 0;
1954 GstStructure *structure;
1955 const GValue *codec_data = NULL;
1956 GstBuffer *buf = NULL;
1957 const gchar *stream_format = NULL;
1960 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1962 if ((old_caps = gst_pad_get_current_caps (pad))) {
1963 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1964 && !gst_caps_is_equal (caps, old_caps)) {
1965 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1966 ("Caps changes are not supported by Matroska"));
1967 gst_caps_unref (old_caps);
1970 gst_caps_unref (old_caps);
1971 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
1972 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1973 ("Caps on pad %" GST_PTR_FORMAT
1974 " arrived late. Headers were already written", pad));
1979 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1980 g_assert (collect_pad);
1981 context = collect_pad->track;
1983 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1984 audiocontext = (GstMatroskaTrackAudioContext *) context;
1986 structure = gst_caps_get_structure (caps, 0);
1987 mimetype = gst_structure_get_name (structure);
1990 gst_structure_get_int (structure, "rate", &samplerate);
1991 gst_structure_get_int (structure, "channels", &channels);
1993 audiocontext->samplerate = samplerate;
1994 audiocontext->channels = channels;
1995 audiocontext->bitdepth = 0;
1996 context->default_duration = 0;
1998 codec_data = gst_structure_get_value (structure, "codec_data");
2000 buf = gst_value_get_buffer (codec_data);
2002 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
2003 * data and other settings
2007 if (!strcmp (mimetype, "audio/mpeg")) {
2008 gint mpegversion = 0;
2010 gst_structure_get_int (structure, "mpegversion", &mpegversion);
2011 switch (mpegversion) {
2017 gst_structure_get_int (structure, "layer", &layer);
2019 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
2020 GST_WARNING_OBJECT (mux,
2021 "Unable to determine MPEG audio version, assuming 1");
2027 else if (layer == 2)
2029 else if (version == 2)
2034 context->default_duration =
2035 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
2039 gst_matroska_mux_set_codec_id (context,
2040 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
2043 gst_matroska_mux_set_codec_id (context,
2044 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
2047 gst_matroska_mux_set_codec_id (context,
2048 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
2057 stream_format = gst_structure_get_string (structure, "stream-format");
2058 /* check this is raw aac */
2059 if (stream_format) {
2060 if (strcmp (stream_format, "raw") != 0) {
2061 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
2065 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
2070 gst_matroska_mux_set_codec_id (context,
2071 GST_MATROSKA_CODEC_ID_AUDIO_AAC);
2072 context->codec_priv_size = gst_buffer_get_size (buf);
2073 context->codec_priv = g_malloc (context->codec_priv_size);
2074 gst_buffer_extract (buf, 0, context->codec_priv,
2075 context->codec_priv_size);
2077 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
2084 } else if (!strcmp (mimetype, "audio/x-raw")) {
2087 gst_audio_info_init (&info);
2088 if (!gst_audio_info_from_caps (&info, caps)) {
2089 GST_DEBUG_OBJECT (mux,
2090 "broken caps, rejected by gst_audio_info_from_caps");
2094 switch (GST_AUDIO_INFO_FORMAT (&info)) {
2095 case GST_AUDIO_FORMAT_U8:
2096 case GST_AUDIO_FORMAT_S16BE:
2097 case GST_AUDIO_FORMAT_S16LE:
2098 case GST_AUDIO_FORMAT_S24BE:
2099 case GST_AUDIO_FORMAT_S24LE:
2100 case GST_AUDIO_FORMAT_S32BE:
2101 case GST_AUDIO_FORMAT_S32LE:
2102 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
2103 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
2106 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
2107 gst_matroska_mux_set_codec_id (context,
2108 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
2110 gst_matroska_mux_set_codec_id (context,
2111 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
2113 case GST_AUDIO_FORMAT_F32LE:
2114 case GST_AUDIO_FORMAT_F64LE:
2115 gst_matroska_mux_set_codec_id (context,
2116 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
2120 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
2124 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
2125 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
2126 const GValue *streamheader;
2128 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
2130 gst_matroska_mux_free_codec_priv (context);
2132 streamheader = gst_structure_get_value (structure, "streamheader");
2133 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
2134 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2135 ("vorbis stream headers missing or malformed"));
2138 } else if (!strcmp (mimetype, "audio/x-flac")) {
2139 const GValue *streamheader;
2141 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
2143 gst_matroska_mux_free_codec_priv (context);
2145 streamheader = gst_structure_get_value (structure, "streamheader");
2146 if (!flac_streamheader_to_codecdata (streamheader, context)) {
2147 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2148 ("flac stream headers missing or malformed"));
2151 } else if (!strcmp (mimetype, "audio/x-speex")) {
2152 const GValue *streamheader;
2154 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
2155 gst_matroska_mux_free_codec_priv (context);
2157 streamheader = gst_structure_get_value (structure, "streamheader");
2158 if (!speex_streamheader_to_codecdata (streamheader, context)) {
2159 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2160 ("speex stream headers missing or malformed"));
2163 } else if (!strcmp (mimetype, "audio/x-opus")) {
2164 const GValue *streamheader;
2166 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
2168 streamheader = gst_structure_get_value (structure, "streamheader");
2170 gst_matroska_mux_free_codec_priv (context);
2171 if (!opus_streamheader_to_codecdata (streamheader, context)) {
2172 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2173 ("opus stream headers missing or malformed"));
2177 /* no streamheader, but we need to have one, so we make one up
2179 gst_matroska_mux_free_codec_priv (context);
2180 if (!opus_make_codecdata (context, caps)) {
2181 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2182 ("opus stream headers missing or malformed"));
2186 } else if (!strcmp (mimetype, "audio/x-ac3")) {
2187 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
2188 } else if (!strcmp (mimetype, "audio/x-eac3")) {
2189 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
2190 } else if (!strcmp (mimetype, "audio/x-dts")) {
2191 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
2192 } else if (!strcmp (mimetype, "audio/x-tta")) {
2195 /* TTA frame duration */
2196 context->default_duration = 1.04489795918367346939 * GST_SECOND;
2198 gst_structure_get_int (structure, "width", &width);
2199 audiocontext->bitdepth = width;
2200 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
2202 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
2204 const GValue *mdpr_data;
2206 gst_structure_get_int (structure, "raversion", &raversion);
2207 switch (raversion) {
2209 gst_matroska_mux_set_codec_id (context,
2210 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
2213 gst_matroska_mux_set_codec_id (context,
2214 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2217 gst_matroska_mux_set_codec_id (context,
2218 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2224 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2225 if (mdpr_data != NULL) {
2226 guint8 *priv_data = NULL;
2227 guint priv_data_size = 0;
2229 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2231 priv_data_size = gst_buffer_get_size (codec_data_buf);
2232 priv_data = g_malloc0 (priv_data_size);
2234 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2236 gst_matroska_mux_free_codec_priv (context);
2238 context->codec_priv = priv_data;
2239 context->codec_priv_size = priv_data_size;
2242 } else if (!strcmp (mimetype, "audio/x-wma")
2243 || !strcmp (mimetype, "audio/x-alaw")
2244 || !strcmp (mimetype, "audio/x-mulaw")
2245 || !strcmp (mimetype, "audio/x-adpcm")
2246 || !strcmp (mimetype, "audio/G722")) {
2248 guint codec_priv_size;
2250 gint block_align = 0;
2253 if (samplerate == 0 || channels == 0) {
2254 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2258 if (!strcmp (mimetype, "audio/x-wma")) {
2262 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2263 || !gst_structure_get_int (structure, "block_align", &block_align)
2264 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2265 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2270 switch (wmaversion) {
2272 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2275 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2278 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2281 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2285 if (gst_structure_get_int (structure, "depth", &depth))
2286 audiocontext->bitdepth = depth;
2287 } else if (!strcmp (mimetype, "audio/x-alaw")
2288 || !strcmp (mimetype, "audio/x-mulaw")) {
2289 audiocontext->bitdepth = 8;
2290 if (!strcmp (mimetype, "audio/x-alaw"))
2291 format = GST_RIFF_WAVE_FORMAT_ALAW;
2293 format = GST_RIFF_WAVE_FORMAT_MULAW;
2295 block_align = channels;
2296 bitrate = block_align * samplerate;
2297 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2300 layout = gst_structure_get_string (structure, "layout");
2302 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2306 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2307 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2311 if (!strcmp (layout, "dvi")) {
2312 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2313 } else if (!strcmp (layout, "g726")) {
2314 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2315 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2316 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2320 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2324 } else if (!strcmp (mimetype, "audio/G722")) {
2325 format = GST_RIFF_WAVE_FORMAT_ADPCM_G722;
2327 g_assert (format != 0);
2329 codec_priv_size = WAVEFORMATEX_SIZE;
2331 codec_priv_size += gst_buffer_get_size (buf);
2333 /* serialize waveformatex structure */
2334 codec_priv = g_malloc0 (codec_priv_size);
2335 GST_WRITE_UINT16_LE (codec_priv, format);
2336 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2337 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2338 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2339 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2340 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2342 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2344 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2346 /* process codec private/initialization data, if any */
2348 gst_buffer_extract (buf, 0,
2349 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2352 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2353 gst_matroska_mux_free_codec_priv (context);
2354 context->codec_priv = (gpointer) codec_priv;
2355 context->codec_priv_size = codec_priv_size;
2363 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2364 GST_PAD_NAME (pad), caps);
2369 /* we probably don't have the data at start,
2370 * so have to reserve (a maximum) space to write this at the end.
2371 * bit spacy, but some formats can hold quite some */
2372 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2375 * gst_matroska_mux_subtitle_pad_setcaps:
2376 * @pad: Pad which got the caps.
2379 * Setcaps function for subtitle sink pad.
2381 * Returns: %TRUE on success.
2384 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2386 /* There is now (at least) one such alement (kateenc), and I'm going
2387 to handle it here and claim it works when it can be piped back
2388 through GStreamer and VLC */
2390 GstMatroskaTrackContext *context = NULL;
2391 GstMatroskaTrackSubtitleContext *scontext;
2392 GstMatroskaMux *mux;
2393 GstMatroskaPad *collect_pad;
2394 GstCollectData *data;
2395 const gchar *mimetype;
2396 GstStructure *structure;
2397 const GValue *value = NULL;
2398 GstBuffer *buf = NULL;
2399 gboolean ret = TRUE;
2402 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2404 if ((old_caps = gst_pad_get_current_caps (pad))) {
2405 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
2406 && !gst_caps_is_equal (caps, old_caps)) {
2407 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2408 ("Caps changes are not supported by Matroska"));
2409 gst_caps_unref (old_caps);
2412 gst_caps_unref (old_caps);
2413 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
2414 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2415 ("Caps on pad %" GST_PTR_FORMAT
2416 " arrived late. Headers were already written", pad));
2421 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2422 g_assert (collect_pad);
2423 data = (GstCollectData *) (collect_pad);
2425 context = collect_pad->track;
2427 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2428 scontext = (GstMatroskaTrackSubtitleContext *) context;
2430 structure = gst_caps_get_structure (caps, 0);
2431 mimetype = gst_structure_get_name (structure);
2434 scontext->check_utf8 = 1;
2435 scontext->invalid_utf8 = 0;
2436 context->default_duration = 0;
2438 if (!strcmp (mimetype, "subtitle/x-kate")) {
2439 const GValue *streamheader;
2441 gst_matroska_mux_set_codec_id (context,
2442 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2444 gst_matroska_mux_free_codec_priv (context);
2446 streamheader = gst_structure_get_value (structure, "streamheader");
2447 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2448 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2449 ("kate stream headers missing or malformed"));
2453 } else if (!strcmp (mimetype, "text/x-raw")) {
2454 gst_matroska_mux_set_codec_id (context,
2455 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2456 } else if (!strcmp (mimetype, "application/x-ssa")) {
2457 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2458 } else if (!strcmp (mimetype, "application/x-ass")) {
2459 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2460 } else if (!strcmp (mimetype, "application/x-usf")) {
2461 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2462 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2463 gst_matroska_mux_set_codec_id (context,
2464 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2470 /* maybe some private data, e.g. vobsub */
2471 value = gst_structure_get_value (structure, "codec_data");
2473 buf = gst_value_get_buffer (value);
2476 guint8 *priv_data = NULL;
2478 gst_buffer_map (buf, &map, GST_MAP_READ);
2480 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2481 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2482 " exceeded maximum (%d); discarding", pad,
2483 SUBTITLE_MAX_CODEC_PRIVATE);
2484 gst_buffer_unmap (buf, &map);
2488 gst_matroska_mux_free_codec_priv (context);
2490 priv_data = g_malloc0 (map.size);
2491 memcpy (priv_data, map.data, map.size);
2492 context->codec_priv = priv_data;
2493 context->codec_priv_size = map.size;
2494 gst_buffer_unmap (buf, &map);
2497 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2498 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2500 /* This pad is sparse. Now that we have caps on it, we can tell collectpads
2501 * not to actually wait for data when muxing */
2502 GST_COLLECT_PADS_STREAM_LOCK (mux->collect);
2503 GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_LOCKED);
2504 gst_collect_pads_set_waiting (mux->collect, data, FALSE);
2505 GST_COLLECT_PADS_STREAM_UNLOCK (mux->collect);
2514 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2515 GST_PAD_NAME (pad), caps);
2522 * gst_matroska_mux_request_new_pad:
2523 * @element: #GstMatroskaMux.
2524 * @templ: #GstPadTemplate.
2525 * @pad_name: New pad name.
2527 * Request pad function for sink templates.
2529 * Returns: New #GstPad.
2532 gst_matroska_mux_request_new_pad (GstElement * element,
2533 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2535 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2536 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2537 GstMatroskaPad *collect_pad;
2538 GstMatroskamuxPad *newpad;
2540 const gchar *pad_name = NULL;
2541 GstMatroskaCapsFunc capsfunc = NULL;
2542 GstMatroskaTrackContext *context = NULL;
2544 const gchar *id = NULL;
2546 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2547 /* don't mix named and unnamed pads, if the pad already exists we fail when
2548 * trying to add it */
2549 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2550 pad_name = req_name;
2552 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2555 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2556 context = (GstMatroskaTrackContext *)
2557 g_new0 (GstMatroskaTrackAudioContext, 1);
2558 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2559 context->name = g_strdup ("Audio");
2560 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2561 /* don't mix named and unnamed pads, if the pad already exists we fail when
2562 * trying to add it */
2563 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2564 pad_name = req_name;
2566 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2569 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2570 context = (GstMatroskaTrackContext *)
2571 g_new0 (GstMatroskaTrackVideoContext, 1);
2572 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2573 context->name = g_strdup ("Video");
2574 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2575 /* don't mix named and unnamed pads, if the pad already exists we fail when
2576 * trying to add it */
2577 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2578 pad_name = req_name;
2580 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2583 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2584 context = (GstMatroskaTrackContext *)
2585 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2586 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2587 context->name = g_strdup ("Subtitle");
2588 /* setcaps may only provide proper one a lot later */
2589 id = "S_SUB_UNKNOWN";
2591 GST_WARNING_OBJECT (mux, "This is not our template!");
2595 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2596 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2598 gst_matroskamux_pad_init (newpad);
2599 collect_pad = (GstMatroskaPad *)
2600 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2601 sizeof (GstMatroskaPad),
2602 (GstCollectDataDestroyNotify) gst_matroska_pad_free, TRUE);
2604 collect_pad->mux = mux;
2605 collect_pad->track = context;
2606 gst_matroska_pad_reset (collect_pad, FALSE);
2608 gst_matroska_mux_set_codec_id (collect_pad->track, id);
2609 collect_pad->track->dts_only = FALSE;
2611 collect_pad->capsfunc = capsfunc;
2612 gst_pad_set_active (GST_PAD (newpad), TRUE);
2613 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2614 goto pad_add_failed;
2620 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2622 return GST_PAD (newpad);
2627 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2629 gst_object_unref (newpad);
2635 * gst_matroska_mux_release_pad:
2636 * @element: #GstMatroskaMux.
2637 * @pad: Pad to release.
2639 * Release a previously requested pad.
2642 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2644 GstMatroskaMux *mux;
2647 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2649 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2650 GstCollectData *cdata = (GstCollectData *) walk->data;
2651 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2653 if (cdata->pad == pad) {
2655 * observed duration, this will remain GST_CLOCK_TIME_NONE
2656 * only if the pad is reset
2658 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2660 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2661 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2662 collected_duration =
2663 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2666 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2667 && mux->duration < collected_duration)
2668 mux->duration = collected_duration;
2674 gst_collect_pads_remove_pad (mux->collect, pad);
2675 if (gst_element_remove_pad (element, pad))
2680 gst_matroska_mux_write_mastering_metadata (GstMatroskaMux * mux,
2681 GstMatroskaTrackVideoContext * videocontext)
2683 GstEbmlWrite *ebml = mux->ebml_write;
2685 GstVideoMasteringDisplayInfo *minfo = &videocontext->mastering_display_info;
2687 const gdouble chroma_scale = 50000;
2688 const gdouble luma_scale = 50000;
2690 if (!videocontext->mastering_display_info_present)
2694 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_MASTERINGMETADATA);
2696 value = (gdouble) minfo->display_primaries[0].x / chroma_scale;
2697 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYX, value);
2699 value = (gdouble) minfo->display_primaries[0].y / chroma_scale;
2700 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYY, value);
2702 value = (gdouble) minfo->display_primaries[1].x / chroma_scale;
2703 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYX, value);
2705 value = (gdouble) minfo->display_primaries[1].y / chroma_scale;
2706 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYY, value);
2708 value = (gdouble) minfo->display_primaries[2].x / chroma_scale;
2709 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYX, value);
2711 value = (gdouble) minfo->display_primaries[2].y / chroma_scale;
2712 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYY, value);
2714 value = (gdouble) minfo->white_point.x / chroma_scale;
2715 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYX, value);
2717 value = (gdouble) minfo->white_point.y / chroma_scale;
2718 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYY, value);
2720 value = (gdouble) minfo->max_display_mastering_luminance / luma_scale;
2721 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMAX, value);
2723 value = (gdouble) minfo->min_display_mastering_luminance / luma_scale;
2724 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMIN, value);
2726 gst_ebml_write_master_finish (ebml, master);
2731 gst_matroska_mux_write_colour (GstMatroskaMux * mux,
2732 GstMatroskaTrackVideoContext * videocontext)
2734 GstEbmlWrite *ebml = mux->ebml_write;
2736 guint matrix_id = 0;
2738 guint transfer_id = 0;
2739 guint primaries_id = 0;
2741 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_VIDEOCOLOUR);
2743 switch (videocontext->colorimetry.range) {
2744 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
2747 case GST_VIDEO_COLOR_RANGE_16_235:
2750 case GST_VIDEO_COLOR_RANGE_0_255:
2754 matrix_id = gst_video_color_matrix_to_iso (videocontext->colorimetry.matrix);
2756 gst_video_transfer_function_to_iso (videocontext->colorimetry.transfer);
2758 gst_video_color_primaries_to_iso (videocontext->colorimetry.primaries);
2760 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEORANGE, range_id);
2761 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS,
2763 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS,
2765 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPRIMARIES, primaries_id);
2766 if (videocontext->content_light_level.max_content_light_level &&
2767 videocontext->content_light_level.max_frame_average_light_level) {
2768 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXCLL,
2769 videocontext->content_light_level.max_content_light_level);
2770 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXFALL,
2771 videocontext->content_light_level.max_frame_average_light_level);
2774 gst_matroska_mux_write_mastering_metadata (mux, videocontext);
2775 gst_ebml_write_master_finish (ebml, master);
2779 * gst_matroska_mux_track_header:
2780 * @mux: #GstMatroskaMux
2781 * @context: Tack context.
2783 * Write a track header.
2786 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2787 GstMatroskaTrackContext * context)
2789 GstEbmlWrite *ebml = mux->ebml_write;
2792 /* TODO: check if everything necessary is written and check default values */
2794 /* track type goes before the type-specific stuff */
2795 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2796 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2798 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2799 if (context->default_duration) {
2800 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2801 context->default_duration);
2803 if (context->language) {
2804 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2808 /* FIXME: until we have a nice way of getting the codecname
2809 * out of the caps, I'm not going to enable this. Too much
2810 * (useless, double, boring) work... */
2811 /* TODO: Use value from tags if any */
2812 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2813 context->codec_name); */
2814 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2816 /* type-specific stuff */
2817 switch (context->type) {
2818 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2819 GstMatroskaTrackVideoContext *videocontext =
2820 (GstMatroskaTrackVideoContext *) context;
2822 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2823 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2824 videocontext->pixel_width);
2825 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2826 videocontext->pixel_height);
2827 if (videocontext->display_width && videocontext->display_height) {
2828 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2829 videocontext->display_width);
2830 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2831 videocontext->display_height);
2833 switch (videocontext->interlace_mode) {
2834 case GST_MATROSKA_INTERLACE_MODE_INTERLACED:
2835 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2837 case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE:
2838 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 2);
2844 if (videocontext->fourcc) {
2845 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2847 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2848 (gpointer) & fcc_le, 4);
2850 gst_matroska_mux_write_colour (mux, videocontext);
2851 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2852 guint64 stereo_mode = 0;
2854 switch (videocontext->multiview_mode) {
2855 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2857 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2858 if (videocontext->multiview_flags &
2859 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2860 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2862 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2864 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2865 if (videocontext->multiview_flags &
2866 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2867 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2869 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2871 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2872 if (videocontext->multiview_flags &
2873 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2874 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2876 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2878 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2879 if (videocontext->multiview_flags &
2880 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2881 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2883 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2884 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2885 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2886 GST_FIXME_OBJECT (mux,
2887 "Frame-by-frame stereoscopic mode not fully implemented");
2890 GST_WARNING_OBJECT (mux,
2891 "Multiview mode %d not supported in Matroska/WebM",
2892 videocontext->multiview_mode);
2896 if (stereo_mode != 0)
2897 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2900 gst_ebml_write_master_finish (ebml, master);
2905 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2906 GstMatroskaTrackAudioContext *audiocontext =
2907 (GstMatroskaTrackAudioContext *) context;
2909 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2910 if (audiocontext->samplerate != 8000)
2911 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2912 audiocontext->samplerate);
2913 if (audiocontext->channels != 1)
2914 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2915 audiocontext->channels);
2916 if (audiocontext->bitdepth) {
2917 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2918 audiocontext->bitdepth);
2921 gst_ebml_write_master_finish (ebml, master);
2926 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2930 /* doesn't need type-specific data */
2934 GST_DEBUG_OBJECT (mux, "Wrote track header. Codec %s", context->codec_id);
2936 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2937 if (context->codec_priv)
2938 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2939 context->codec_priv, context->codec_priv_size);
2941 if (context->seek_preroll) {
2942 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2943 context->seek_preroll);
2946 if (context->codec_delay) {
2947 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2948 context->codec_delay);
2953 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2955 guint64 title_master;
2958 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2960 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2961 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2962 GST_MATROSKA_MUX_CHAPLANG);
2964 gst_ebml_write_master_finish (ebml, title_master);
2967 static GstTocEntry *
2968 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2969 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2970 guint64 * master_edition)
2972 guint64 master_chapteratom;
2979 GstTocEntry *internal_chapter, *internal_nested;
2982 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2984 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2986 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2987 /* create uid for the parent */
2989 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2991 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID,
2992 g_ascii_strtoull (gst_toc_entry_get_uid (edition), NULL, 10));
2993 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2994 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2995 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2998 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2999 tags = gst_toc_entry_get_tags (entry);
3001 tags = gst_tag_list_copy (tags);
3004 /* build internal chapter */
3005 uid = gst_matroska_mux_create_uid (mux);
3006 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
3007 internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
3009 /* Write the chapter entry */
3010 master_chapteratom =
3011 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
3013 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
3014 /* Store the user provided UID in the ChapterStringUID */
3015 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPTERSTRINGUID,
3016 gst_toc_entry_get_uid (entry));
3017 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
3018 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
3019 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
3020 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
3022 /* write current ChapterDisplays before the nested chapters */
3023 if (G_LIKELY (tags != NULL)) {
3024 count = gst_tag_list_get_tag_size (tags, GST_TAG_TITLE);
3026 for (i = 0; i < count; ++i) {
3027 gst_tag_list_get_string_index (tags, GST_TAG_TITLE, i, &title);
3028 /* FIXME: handle ChapterLanguage entries */
3029 gst_matroska_mux_write_chapter_title (title, ebml);
3033 /* remove title tag */
3034 if (G_LIKELY (count > 0))
3035 gst_tag_list_remove_tag (tags, GST_TAG_TITLE);
3037 gst_toc_entry_set_tags (internal_chapter, tags);
3040 /* Write nested chapters */
3041 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3043 internal_nested = gst_matroska_mux_write_chapter (mux, NULL, cur->data,
3046 gst_toc_entry_append_sub_entry (internal_chapter, internal_nested);
3049 gst_ebml_write_master_finish (ebml, master_chapteratom);
3051 return internal_chapter;
3054 static GstTocEntry *
3055 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
3056 GstTocEntry * edition, GList * chapters, GstEbmlWrite * ebml,
3057 guint64 * master_chapters)
3059 guint64 master_edition = 0;
3062 GstTocEntry *internal_edition, *internal_chapter;
3063 GstTagList *tags = NULL;
3065 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT,
3066 gst_matroska_mux_create_uid (mux));
3068 if (edition != NULL) {
3069 /* Edition entry defined, get its tags */
3070 tags = gst_toc_entry_get_tags (edition);
3072 tags = gst_tag_list_copy (tags);
3076 internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
3078 gst_toc_entry_set_tags (internal_edition, tags);
3081 for (cur = g_list_first (chapters); cur != NULL; cur = cur->next) {
3082 internal_chapter = gst_matroska_mux_write_chapter (mux, internal_edition,
3083 cur->data, ebml, master_chapters, &master_edition);
3085 gst_toc_entry_append_sub_entry (internal_edition, internal_chapter);
3088 if (G_LIKELY (master_edition != 0))
3089 gst_ebml_write_master_finish (ebml, master_edition);
3091 return internal_edition;
3095 * gst_matroska_mux_start:
3096 * @mux: #GstMatroskaMux
3098 * Start a new matroska file (write headers etc...)
3101 gst_matroska_mux_start (GstMatroskaMux * mux, GstMatroskaPad * first_pad,
3102 GstBuffer * first_pad_buf)
3104 GstEbmlWrite *ebml = mux->ebml_write;
3105 const gchar *doctype;
3106 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
3107 GST_MATROSKA_ID_TRACKS,
3108 GST_MATROSKA_ID_CHAPTERS,
3109 GST_MATROSKA_ID_CUES,
3110 GST_MATROSKA_ID_TAGS,
3113 const gchar *media_type;
3114 gboolean audio_only;
3115 guint64 master, child;
3119 GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
3120 GstClockTime duration = 0;
3121 guint32 segment_uid[4];
3126 /* if not streaming, check if downstream is seekable */
3127 if (!mux->ebml_write->streamable) {
3131 query = gst_query_new_seeking (GST_FORMAT_BYTES);
3132 if (gst_pad_peer_query (mux->srcpad, query)) {
3133 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
3134 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
3136 /* assume seeking is not supported if query not handled downstream */
3137 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
3141 mux->ebml_write->streamable = TRUE;
3142 g_object_notify (G_OBJECT (mux), "streamable");
3143 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
3144 "streamable=false. Will ignore that and create streamable output "
3147 gst_query_unref (query);
3150 /* stream-start (FIXME: create id based on input ids) */
3151 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
3152 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
3155 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
3157 media_type = (audio_only) ? "audio/webm" : "video/webm";
3159 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
3161 ebml->caps = gst_caps_new_empty_simple (media_type);
3162 gst_pad_set_caps (mux->srcpad, ebml->caps);
3163 /* we start with a EBML header */
3164 doctype = mux->doctype;
3165 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
3166 doctype, mux->doctype_version);
3167 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
3169 /* the rest of the header is cached */
3170 gst_ebml_write_set_cache (ebml, 0x1000);
3172 /* start a segment */
3174 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
3175 mux->segment_master = ebml->pos;
3177 if (!mux->ebml_write->streamable) {
3178 /* seekhead (table of contents) - we set the positions later */
3179 mux->seekhead_pos = ebml->pos;
3180 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
3181 for (i = 0; seekhead_id[i] != 0; i++) {
3182 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
3183 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
3184 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
3185 gst_ebml_write_master_finish (ebml, child);
3187 gst_ebml_write_master_finish (ebml, master);
3190 if (mux->ebml_write->streamable) {
3191 const GstTagList *tags;
3192 gboolean has_main_tags;
3195 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3196 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3198 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
3199 guint64 master_tags, master_tag;
3201 GST_DEBUG_OBJECT (mux, "Writing tags");
3203 mux->tags_pos = ebml->pos;
3204 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3205 if (has_main_tags) {
3206 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3207 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3208 gst_ebml_write_master_finish (ebml, master_tag);
3210 gst_matroska_mux_write_streams_tags (mux);
3211 gst_ebml_write_master_finish (ebml, master_tags);
3216 mux->info_pos = ebml->pos;
3217 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
3219 /* WebM does not support SegmentUID field on SegmentInfo */
3220 if (!mux->is_webm) {
3221 for (i = 0; i < 4; i++) {
3222 segment_uid[i] = g_random_int ();
3224 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
3225 (guint8 *) segment_uid, 16);
3228 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
3229 mux->duration_pos = ebml->pos;
3231 if (!mux->ebml_write->streamable) {
3232 for (collected = mux->collect->data; collected;
3233 collected = g_slist_next (collected)) {
3234 GstMatroskaPad *collect_pad;
3236 gint64 trackduration;
3238 collect_pad = (GstMatroskaPad *) collected->data;
3239 thepad = collect_pad->collect.pad;
3241 /* Query the total length of the track. */
3242 GST_DEBUG_OBJECT (thepad, "querying peer duration");
3243 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
3244 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
3245 GST_TIME_ARGS (trackduration));
3246 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
3247 duration = (GstClockTime) trackduration;
3251 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3252 gst_guint64_to_gdouble (duration) /
3253 gst_guint64_to_gdouble (mux->time_scale));
3255 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
3256 "GStreamer matroskamux version " PACKAGE_VERSION);
3257 if (mux->writing_app && mux->writing_app[0]) {
3258 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
3260 if (mux->creation_time != NULL) {
3261 time = g_date_time_to_unix (mux->creation_time) * GST_SECOND;
3262 time += g_date_time_get_microsecond (mux->creation_time) * GST_USECOND;
3264 time = g_get_real_time () * GST_USECOND;
3266 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time);
3267 gst_ebml_write_master_finish (ebml, master);
3270 mux->tracks_pos = ebml->pos;
3271 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
3273 for (collected = mux->collect->data; collected;
3274 collected = g_slist_next (collected)) {
3275 GstMatroskaPad *collect_pad;
3278 collect_pad = (GstMatroskaPad *) collected->data;
3280 /* This will cause an error at a later time */
3281 if (collect_pad->track->codec_id == NULL)
3284 /* Find the smallest timestamp so we can offset all streams by this to
3286 if (mux->offset_to_zero) {
3289 if (collect_pad == first_pad)
3290 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3292 buf = gst_collect_pads_peek (mux->collect, collected->data);
3295 ts = gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3297 if (earliest_time == GST_CLOCK_TIME_NONE)
3299 else if (ts != GST_CLOCK_TIME_NONE && ts < earliest_time)
3304 gst_buffer_unref (buf);
3307 /* For audio tracks, use the first buffers duration as the default
3308 * duration if we didn't get any better idea from the caps event already
3310 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO &&
3311 collect_pad->track->default_duration == 0) {
3312 if (collect_pad == first_pad)
3313 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3315 buf = gst_collect_pads_peek (mux->collect, collected->data);
3317 if (buf && GST_BUFFER_DURATION_IS_VALID (buf))
3318 collect_pad->track->default_duration =
3319 GST_BUFFER_DURATION (buf) + collect_pad->track->codec_delay;
3321 gst_buffer_unref (buf);
3324 collect_pad->track->num = tracknum++;
3325 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
3326 gst_matroska_mux_track_header (mux, collect_pad->track);
3327 gst_ebml_write_master_finish (ebml, child);
3328 /* some remaining pad/track setup */
3329 collect_pad->default_duration_scaled =
3330 gst_util_uint64_scale (collect_pad->track->default_duration,
3331 1, mux->time_scale);
3333 gst_ebml_write_master_finish (ebml, master);
3335 mux->earliest_time = earliest_time == GST_CLOCK_TIME_NONE ? 0 : earliest_time;
3338 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3339 if (toc != NULL && !mux->ebml_write->streamable) {
3340 guint64 master_chapters = 0;
3341 GstTocEntry *internal_edition;
3342 GList *cur, *chapters;
3344 GST_DEBUG ("Writing chapters");
3346 /* There are two UIDs for Chapters:
3347 * - The ChapterUID is a mandatory unsigned integer which internally
3348 * refers to a given chapter. Except for the title & language which use
3349 * dedicated fields, this UID can also be used to add tags to the Chapter.
3350 * The tags come in a separate section of the container.
3351 * - The ChapterStringUID is an optional UTF-8 string which also uniquely
3352 * refers to a chapter but from an external perspective. It can act as a
3353 * "WebVTT cue identifier" which "can be used to reference a specific cue,
3354 * for example from script or CSS".
3356 * The ChapterUID will be generated and checked for unicity, while the
3357 * ChapterStringUID will receive the user defined UID.
3359 * In order to be able to refer to chapters from the tags section,
3360 * we must maintain an internal Toc tree with the generated ChapterUID
3361 * (see gst_matroska_mux_write_toc_entry_tags) */
3363 /* Check whether we have editions or chapters at the root level. */
3364 cur = gst_toc_get_entries (toc);
3366 mux->chapters_pos = ebml->pos;
3368 mux->internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
3370 if (gst_toc_entry_get_entry_type (cur->data) ==
3371 GST_TOC_ENTRY_TYPE_EDITION) {
3372 /* Editions at the root level */
3373 for (; cur != NULL; cur = cur->next) {
3374 chapters = gst_toc_entry_get_sub_entries (cur->data);
3375 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3376 cur->data, chapters, ebml, &master_chapters);
3377 gst_toc_append_entry (mux->internal_toc, internal_edition);
3380 /* Chapters at the root level */
3381 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3382 NULL, cur, ebml, &master_chapters);
3383 gst_toc_append_entry (mux->internal_toc, internal_edition);
3386 /* close master element if any edition was written */
3387 if (G_LIKELY (master_chapters != 0))
3388 gst_ebml_write_master_finish (ebml, master_chapters);
3392 /* lastly, flush the cache */
3393 gst_ebml_write_flush_cache (ebml, FALSE, 0);
3396 gst_toc_unref (toc);
3399 /* TODO: more sensible tag mappings */
3402 const gchar *matroska_tagname;
3403 const gchar *gstreamer_tagname;
3405 gst_matroska_tag_conv[] = {
3407 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
3408 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
3409 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
3410 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
3411 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
3412 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
3413 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
3414 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
3415 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
3416 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
3417 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
3418 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
3419 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
3420 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
3421 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
3424 /* Every stagefright implementation on android up to and including 6.0.1 is using
3425 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
3426 so before outputting tags and tag elements we better make sure that there are
3427 actually tags we are going to write */
3429 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3432 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3433 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3435 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3436 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3437 if (strcmp (tagname_gst, tag) == 0) {
3438 GValue src = { 0, };
3441 if (!gst_tag_list_copy_value (&src, list, tag))
3443 dest = gst_value_serialize (&src);
3445 g_value_unset (&src);
3457 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3460 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3462 guint64 simpletag_master;
3464 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3465 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3466 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3468 if (strcmp (tagname_gst, tag) == 0) {
3469 GValue src = { 0, };
3472 if (!gst_tag_list_copy_value (&src, list, tag))
3474 if ((dest = gst_value_serialize (&src))) {
3476 simpletag_master = gst_ebml_write_master_start (ebml,
3477 GST_MATROSKA_ID_SIMPLETAG);
3478 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3479 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3480 gst_ebml_write_master_finish (ebml, simpletag_master);
3483 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3485 g_value_unset (&src);
3492 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3494 guint64 master_tag, master_targets;
3497 ebml = mux->ebml_write;
3499 if (G_UNLIKELY (mpad->tags == NULL
3500 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3503 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3504 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3506 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3508 gst_ebml_write_master_finish (ebml, master_targets);
3509 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3510 gst_ebml_write_master_finish (ebml, master_tag);
3514 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3518 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3519 GstMatroskaPad *collect_pad;
3521 collect_pad = (GstMatroskaPad *) walk->data;
3523 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3528 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3532 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3533 GstMatroskaPad *collect_pad;
3535 collect_pad = (GstMatroskaPad *) walk->data;
3536 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3543 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3544 const GstTocEntry * entry, guint64 * master_tags, gboolean * has_tags)
3546 guint64 master_tag, master_targets;
3549 const GstTagList *tags;
3551 ebml = mux->ebml_write;
3553 tags = gst_toc_entry_get_tags (entry);
3554 if (G_UNLIKELY (tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags))) {
3557 if (*master_tags == 0) {
3558 mux->tags_pos = ebml->pos;
3559 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3562 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3564 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3566 if (gst_toc_entry_get_entry_type (entry) == GST_TOC_ENTRY_TYPE_EDITION)
3567 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3568 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3570 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3571 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3573 gst_ebml_write_master_finish (ebml, master_targets);
3574 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3575 gst_ebml_write_master_finish (ebml, master_tag);
3578 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3580 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags,
3586 * gst_matroska_mux_finish:
3587 * @mux: #GstMatroskaMux
3589 * Finish a new matroska file (write index etc...)
3592 gst_matroska_mux_finish (GstMatroskaMux * mux)
3594 GstEbmlWrite *ebml = mux->ebml_write;
3596 guint64 duration = 0;
3598 const GstTagList *tags, *toc_tags;
3600 gboolean has_main_tags, toc_has_tags = FALSE;
3603 /* finish last cluster */
3605 gst_ebml_write_master_finish (ebml, mux->cluster);
3609 if (mux->index != NULL) {
3611 guint64 master, pointentry_master, trackpos_master;
3613 mux->cues_pos = ebml->pos;
3614 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3615 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3617 for (n = 0; n < mux->num_indexes; n++) {
3618 GstMatroskaIndex *idx = &mux->index[n];
3620 pointentry_master = gst_ebml_write_master_start (ebml,
3621 GST_MATROSKA_ID_POINTENTRY);
3622 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3623 idx->time / mux->time_scale);
3624 trackpos_master = gst_ebml_write_master_start (ebml,
3625 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3626 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3627 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3628 idx->pos - mux->segment_master);
3629 gst_ebml_write_master_finish (ebml, trackpos_master);
3630 gst_ebml_write_master_finish (ebml, pointentry_master);
3633 gst_ebml_write_master_finish (ebml, master);
3634 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3638 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3639 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3640 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3642 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux) || toc != NULL) {
3643 guint64 master_tags = 0, master_tag;
3645 GST_DEBUG_OBJECT (mux, "Writing tags");
3647 if (has_main_tags) {
3648 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3649 mux->tags_pos = ebml->pos;
3650 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3651 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3654 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3655 if (mux->internal_toc != NULL) {
3656 toc_tags = gst_toc_get_tags (mux->internal_toc);
3657 toc_has_tags = (toc_tags != NULL);
3658 gst_tag_list_foreach (toc_tags, gst_matroska_mux_write_simple_tag,
3662 gst_ebml_write_master_finish (ebml, master_tag);
3665 if (mux->internal_toc != NULL) {
3666 for (cur = gst_toc_get_entries (mux->internal_toc); cur != NULL;
3668 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags,
3673 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3674 mux->tags_pos = ebml->pos;
3675 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3677 gst_matroska_mux_write_streams_tags (mux);
3679 if (master_tags != 0)
3680 gst_ebml_write_master_finish (ebml, master_tags);
3683 /* update seekhead. We know that:
3684 * - a seekhead contains 5 entries.
3685 * - order of entries is as above.
3686 * - a seekhead has a 4-byte header + 8-byte length
3687 * - each entry is 2-byte master, 2-byte ID pointer,
3688 * 2-byte length pointer, all 8/1-byte length, 4-
3689 * byte ID and 8-byte length pointer, where the
3690 * length pointer starts at 20.
3691 * - all entries are local to the segment (so pos - segment_master).
3692 * - so each entry is at 12 + 20 + num * 28. */
3693 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3694 mux->info_pos - mux->segment_master);
3695 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3696 mux->tracks_pos - mux->segment_master);
3697 if (toc != NULL && mux->chapters_pos > 0) {
3698 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3699 mux->chapters_pos - mux->segment_master);
3702 guint64 my_pos = ebml->pos;
3704 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3705 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3706 gst_ebml_write_seek (ebml, my_pos);
3708 if (mux->index != NULL) {
3709 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3710 mux->cues_pos - mux->segment_master);
3713 guint64 my_pos = ebml->pos;
3715 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3716 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3717 gst_ebml_write_seek (ebml, my_pos);
3720 if (mux->tags_pos != 0 || toc_has_tags) {
3721 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3722 mux->tags_pos - mux->segment_master);
3725 guint64 my_pos = ebml->pos;
3727 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3728 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3729 gst_ebml_write_seek (ebml, my_pos);
3733 gst_toc_unref (toc);
3737 * - first get the overall duration
3738 * (a released track may have left a duration in here)
3739 * - write some track header data for subtitles
3741 duration = mux->duration;
3743 for (collected = mux->collect->data; collected;
3744 collected = g_slist_next (collected)) {
3745 GstMatroskaPad *collect_pad;
3747 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3748 * since this means buffer without timestamps that is not possible
3750 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3752 collect_pad = (GstMatroskaPad *) collected->data;
3754 GST_DEBUG_OBJECT (mux,
3755 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3756 " end ts %" GST_TIME_FORMAT, collect_pad,
3757 GST_TIME_ARGS (collect_pad->start_ts),
3758 GST_TIME_ARGS (collect_pad->end_ts));
3760 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3761 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3762 collected_duration =
3763 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3764 GST_DEBUG_OBJECT (collect_pad->collect.pad,
3765 "final track duration: %" GST_TIME_FORMAT,
3766 GST_TIME_ARGS (collected_duration));
3768 GST_WARNING_OBJECT (collect_pad->collect.pad,
3769 "unable to get final track duration");
3771 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3772 duration < collected_duration)
3773 duration = collected_duration;
3777 /* seek back (optional, but do anyway) */
3778 gst_ebml_write_seek (ebml, pos);
3780 /* update duration */
3781 if (duration != 0) {
3782 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3783 GST_TIME_ARGS (duration));
3784 pos = mux->ebml_write->pos;
3785 gst_ebml_write_seek (ebml, mux->duration_pos);
3786 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3787 gst_guint64_to_gdouble (duration) /
3788 gst_guint64_to_gdouble (mux->time_scale));
3789 gst_ebml_write_seek (ebml, pos);
3792 guint64 my_pos = ebml->pos;
3794 gst_ebml_write_seek (ebml, mux->duration_pos);
3795 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3796 gst_ebml_write_seek (ebml, my_pos);
3798 GST_DEBUG_OBJECT (mux, "finishing segment");
3799 /* finish segment - this also writes element length */
3800 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3804 * gst_matroska_mux_buffer_header:
3805 * @track: Track context.
3806 * @relative_timestamp: relative timestamp of the buffer
3807 * @flags: Buffer flags.
3809 * Create a buffer containing buffer header.
3811 * Returns: New buffer.
3814 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3815 gint16 relative_timestamp, int flags)
3818 guint8 *data = g_malloc (4);
3820 hdr = gst_buffer_new_wrapped (data, 4);
3821 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3822 data[0] = track->num | 0x80;
3823 /* time relative to clustertime */
3824 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3832 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3833 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3834 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3837 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3838 GstMatroskaPad * collect_pad, GstBuffer * buf)
3840 GstMatroskaTrackVideoContext *ctx =
3841 (GstMatroskaTrackVideoContext *) collect_pad->track;
3846 guint32 next_parse_offset;
3847 GstBuffer *ret = NULL;
3848 gboolean is_muxing_unit = FALSE;
3850 gst_buffer_map (buf, &map, GST_MAP_READ);
3855 gst_buffer_unmap (buf, &map);
3856 gst_buffer_unref (buf);
3860 /* Check if this buffer contains a picture or end-of-sequence packet */
3861 while (size >= 13) {
3862 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3863 gst_buffer_unmap (buf, &map);
3864 gst_buffer_unref (buf);
3868 parse_code = GST_READ_UINT8 (data + 4);
3869 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3870 if (ctx->dirac_unit) {
3871 gst_buffer_unref (ctx->dirac_unit);
3872 ctx->dirac_unit = NULL;
3874 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3875 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3876 is_muxing_unit = TRUE;
3880 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3882 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3885 data += next_parse_offset;
3886 size -= next_parse_offset;
3889 if (ctx->dirac_unit)
3890 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3892 ctx->dirac_unit = gst_buffer_ref (buf);
3894 gst_buffer_unmap (buf, &map);
3896 if (is_muxing_unit) {
3897 ret = gst_buffer_make_writable (ctx->dirac_unit);
3898 ctx->dirac_unit = NULL;
3899 gst_buffer_copy_into (ret, buf,
3900 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3901 gst_buffer_unref (buf);
3903 gst_buffer_unref (buf);
3911 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3915 GValue streamheader = { 0 };
3916 GValue bufval = { 0 };
3917 GstBuffer *streamheader_buffer;
3918 GstEbmlWrite *ebml = mux->ebml_write;
3920 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3921 caps = gst_caps_copy (mux->ebml_write->caps);
3922 s = gst_caps_get_structure (caps, 0);
3923 g_value_init (&streamheader, GST_TYPE_ARRAY);
3924 g_value_init (&bufval, GST_TYPE_BUFFER);
3925 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3926 gst_value_set_buffer (&bufval, streamheader_buffer);
3927 gst_value_array_append_value (&streamheader, &bufval);
3928 g_value_unset (&bufval);
3929 gst_structure_set_value (s, "streamheader", &streamheader);
3930 g_value_unset (&streamheader);
3931 gst_caps_replace (&ebml->caps, caps);
3932 gst_buffer_unref (streamheader_buffer);
3933 gst_pad_set_caps (mux->srcpad, caps);
3934 gst_caps_unref (caps);
3938 * gst_matroska_mux_write_data:
3939 * @mux: #GstMatroskaMux
3940 * @collect_pad: #GstMatroskaPad with the data
3942 * Write collected data (called from gst_matroska_mux_collected).
3944 * Returns: Result of the gst_pad_push issued to write the data.
3946 static GstFlowReturn
3947 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3950 GstEbmlWrite *ebml = mux->ebml_write;
3953 gboolean write_duration;
3954 guint64 cluster_time_scaled;
3955 gint16 relative_timestamp;
3956 gint64 relative_timestamp64;
3957 guint64 block_duration, duration_diff = 0;
3958 gboolean is_video_keyframe = FALSE;
3959 gboolean is_video_invisible = FALSE;
3960 gboolean is_audio_only = FALSE;
3961 gboolean is_min_duration_reached = FALSE;
3962 gboolean is_max_duration_exceeded = FALSE;
3963 GstMatroskamuxPad *pad;
3965 GstClockTime buffer_timestamp;
3966 GstAudioClippingMeta *cmeta = NULL;
3969 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3971 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3972 if (collect_pad->track->xiph_headers_to_skip > 0) {
3973 --collect_pad->track->xiph_headers_to_skip;
3974 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_HEADER)) {
3975 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3976 gst_buffer_unref (buf);
3981 /* for dirac we have to queue up everything up to a picture unit */
3982 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) {
3983 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3986 } else if (!strcmp (collect_pad->track->codec_id,
3987 GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
3988 /* Remove the 'Frame container atom' header' */
3989 buf = gst_buffer_make_writable (buf);
3990 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
3994 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3995 if (buffer_timestamp >= mux->earliest_time) {
3996 buffer_timestamp -= mux->earliest_time;
3998 buffer_timestamp = 0;
4001 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
4002 * this would wreak havoc with time stored in matroska file */
4003 /* TODO: maybe calculate a timestamp by using the previous timestamp
4004 * and default duration */
4005 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4006 GST_WARNING_OBJECT (collect_pad->collect.pad,
4007 "Invalid buffer timestamp; dropping buffer");
4008 gst_buffer_unref (buf);
4012 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4013 && collect_pad->track->codec_delay) {
4014 /* All timestamps should include the codec delay */
4015 if (buffer_timestamp > collect_pad->track->codec_delay) {
4016 buffer_timestamp += collect_pad->track->codec_delay;
4018 buffer_timestamp = 0;
4019 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
4023 /* set the timestamp for outgoing buffers */
4024 ebml->timestamp = buffer_timestamp;
4026 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
4027 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
4028 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
4029 GST_TIME_ARGS (buffer_timestamp));
4030 is_video_keyframe = TRUE;
4031 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
4032 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
4033 || !strcmp (collect_pad->track->codec_id,
4034 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
4035 GST_LOG_OBJECT (mux,
4036 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
4037 GST_TIME_ARGS (buffer_timestamp));
4038 is_video_invisible = TRUE;
4042 /* From this point on we use the buffer_timestamp to do cluster and other
4043 * related arithmetic, so apply the timestamp offset if we have one */
4044 buffer_timestamp += mux->cluster_timestamp_offset;
4046 is_audio_only = (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
4047 (mux->num_streams == 1);
4048 is_min_duration_reached = (mux->min_cluster_duration == 0
4049 || (buffer_timestamp > mux->cluster_time
4050 && (buffer_timestamp - mux->cluster_time) >=
4051 mux->min_cluster_duration));
4052 is_max_duration_exceeded = (mux->max_cluster_duration > 0
4053 && buffer_timestamp > mux->cluster_time
4054 && (buffer_timestamp - mux->cluster_time) >=
4055 MIN (G_MAXINT16 * mux->time_scale, mux->max_cluster_duration));
4058 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
4059 * or when we may be reaching the limit of the relative timestamp */
4060 if (is_max_duration_exceeded || (is_video_keyframe
4061 && is_min_duration_reached) || mux->force_key_unit_event
4062 || (is_audio_only && is_min_duration_reached)) {
4063 if (!mux->ebml_write->streamable)
4064 gst_ebml_write_master_finish (ebml, mux->cluster);
4066 /* Forward the GstForceKeyUnit event after finishing the cluster */
4067 if (mux->force_key_unit_event) {
4068 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
4069 mux->force_key_unit_event = NULL;
4071 cluster_time_scaled =
4072 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4074 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
4075 mux->cluster_pos = ebml->pos;
4076 gst_ebml_write_set_cache (ebml, 0x20);
4078 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4079 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4080 cluster_time_scaled);
4081 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
4082 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
4083 gst_ebml_write_flush_cache (ebml, is_video_keyframe
4084 || is_audio_only, buffer_timestamp);
4085 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
4086 mux->prev_cluster_size);
4087 /* cluster_time needs to be identical in value to what's stored in the
4088 * matroska so we need to have it with the same precision as what's
4089 * possible with the set timecodescale rather than just using the
4091 * If this is not done the rounding of relative_timestamp will be
4092 * incorrect and possibly making the timestamps get out of order if tw
4093 * buffers arrive at the same millisecond (assuming default timecodescale
4096 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4100 cluster_time_scaled =
4101 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4102 mux->cluster_pos = ebml->pos;
4103 gst_ebml_write_set_cache (ebml, 0x20);
4104 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4105 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4106 cluster_time_scaled);
4107 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
4108 /* cluster_time needs to be identical in value to what's stored in the
4109 * matroska so we need to have it with the same precision as what's
4110 * possible with the set timecodescale rather than just using the
4112 * If this is not done the rounding of relative_timestamp will be
4113 * incorrect and possibly making the timestamps get out of order if tw
4114 * buffers arrive at the same millisecond (assuming default timecodescale
4117 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4120 /* We currently write index entries for all video tracks or for the audio
4121 * track in a single-track audio file. This could be improved by keeping the
4122 * index only for the *first* video track. */
4124 /* TODO: index is useful for every track, should contain the number of
4125 * the block in the cluster which contains the timestamp, should also work
4126 * for files with multiple audio tracks.
4128 if (!mux->ebml_write->streamable && (is_video_keyframe || is_audio_only)) {
4131 if (mux->min_index_interval != 0) {
4132 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
4133 if (mux->index[last_idx].track == collect_pad->track->num)
4138 if (last_idx < 0 || mux->min_index_interval == 0 ||
4139 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
4140 >= mux->min_index_interval)) {
4141 GstMatroskaIndex *idx;
4143 if (mux->num_indexes % 32 == 0) {
4144 mux->index = g_renew (GstMatroskaIndex, mux->index,
4145 mux->num_indexes + 32);
4147 idx = &mux->index[mux->num_indexes++];
4149 idx->pos = mux->cluster_pos;
4150 idx->time = buffer_timestamp;
4151 idx->track = collect_pad->track->num;
4155 /* Check if the duration differs from the default duration. */
4156 write_duration = FALSE;
4158 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
4159 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
4160 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
4162 /* small difference should be ok. */
4163 if (block_duration > collect_pad->default_duration_scaled + 1 ||
4164 block_duration < collect_pad->default_duration_scaled - 1) {
4165 write_duration = TRUE;
4169 /* write the block, for doctype v2 use SimpleBlock if possible
4170 * one slice (*breath*).
4171 * FIXME: Need to do correct lacing! */
4172 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
4173 if (relative_timestamp64 >= 0) {
4174 /* round the timestamp */
4175 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
4176 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
4179 /* round the timestamp */
4180 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
4181 relative_timestamp =
4182 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
4186 if (is_video_invisible)
4189 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
4190 cmeta = gst_buffer_get_audio_clipping_meta (buf);
4191 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
4193 /* Start clipping is done via header and CodecDelay */
4194 if (cmeta && !cmeta->end)
4198 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
4199 if (is_video_keyframe)
4203 gst_matroska_mux_create_buffer_header (collect_pad->track,
4204 relative_timestamp, flags);
4205 gst_ebml_write_set_cache (ebml, 0x40);
4206 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
4207 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4208 gst_ebml_write_buffer (ebml, hdr);
4209 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4210 gst_ebml_write_buffer (ebml, buf);
4212 return gst_ebml_last_write_result (ebml);
4214 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
4215 /* write and call order slightly unnatural,
4216 * but avoids seek and minizes pushing */
4217 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
4219 gst_matroska_mux_create_buffer_header (collect_pad->track,
4220 relative_timestamp, flags);
4222 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
4224 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4226 /* Start clipping is done via header and CodecDelay */
4229 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
4230 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
4234 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
4235 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4236 gst_ebml_write_buffer (ebml, hdr);
4237 gst_ebml_write_master_finish_full (ebml, blockgroup,
4238 gst_buffer_get_size (buf));
4239 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4240 gst_ebml_write_buffer (ebml, buf);
4242 return gst_ebml_last_write_result (ebml);
4247 * gst_matroska_mux_handle_buffer:
4248 * @pads: #GstCollectPads
4249 * @uuser_data: #GstMatroskaMux
4251 * Collectpads callback.
4253 * Returns: #GstFlowReturn
4255 static GstFlowReturn
4256 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
4257 GstBuffer * buf, gpointer user_data)
4259 GstClockTime buffer_timestamp;
4260 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
4261 GstEbmlWrite *ebml = mux->ebml_write;
4262 GstMatroskaPad *best = (GstMatroskaPad *) data;
4263 GstFlowReturn ret = GST_FLOW_OK;
4264 GST_DEBUG_OBJECT (mux, "Collected pads");
4266 /* start with a header */
4267 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
4268 if (mux->collect->data == NULL) {
4269 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
4270 ("No input streams configured"));
4271 return GST_FLOW_ERROR;
4273 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
4274 gst_ebml_start_streamheader (ebml);
4275 gst_matroska_mux_start (mux, best, buf);
4276 gst_matroska_mux_stop_streamheader (mux);
4277 mux->state = GST_MATROSKA_MUX_STATE_DATA;
4280 /* if there is no best pad, we have reached EOS */
4282 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
4283 if (!mux->ebml_write->streamable) {
4284 gst_matroska_mux_finish (mux);
4286 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
4288 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
4293 if (best->track->codec_id == NULL) {
4294 GST_ERROR_OBJECT (best->collect.pad, "No codec-id for pad");
4295 ret = GST_FLOW_NOT_NEGOTIATED;
4299 /* if we have a best stream, should also have a buffer */
4302 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
4303 if (buffer_timestamp >= mux->earliest_time) {
4304 buffer_timestamp -= mux->earliest_time;
4306 GST_ERROR_OBJECT (mux,
4307 "PTS before first PTS (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
4308 GST_TIME_ARGS (buffer_timestamp), GST_TIME_ARGS (mux->earliest_time));
4309 buffer_timestamp = 0;
4312 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
4313 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
4314 GST_TIME_ARGS (buffer_timestamp),
4315 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
4317 /* make note of first and last encountered timestamps, so we can calculate
4318 * the actual duration later when we send an updated header on eos */
4319 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4320 GstClockTime start_ts = buffer_timestamp;
4321 GstClockTime end_ts = start_ts;
4323 if (GST_BUFFER_DURATION_IS_VALID (buf))
4324 end_ts += GST_BUFFER_DURATION (buf);
4325 else if (best->track->default_duration)
4326 end_ts += best->track->default_duration;
4328 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
4329 best->end_ts = end_ts;
4331 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
4332 start_ts < best->start_ts))
4333 best->start_ts = start_ts;
4336 /* write one buffer */
4337 ret = gst_matroska_mux_write_data (mux, best, buf);
4345 * gst_matroska_mux_change_state:
4346 * @element: #GstMatroskaMux
4347 * @transition: State change transition.
4349 * Change the muxer state.
4351 * Returns: #GstStateChangeReturn
4353 static GstStateChangeReturn
4354 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
4356 GstStateChangeReturn ret;
4357 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
4359 switch (transition) {
4360 case GST_STATE_CHANGE_NULL_TO_READY:
4362 case GST_STATE_CHANGE_READY_TO_PAUSED:
4363 gst_collect_pads_start (mux->collect);
4365 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4367 case GST_STATE_CHANGE_PAUSED_TO_READY:
4368 gst_collect_pads_stop (mux->collect);
4374 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4376 switch (transition) {
4377 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4379 case GST_STATE_CHANGE_PAUSED_TO_READY:
4380 gst_matroska_mux_reset (GST_ELEMENT (mux));
4382 case GST_STATE_CHANGE_READY_TO_NULL:
4392 gst_matroska_mux_set_property (GObject * object,
4393 guint prop_id, const GValue * value, GParamSpec * pspec)
4395 GstMatroskaMux *mux;
4397 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4398 mux = GST_MATROSKA_MUX (object);
4401 case PROP_WRITING_APP:
4402 if (!g_value_get_string (value)) {
4403 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
4406 g_free (mux->writing_app);
4407 mux->writing_app = g_value_dup_string (value);
4409 case PROP_DOCTYPE_VERSION:
4410 mux->doctype_version = g_value_get_int (value);
4412 case PROP_MIN_INDEX_INTERVAL:
4413 mux->min_index_interval = g_value_get_int64 (value);
4415 case PROP_STREAMABLE:
4416 mux->ebml_write->streamable = g_value_get_boolean (value);
4418 case PROP_TIMECODESCALE:
4419 mux->time_scale = g_value_get_int64 (value);
4421 case PROP_MIN_CLUSTER_DURATION:
4422 mux->min_cluster_duration = g_value_get_int64 (value);
4424 case PROP_MAX_CLUSTER_DURATION:
4425 mux->max_cluster_duration = g_value_get_int64 (value);
4427 case PROP_OFFSET_TO_ZERO:
4428 mux->offset_to_zero = g_value_get_boolean (value);
4430 case PROP_CREATION_TIME:
4431 g_clear_pointer (&mux->creation_time, g_date_time_unref);
4432 mux->creation_time = g_value_dup_boxed (value);
4434 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4435 mux->cluster_timestamp_offset = g_value_get_uint64 (value);
4438 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4444 gst_matroska_mux_get_property (GObject * object,
4445 guint prop_id, GValue * value, GParamSpec * pspec)
4447 GstMatroskaMux *mux;
4449 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4450 mux = GST_MATROSKA_MUX (object);
4453 case PROP_WRITING_APP:
4454 g_value_set_string (value, mux->writing_app);
4456 case PROP_DOCTYPE_VERSION:
4457 g_value_set_int (value, mux->doctype_version);
4459 case PROP_MIN_INDEX_INTERVAL:
4460 g_value_set_int64 (value, mux->min_index_interval);
4462 case PROP_STREAMABLE:
4463 g_value_set_boolean (value, mux->ebml_write->streamable);
4465 case PROP_TIMECODESCALE:
4466 g_value_set_int64 (value, mux->time_scale);
4468 case PROP_MIN_CLUSTER_DURATION:
4469 g_value_set_int64 (value, mux->min_cluster_duration);
4471 case PROP_MAX_CLUSTER_DURATION:
4472 g_value_set_int64 (value, mux->max_cluster_duration);
4474 case PROP_OFFSET_TO_ZERO:
4475 g_value_set_boolean (value, mux->offset_to_zero);
4477 case PROP_CREATION_TIME:
4478 g_value_set_boxed (value, mux->creation_time);
4480 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4481 g_value_set_uint64 (value, mux->cluster_timestamp_offset);
4484 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);