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, " "stream-format = (string) \"obu-stream\", "
150 "alignment = (string) \"tu\", " COMMON_VIDEO_CAPS ";"
151 "video/x-ffv, ffversion = (int) 1, " COMMON_VIDEO_CAPS)
154 #define COMMON_AUDIO_CAPS \
155 "channels = (int) [ 1, MAX ], " \
156 "rate = (int) [ 1, MAX ]"
159 * * require codec data, etc as needed
161 static GstStaticPadTemplate audiosink_templ =
162 GST_STATIC_PAD_TEMPLATE ("audio_%u",
165 GST_STATIC_CAPS ("audio/mpeg, "
166 "mpegversion = (int) 1, "
167 "layer = (int) [ 1, 3 ], "
168 COMMON_AUDIO_CAPS "; "
170 "mpegversion = (int) { 2, 4 }, "
171 "stream-format = (string) raw, "
172 COMMON_AUDIO_CAPS "; "
174 COMMON_AUDIO_CAPS "; "
176 COMMON_AUDIO_CAPS "; "
178 COMMON_AUDIO_CAPS "; "
180 COMMON_AUDIO_CAPS "; "
182 COMMON_AUDIO_CAPS "; "
184 "channels = (int) [ 1, 8 ], "
185 "rate = (int) { 8000, 16000, 24000, 32000, 48000 }; "
187 COMMON_AUDIO_CAPS "; "
189 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
190 "layout = (string) interleaved, "
191 COMMON_AUDIO_CAPS ";"
193 "width = (int) { 8, 16, 24 }, "
194 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
195 "audio/x-pn-realaudio, "
196 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
197 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
198 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
199 COMMON_AUDIO_CAPS ";"
201 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
203 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
205 "layout = (string)dvi, "
206 "block_align = (int)[64, 8192], "
207 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
209 "channels = (int)1," "rate = (int)16000; "
211 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
214 static GstStaticPadTemplate subtitlesink_templ =
215 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
218 GST_STATIC_CAPS ("subtitle/x-kate; "
219 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
220 "application/x-usf; subpicture/x-dvd; "
221 "application/x-subtitle-unknown")
224 static gpointer parent_class; /* NULL */
226 /* Matroska muxer destructor */
227 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
228 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
229 static void gst_matroska_mux_finalize (GObject * object);
231 /* Pads collected callback */
232 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
233 GstCollectData * data, GstBuffer * buf, gpointer user_data);
234 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
235 GstCollectData * data, GstEvent * event, gpointer user_data);
238 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
239 GstObject * parent, GstEvent * event);
240 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
241 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
242 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
244 /* gst internal change state handler */
245 static GstStateChangeReturn
246 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
248 /* gobject bla bla */
249 static void gst_matroska_mux_set_property (GObject * object,
250 guint prop_id, const GValue * value, GParamSpec * pspec);
251 static void gst_matroska_mux_get_property (GObject * object,
252 guint prop_id, GValue * value, GParamSpec * pspec);
255 static void gst_matroska_mux_reset (GstElement * element);
258 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
260 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
261 GstMatroskaTrackContext * context);
262 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
263 GstMatroskaTrackContext * context);
264 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
265 GstMatroskaTrackContext * context);
266 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
267 GstMatroskaTrackContext * context);
268 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
269 GstMatroskaTrackContext * context);
271 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
273 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
274 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
275 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
277 /* Cannot use boilerplate macros here because we need the full init function
278 * signature with the additional class argument, so we use the right template
279 * for the sink caps */
281 gst_matroska_mux_get_type (void)
283 static GType object_type; /* 0 */
285 if (object_type == 0) {
286 static const GTypeInfo object_info = {
287 sizeof (GstMatroskaMuxClass),
288 NULL, /* base_init */
289 NULL, /* base_finalize */
290 (GClassInitFunc) gst_matroska_mux_class_init,
291 NULL, /* class_finalize */
292 NULL, /* class_data */
293 sizeof (GstMatroskaMux),
295 (GInstanceInitFunc) gst_matroska_mux_init
297 const GInterfaceInfo iface_info = { NULL };
299 object_type = g_type_register_static (GST_TYPE_ELEMENT,
300 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
302 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
303 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
309 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (matroskamux, "matroskamux",
310 GST_RANK_PRIMARY, GST_TYPE_MATROSKA_MUX, matroska_element_init (plugin));
313 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
315 GObjectClass *gobject_class;
316 GstElementClass *gstelement_class;
318 gobject_class = (GObjectClass *) klass;
319 gstelement_class = (GstElementClass *) klass;
321 gst_element_class_add_static_pad_template (gstelement_class,
323 gst_element_class_add_static_pad_template (gstelement_class,
325 gst_element_class_add_static_pad_template (gstelement_class,
326 &subtitlesink_templ);
327 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
328 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
330 "Muxes video/audio/subtitle streams into a matroska stream",
331 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
333 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
336 gobject_class->finalize = gst_matroska_mux_finalize;
338 gobject_class->get_property = gst_matroska_mux_get_property;
339 gobject_class->set_property = gst_matroska_mux_set_property;
341 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
342 g_param_spec_string ("writing-app", "Writing application.",
343 "The name the application that creates the matroska file.",
344 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
345 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
346 g_param_spec_int ("version", "DocType version",
347 "This parameter determines what Matroska features can be used.",
348 1, 2, DEFAULT_DOCTYPE_VERSION,
349 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
350 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
351 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
352 "entries", "An index entry is created every so many nanoseconds.",
353 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
354 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
355 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
356 g_param_spec_boolean ("streamable", "Determines whether output should "
357 "be streamable", "If set to true, the output should be as if it is "
358 "to be streamed and hence no indexes written or duration written.",
359 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
360 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
361 g_param_spec_int64 ("timecodescale", "Timecode Scale",
362 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
363 GST_SECOND, DEFAULT_TIMECODESCALE,
364 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
365 g_object_class_install_property (gobject_class, PROP_MIN_CLUSTER_DURATION,
366 g_param_spec_int64 ("min-cluster-duration", "Minimum cluster duration",
367 "Desired cluster duration as nanoseconds. A new cluster will be "
368 "created irrespective of this property if a force key unit event "
369 "is received. 0 means create a new cluster for each video keyframe "
370 "or for each audio buffer in audio only streams.", 0,
371 G_MAXINT64, DEFAULT_MIN_CLUSTER_DURATION,
372 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
373 g_object_class_install_property (gobject_class, PROP_MAX_CLUSTER_DURATION,
374 g_param_spec_int64 ("max-cluster-duration", "Maximum cluster duration",
375 "A new cluster will be created if its duration exceeds this value. "
376 "0 means no maximum duration.", 0,
377 G_MAXINT64, DEFAULT_MAX_CLUSTER_DURATION,
378 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
379 g_object_class_install_property (gobject_class, PROP_OFFSET_TO_ZERO,
380 g_param_spec_boolean ("offset-to-zero", "Offset To Zero",
381 "Offsets all streams so that the " "earliest stream starts at 0.",
382 DEFAULT_OFFSET_TO_ZERO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
383 g_object_class_install_property (gobject_class, PROP_CREATION_TIME,
384 g_param_spec_boxed ("creation-time", "Creation Time",
385 "Date and time of creation. This will be used for the DateUTC field."
386 " NULL means that the current time will be used.",
387 G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
390 * GstMatroskaMux:cluster-timestamp-offset:
392 * An offset to add to all clusters/blocks (in nanoseconds)
396 g_object_class_install_property (gobject_class, PROP_CLUSTER_TIMESTAMP_OFFSET,
397 g_param_spec_uint64 ("cluster-timestamp-offset",
398 "Cluster timestamp offset",
399 "An offset to add to all clusters/blocks (in nanoseconds)", 0,
400 G_MAXUINT64, DEFAULT_CLUSTER_TIMESTAMP_OFFSET,
401 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
403 gstelement_class->change_state =
404 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
405 gstelement_class->request_new_pad =
406 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
407 gstelement_class->release_pad =
408 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
410 parent_class = g_type_class_peek_parent (klass);
414 * Start of pad option handler code
416 #define DEFAULT_PAD_FRAME_DURATION TRUE
421 PROP_PAD_FRAME_DURATION
427 gboolean frame_duration;
428 gboolean frame_duration_user;
431 typedef GstPadClass GstMatroskamuxPadClass;
433 GType gst_matroskamux_pad_get_type (void);
434 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
436 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
437 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
438 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
439 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
442 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
443 GValue * value, GParamSpec * pspec)
445 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
448 case PROP_PAD_FRAME_DURATION:
449 g_value_set_boolean (value, pad->frame_duration);
452 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
458 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
459 const GValue * value, GParamSpec * pspec)
461 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
464 case PROP_PAD_FRAME_DURATION:
465 pad->frame_duration = g_value_get_boolean (value);
466 pad->frame_duration_user = TRUE;
469 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
475 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
477 GObjectClass *gobject_class = (GObjectClass *) klass;
479 gobject_class->set_property = gst_matroskamux_pad_set_property;
480 gobject_class->get_property = gst_matroskamux_pad_get_property;
482 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
483 g_param_spec_boolean ("frame-duration", "Frame duration",
484 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
485 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
489 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
491 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
492 pad->frame_duration_user = FALSE;
496 * End of pad option handler code
500 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
502 GstPadTemplate *templ;
505 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
506 mux->srcpad = gst_pad_new_from_template (templ, "src");
508 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
509 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
510 gst_pad_use_fixed_caps (mux->srcpad);
512 mux->collect = gst_collect_pads_new ();
513 gst_collect_pads_set_clip_function (mux->collect,
514 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
515 gst_collect_pads_set_buffer_function (mux->collect,
516 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
517 gst_collect_pads_set_event_function (mux->collect,
518 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
520 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
521 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
523 /* property defaults */
524 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
525 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
526 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
527 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
528 mux->time_scale = DEFAULT_TIMECODESCALE;
529 mux->min_cluster_duration = DEFAULT_MIN_CLUSTER_DURATION;
530 mux->max_cluster_duration = DEFAULT_MAX_CLUSTER_DURATION;
531 mux->cluster_timestamp_offset = DEFAULT_CLUSTER_TIMESTAMP_OFFSET;
533 /* initialize internal variables */
535 mux->num_streams = 0;
536 mux->num_a_streams = 0;
537 mux->num_t_streams = 0;
538 mux->num_v_streams = 0;
539 mux->internal_toc = NULL;
541 /* initialize remaining variables */
542 gst_matroska_mux_reset (GST_ELEMENT (mux));
547 * gst_matroska_mux_finalize:
548 * @object: #GstMatroskaMux that should be finalized.
550 * Finalize matroska muxer.
553 gst_matroska_mux_finalize (GObject * object)
555 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
557 gst_event_replace (&mux->force_key_unit_event, NULL);
559 gst_object_unref (mux->collect);
560 gst_object_unref (mux->ebml_write);
561 g_free (mux->writing_app);
562 g_clear_pointer (&mux->creation_time, g_date_time_unref);
564 if (mux->internal_toc) {
565 gst_toc_unref (mux->internal_toc);
566 mux->internal_toc = NULL;
569 G_OBJECT_CLASS (parent_class)->finalize (object);
574 * gst_matroska_mux_create_uid:
575 * @mux: #GstMatroskaMux to generate UID for.
577 * Generate new track UID.
579 * Returns: New track UID.
582 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
584 return (((guint64) g_random_int ()) << 32) | g_random_int ();
589 * gst_matroska_pad_reset:
590 * @collect_pad: the #GstMatroskaPad
592 * Reset and/or release resources of a matroska collect pad.
595 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
598 GstMatroskaTrackType type = 0;
600 /* free track information */
601 if (collect_pad->track != NULL) {
602 /* retrieve for optional later use */
603 name = collect_pad->track->name;
604 type = collect_pad->track->type;
605 /* extra for video */
606 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
607 GstMatroskaTrackVideoContext *ctx =
608 (GstMatroskaTrackVideoContext *) collect_pad->track;
610 if (ctx->dirac_unit) {
611 gst_buffer_unref (ctx->dirac_unit);
612 ctx->dirac_unit = NULL;
615 g_free (collect_pad->track->codec_id);
616 g_free (collect_pad->track->codec_name);
618 g_free (collect_pad->track->name);
619 g_free (collect_pad->track->language);
620 g_free (collect_pad->track->codec_priv);
621 g_free (collect_pad->track);
622 collect_pad->track = NULL;
623 if (collect_pad->tags) {
624 gst_tag_list_unref (collect_pad->tags);
625 collect_pad->tags = NULL;
629 if (!full && type != 0) {
630 GstMatroskaTrackContext *context;
632 /* create a fresh context */
634 case GST_MATROSKA_TRACK_TYPE_VIDEO:
635 context = (GstMatroskaTrackContext *)
636 g_new0 (GstMatroskaTrackVideoContext, 1);
638 case GST_MATROSKA_TRACK_TYPE_AUDIO:
639 context = (GstMatroskaTrackContext *)
640 g_new0 (GstMatroskaTrackAudioContext, 1);
642 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
643 context = (GstMatroskaTrackContext *)
644 g_new0 (GstMatroskaTrackSubtitleContext, 1);
647 g_assert_not_reached ();
651 context->type = type;
652 context->name = name;
653 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
654 /* TODO: check default values for the context */
655 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
656 collect_pad->track = context;
657 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
658 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
659 collect_pad->tags = gst_tag_list_new_empty ();
660 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
665 * gst_matroska_pad_free:
666 * @collect_pad: the #GstMatroskaPad
668 * Release resources of a matroska collect pad.
671 gst_matroska_pad_free (GstPad * collect_pad)
673 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
678 * gst_matroska_mux_reset:
679 * @element: #GstMatroskaMux that should be reset.
681 * Reset matroska muxer back to initial state.
684 gst_matroska_mux_reset (GstElement * element)
686 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
689 /* reset EBML write */
690 gst_ebml_write_reset (mux->ebml_write);
693 mux->state = GST_MATROSKA_MUX_STATE_START;
695 /* clean up existing streams */
697 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
698 GstMatroskaPad *collect_pad;
700 collect_pad = (GstMatroskaPad *) walk->data;
702 /* reset collect pad to pristine state */
703 gst_matroska_pad_reset (collect_pad, FALSE);
707 mux->num_indexes = 0;
716 mux->cluster_time = 0;
717 mux->cluster_pos = 0;
718 mux->prev_cluster_size = 0;
721 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
726 gst_toc_setter_reset (GST_TOC_SETTER (mux));
727 if (mux->internal_toc) {
728 gst_toc_unref (mux->internal_toc);
729 mux->internal_toc = NULL;
732 mux->chapters_pos = 0;
736 * gst_matroska_mux_handle_src_event:
737 * @pad: Pad which received the event.
738 * @event: Received event.
740 * handle events - copied from oggmux without understanding
742 * Returns: %TRUE on success.
745 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
750 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
754 /* disable seeking for now */
760 return gst_pad_event_default (pad, parent, event);
765 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
767 if (context->codec_priv != NULL) {
768 g_free (context->codec_priv);
769 context->codec_priv = NULL;
770 context->codec_priv_size = 0;
775 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
785 /* produce comma-separated list in hex format */
786 for (i = 0; i < 16; ++i) {
788 /* replicate vobsub's slightly off RGB conversion calculation */
789 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
790 u = ((col >> 8) & 0xff) - 128;
791 v = (col & 0xff) - 128;
792 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
793 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
794 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
795 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
798 sclut = g_strjoinv (",", clutv);
800 /* build codec private; only palette for now */
801 gst_matroska_mux_free_codec_priv (context);
802 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
803 /* include terminating 0 */
804 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
806 for (i = 0; i < 16; ++i) {
813 * gst_matroska_mux_handle_sink_event:
814 * @pad: Pad which received the event.
815 * @event: Received event.
817 * handle events - informational ones like tags
819 * Returns: %TRUE on success.
822 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
823 GstCollectData * data, GstEvent * event, gpointer user_data)
825 GstMatroskaPad *collect_pad;
826 GstMatroskaTrackContext *context;
832 mux = GST_MATROSKA_MUX (user_data);
833 collect_pad = (GstMatroskaPad *) data;
835 context = collect_pad->track;
838 switch (GST_EVENT_TYPE (event)) {
839 case GST_EVENT_CAPS:{
842 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
843 gst_event_parse_caps (event, &caps);
845 ret = collect_pad->capsfunc (pad, caps);
846 gst_event_unref (event);
853 GST_DEBUG_OBJECT (mux, "received tag event");
854 gst_event_parse_tag (event, &list);
856 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
857 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
858 const gchar *lang_code;
860 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
862 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
863 g_free (context->language);
864 context->language = g_strdup (lang_code);
866 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
871 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
872 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
873 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
877 /* Stream specific tags */
878 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
880 /* If the tags contain a title, update the context name to write it there */
881 if (gst_tag_list_get_string (list, GST_TAG_TITLE, &title)) {
882 GST_INFO_OBJECT (pad, "Setting track name to '%s'", title);
883 g_free (context->name);
884 context->name = g_strdup (title);
889 gst_event_unref (event);
890 /* handled this, don't want collectpads to forward it downstream */
896 GstToc *toc, *old_toc;
898 if (mux->chapters_pos > 0)
901 GST_DEBUG_OBJECT (mux, "received toc event");
902 gst_event_parse_toc (event, &toc, NULL);
905 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
906 if (old_toc != NULL) {
908 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
909 gst_toc_unref (old_toc);
912 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
916 gst_event_unref (event);
917 /* handled this, don't want collectpads to forward it downstream */
921 case GST_EVENT_CUSTOM_DOWNSTREAM:
922 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
923 const GstStructure *structure;
925 structure = gst_event_get_structure (event);
926 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
927 gst_event_replace (&mux->force_key_unit_event, NULL);
928 mux->force_key_unit_event = event;
930 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
931 !strcmp ("dvd-spu-clut-change",
932 gst_structure_get_string (structure, "event"))) {
937 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
938 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
939 GST_DEBUG_OBJECT (pad, "... discarding");
942 /* first transform event data into table form */
943 for (i = 0; i < 16; i++) {
944 g_snprintf (name, sizeof (name), "clut%02d", i);
945 if (!gst_structure_get_int (structure, name, &value)) {
946 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
947 "contain %s field", name);
953 /* transform into private data for stream; text form */
954 gst_matroska_mux_build_vobsub_private (context, clut);
964 return gst_collect_pads_event_default (pads, data, event, FALSE);
970 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
973 g_assert (context && id);
974 g_free (context->codec_id);
975 context->codec_id = g_strdup (id);
979 check_field (GQuark field_id, const GValue * value, gpointer user_data)
981 GstStructure *structure = (GstStructure *) user_data;
982 const gchar *name = gst_structure_get_name (structure);
984 if ((g_strcmp0 (name, "video/x-h264") == 0 &&
985 !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
986 "avc3")) || (g_strcmp0 (name, "video/x-h265") == 0
987 && !g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
990 /* While in theory, matroska only supports avc1 / hvc1, and doesn't support codec_data
991 * changes, in practice most decoders will use in-band SPS / PPS (avc3 / hev1), if the
992 * input stream is avc3 / hev1 we let the new codec_data slide to support "smart" encoding.
994 * We don't warn here as we already warned elsewhere.
996 if (field_id == g_quark_from_static_string ("codec_data")) {
998 } else if (field_id == g_quark_from_static_string ("tier")) {
1000 } else if (field_id == g_quark_from_static_string ("profile")) {
1002 } else if (field_id == g_quark_from_static_string ("level")) {
1004 } else if (field_id == g_quark_from_static_string ("width")) {
1006 } else if (field_id == g_quark_from_static_string ("height")) {
1009 } else if (gst_structure_has_name (structure, "video/x-vp8")
1010 || gst_structure_has_name (structure, "video/x-vp9")) {
1011 /* We do not use profile and streamheader for VPX so let it change
1013 if (field_id == g_quark_from_static_string ("streamheader"))
1015 else if (field_id == g_quark_from_static_string ("profile"))
1017 else if (field_id == g_quark_from_static_string ("width"))
1019 else if (field_id == g_quark_from_static_string ("height"))
1023 /* This fields aren't used and are not retained into the bitstream so we can
1025 if (g_str_has_prefix (gst_structure_get_name (structure), "video/")) {
1026 if (field_id == g_quark_from_static_string ("chroma-site"))
1028 else if (field_id == g_quark_from_static_string ("chroma-format"))
1030 else if (field_id == g_quark_from_static_string ("bit-depth-luma"))
1033 /* Remove pixel-aspect-ratio field if it contains 1/1 as that's considered
1034 * equivalent to not having the field but are not considered equivalent
1035 * by the generic caps functions
1037 if (field_id == g_quark_from_static_string ("pixel-aspect-ratio")) {
1038 gint par_n = gst_value_get_fraction_numerator (value);
1039 gint par_d = gst_value_get_fraction_denominator (value);
1041 if (par_n == 1 && par_d == 1)
1045 /* Remove multiview-mode=mono and multiview-flags=0 fields as those are
1046 * equivalent with not having the fields but are not considered equivalent
1047 * by the generic caps functions.
1049 if (field_id == g_quark_from_static_string ("multiview-mode")) {
1050 const gchar *s = g_value_get_string (value);
1052 if (g_strcmp0 (s, "mono") == 0)
1056 if (field_id == g_quark_from_static_string ("multiview-flags")) {
1057 guint multiview_flags = gst_value_get_flagset_flags (value);
1059 if (multiview_flags == 0)
1068 check_new_caps (GstMatroskaTrackVideoContext * videocontext, GstCaps * old_caps,
1071 GstStructure *old_s, *new_s;
1074 old_caps = gst_caps_copy (old_caps);
1075 new_caps = gst_caps_copy (new_caps);
1077 new_s = gst_caps_get_structure (new_caps, 0);
1078 old_s = gst_caps_get_structure (old_caps, 0);
1080 gst_structure_filter_and_map_in_place (new_s,
1081 (GstStructureFilterMapFunc) check_field, new_s);
1082 gst_structure_filter_and_map_in_place (old_s,
1083 (GstStructureFilterMapFunc) check_field, old_s);
1085 ret = gst_caps_is_subset (new_caps, old_caps);
1087 gst_caps_unref (new_caps);
1088 gst_caps_unref (old_caps);
1094 * gst_matroska_mux_video_pad_setcaps:
1095 * @pad: Pad which got the caps.
1098 * Setcaps function for video sink pad.
1100 * Returns: %TRUE on success.
1103 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
1105 GstMatroskaTrackContext *context = NULL;
1106 GstMatroskaTrackVideoContext *videocontext;
1107 GstMatroskaMux *mux;
1108 GstMatroskaPad *collect_pad;
1109 GstStructure *structure;
1110 const gchar *mimetype;
1111 const gchar *interlace_mode, *s;
1112 const GValue *value = NULL;
1113 GstBuffer *codec_buf = NULL;
1114 gint width, height, pixel_width, pixel_height;
1116 guint multiview_flags;
1119 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1122 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1123 g_assert (collect_pad);
1124 context = collect_pad->track;
1126 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
1127 videocontext = (GstMatroskaTrackVideoContext *) context;
1129 if ((old_caps = gst_pad_get_current_caps (pad))) {
1130 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1131 && !check_new_caps (videocontext, old_caps, caps)) {
1132 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1133 ("Caps changes are not supported by Matroska\nCurrent: `%"
1134 GST_PTR_FORMAT "`\nNew: `%" GST_PTR_FORMAT "`", old_caps, caps));
1135 gst_caps_unref (old_caps);
1138 gst_caps_unref (old_caps);
1139 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
1140 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1141 ("Caps on pad %" GST_PTR_FORMAT
1142 " arrived late. Headers were already written", pad));
1146 /* gst -> matroska ID'ing */
1147 structure = gst_caps_get_structure (caps, 0);
1149 mimetype = gst_structure_get_name (structure);
1151 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
1152 if (interlace_mode != NULL) {
1153 if (strcmp (interlace_mode, "progressive") == 0)
1154 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE;
1156 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_INTERLACED;
1158 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_UNKNOWN;
1161 if (!strcmp (mimetype, "video/x-theora")) {
1162 /* we'll extract the details later from the theora identification header */
1166 /* get general properties */
1167 /* spec says it is mandatory */
1168 if (!gst_structure_get_int (structure, "width", &width) ||
1169 !gst_structure_get_int (structure, "height", &height))
1172 videocontext->pixel_width = width;
1173 videocontext->pixel_height = height;
1175 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
1176 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
1178 context->default_duration =
1179 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
1180 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
1181 GST_TIME_ARGS (context->default_duration));
1183 context->default_duration = 0;
1185 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
1186 &pixel_width, &pixel_height)) {
1187 if (pixel_width > pixel_height) {
1188 videocontext->display_width = width * pixel_width / pixel_height;
1189 videocontext->display_height = height;
1190 } else if (pixel_width < pixel_height) {
1191 videocontext->display_width = width;
1192 videocontext->display_height = height * pixel_height / pixel_width;
1194 videocontext->display_width = 0;
1195 videocontext->display_height = 0;
1198 videocontext->display_width = 0;
1199 videocontext->display_height = 0;
1202 if ((s = gst_structure_get_string (structure, "colorimetry"))) {
1203 if (!gst_video_colorimetry_from_string (&videocontext->colorimetry, s)) {
1204 GST_WARNING_OBJECT (pad, "Could not parse colorimetry %s", s);
1208 if ((s = gst_structure_get_string (structure, "mastering-display-info"))) {
1209 if (!gst_video_mastering_display_info_from_string
1210 (&videocontext->mastering_display_info, s)) {
1211 GST_WARNING_OBJECT (pad, "Could not parse mastering-display-metadata %s",
1214 videocontext->mastering_display_info_present = TRUE;
1218 if ((s = gst_structure_get_string (structure, "content-light-level"))) {
1219 if (!gst_video_content_light_level_from_string
1220 (&videocontext->content_light_level, s))
1221 GST_WARNING_OBJECT (pad, "Could not parse content-light-level %s", s);
1224 /* Collect stereoscopic info, if any */
1225 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1226 videocontext->multiview_mode =
1227 gst_video_multiview_mode_from_caps_string (s);
1228 gst_structure_get_flagset (structure, "multiview-flags", &multiview_flags,
1230 videocontext->multiview_flags = multiview_flags;
1235 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1236 videocontext->fourcc = 0;
1238 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1239 * data and other settings
1243 /* extract codec_data, may turn out needed */
1244 value = gst_structure_get_value (structure, "codec_data");
1246 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1249 if (!strcmp (mimetype, "video/x-raw")) {
1251 gst_matroska_mux_set_codec_id (context,
1252 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1253 fstr = gst_structure_get_string (structure, "format");
1255 if (strlen (fstr) == 4)
1256 videocontext->fourcc = GST_STR_FOURCC (fstr);
1257 else if (!strcmp (fstr, "GRAY8"))
1258 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1259 else if (!strcmp (fstr, "BGR"))
1260 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1261 else if (!strcmp (fstr, "RGB"))
1262 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1264 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1265 ||!strcmp (mimetype, "video/x-divx")
1266 || !strcmp (mimetype, "video/x-dv")
1267 || !strcmp (mimetype, "video/x-h263")
1268 || !strcmp (mimetype, "video/x-msmpeg")
1269 || !strcmp (mimetype, "video/x-wmv")
1270 || !strcmp (mimetype, "image/jpeg")) {
1271 gst_riff_strf_vids *bih;
1272 gint size = sizeof (gst_riff_strf_vids);
1275 if (!strcmp (mimetype, "video/x-huffyuv"))
1276 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1277 else if (!strcmp (mimetype, "video/x-dv"))
1278 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1279 else if (!strcmp (mimetype, "video/x-h263"))
1280 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1281 else if (!strcmp (mimetype, "video/x-divx")) {
1284 gst_structure_get_int (structure, "divxversion", &divxversion);
1285 switch (divxversion) {
1287 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1290 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1293 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1296 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1299 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1300 switch (msmpegversion) {
1302 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1305 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1311 } else if (!strcmp (mimetype, "video/x-wmv")) {
1315 fstr = gst_structure_get_string (structure, "format");
1316 if (fstr && strlen (fstr) == 4) {
1317 fourcc = GST_STR_FOURCC (fstr);
1318 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1319 if (wmvversion == 2) {
1320 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1321 } else if (wmvversion == 1) {
1322 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1323 } else if (wmvversion == 3) {
1324 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1327 } else if (!strcmp (mimetype, "image/jpeg")) {
1328 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1334 bih = g_new0 (gst_riff_strf_vids, 1);
1335 GST_WRITE_UINT32_LE (&bih->size, size);
1336 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1337 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1338 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1339 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1340 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1341 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1342 videocontext->pixel_height * 3);
1344 /* process codec private/initialization data, if any */
1346 size += gst_buffer_get_size (codec_buf);
1347 bih = g_realloc (bih, size);
1348 GST_WRITE_UINT32_LE (&bih->size, size);
1349 gst_buffer_extract (codec_buf, 0,
1350 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1353 gst_matroska_mux_set_codec_id (context,
1354 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1355 gst_matroska_mux_free_codec_priv (context);
1356 context->codec_priv = (gpointer) bih;
1357 context->codec_priv_size = size;
1358 context->dts_only = TRUE;
1359 } else if (!strcmp (mimetype, "video/x-h264")) {
1360 gst_matroska_mux_set_codec_id (context,
1361 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1362 gst_matroska_mux_free_codec_priv (context);
1364 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1366 GST_WARNING_OBJECT (mux,
1367 "avc3 is not officially supported, only use this format for smart encoding");
1370 /* Create avcC header */
1371 if (codec_buf != NULL) {
1372 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1373 context->codec_priv = g_malloc0 (context->codec_priv_size);
1374 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1376 } else if (!strcmp (mimetype, "video/x-h265")) {
1377 gst_matroska_mux_set_codec_id (context,
1378 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1379 gst_matroska_mux_free_codec_priv (context);
1381 if (!g_strcmp0 (gst_structure_get_string (structure, "stream-format"),
1383 GST_WARNING_OBJECT (mux,
1384 "hev1 is not officially supported, only use this format for smart encoding");
1387 /* Create hvcC header */
1388 if (codec_buf != NULL) {
1389 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1390 context->codec_priv = g_malloc0 (context->codec_priv_size);
1391 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1393 } else if (!strcmp (mimetype, "video/x-theora")) {
1394 const GValue *streamheader;
1396 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1398 gst_matroska_mux_free_codec_priv (context);
1400 streamheader = gst_structure_get_value (structure, "streamheader");
1401 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1402 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1403 ("theora stream headers missing or malformed"));
1406 } else if (!strcmp (mimetype, "video/x-dirac")) {
1407 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1408 } else if (!strcmp (mimetype, "video/x-vp8")) {
1409 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1410 } else if (!strcmp (mimetype, "video/x-vp9")) {
1411 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1412 } else if (!strcmp (mimetype, "video/x-av1")) {
1413 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_AV1);
1414 gst_matroska_mux_free_codec_priv (context);
1415 /* Create av1C header */
1416 if (codec_buf != NULL)
1417 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1418 &context->codec_priv, &context->codec_priv_size);
1419 } else if (!strcmp (mimetype, "video/x-ffv")) {
1420 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_FFV1);
1421 gst_matroska_mux_free_codec_priv (context);
1422 if (codec_buf != NULL)
1423 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1424 &context->codec_priv, &context->codec_priv_size);
1425 } else if (!strcmp (mimetype, "video/mpeg")) {
1428 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1429 switch (mpegversion) {
1431 gst_matroska_mux_set_codec_id (context,
1432 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1435 gst_matroska_mux_set_codec_id (context,
1436 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1439 gst_matroska_mux_set_codec_id (context,
1440 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1446 /* global headers may be in codec data */
1447 if (codec_buf != NULL) {
1448 gst_matroska_mux_free_codec_priv (context);
1449 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1450 context->codec_priv = g_malloc0 (context->codec_priv_size);
1451 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1453 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1455 /* can only make it here if preceding case verified it was version 3 */
1456 gst_matroska_mux_set_codec_id (context,
1457 GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1458 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1460 const GValue *mdpr_data;
1462 gst_structure_get_int (structure, "rmversion", &rmversion);
1463 switch (rmversion) {
1465 gst_matroska_mux_set_codec_id (context,
1466 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1469 gst_matroska_mux_set_codec_id (context,
1470 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1473 gst_matroska_mux_set_codec_id (context,
1474 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1477 gst_matroska_mux_set_codec_id (context,
1478 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1484 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1485 if (mdpr_data != NULL) {
1486 guint8 *priv_data = NULL;
1487 guint priv_data_size = 0;
1489 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1491 priv_data_size = gst_buffer_get_size (codec_data_buf);
1492 priv_data = g_malloc0 (priv_data_size);
1494 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1496 gst_matroska_mux_free_codec_priv (context);
1497 context->codec_priv = priv_data;
1498 context->codec_priv_size = priv_data_size;
1500 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1501 const gchar *variant;
1503 gst_matroska_mux_free_codec_priv (context);
1505 variant = gst_structure_get_string (structure, "format");
1506 if (!variant || !g_strcmp0 (variant, "standard"))
1507 context->codec_priv = g_strdup ("apcn");
1508 else if (!g_strcmp0 (variant, "hq"))
1509 context->codec_priv = g_strdup ("apch");
1510 else if (!g_strcmp0 (variant, "lt"))
1511 context->codec_priv = g_strdup ("apcs");
1512 else if (!g_strcmp0 (variant, "proxy"))
1513 context->codec_priv = g_strdup ("apco");
1514 else if (!g_strcmp0 (variant, "4444"))
1515 context->codec_priv = g_strdup ("ap4h");
1517 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1522 context->codec_priv_size = sizeof (guint32);
1523 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1531 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1532 GST_PAD_NAME (pad), caps);
1537 /* N > 0 to expect a particular number of headers, negative if the
1538 number of headers is variable */
1540 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1541 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1543 GstBuffer **buf = NULL;
1546 guint bufi, i, offset, priv_data_size;
1548 if (streamheader == NULL)
1549 goto no_stream_headers;
1551 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1554 bufarr = g_value_peek_pointer (streamheader);
1555 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1557 if (N > 0 && bufarr->len != N)
1560 context->xiph_headers_to_skip = bufarr->len;
1562 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1563 for (i = 0; i < bufarr->len; i++) {
1564 GValue *bufval = &g_array_index (bufarr, GValue, i);
1566 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1568 goto wrong_content_type;
1571 buf[i] = g_value_peek_pointer (bufval);
1575 if (bufarr->len > 0) {
1576 for (i = 0; i < bufarr->len - 1; i++) {
1577 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1581 for (i = 0; i < bufarr->len; ++i) {
1582 priv_data_size += gst_buffer_get_size (buf[i]);
1585 priv_data = g_malloc0 (priv_data_size);
1587 priv_data[0] = bufarr->len - 1;
1590 if (bufarr->len > 0) {
1591 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1592 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1593 priv_data[offset++] = 0xff;
1595 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1599 for (i = 0; i < bufarr->len; ++i) {
1600 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1601 offset += gst_buffer_get_size (buf[i]);
1604 gst_matroska_mux_free_codec_priv (context);
1605 context->codec_priv = priv_data;
1606 context->codec_priv_size = priv_data_size;
1609 *p_buf0 = gst_buffer_ref (buf[0]);
1618 GST_WARNING ("required streamheaders missing in sink caps!");
1623 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1624 G_VALUE_TYPE_NAME (streamheader));
1629 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1634 GST_WARNING ("streamheaders array does not contain GstBuffers");
1640 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1641 GstMatroskaTrackContext * context)
1643 GstBuffer *buf0 = NULL;
1645 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1648 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1649 GST_WARNING ("First vorbis header too small, ignoring");
1651 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1652 GstMatroskaTrackAudioContext *audiocontext;
1656 gst_buffer_map (buf0, &map, GST_MAP_READ);
1657 hdr = map.data + 1 + 6 + 4;
1658 audiocontext = (GstMatroskaTrackAudioContext *) context;
1659 audiocontext->channels = GST_READ_UINT8 (hdr);
1660 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1661 gst_buffer_unmap (buf0, &map);
1666 gst_buffer_unref (buf0);
1672 theora_streamheader_to_codecdata (const GValue * streamheader,
1673 GstMatroskaTrackContext * context)
1675 GstBuffer *buf0 = NULL;
1677 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1680 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1681 GST_WARNING ("First theora header too small, ignoring");
1682 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1683 GST_WARNING ("First header not a theora identification header, ignoring");
1685 GstMatroskaTrackVideoContext *videocontext;
1686 guint fps_num, fps_denom, par_num, par_denom;
1690 gst_buffer_map (buf0, &map, GST_MAP_READ);
1691 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1693 videocontext = (GstMatroskaTrackVideoContext *) context;
1694 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1695 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1696 hdr += 3 + 3 + 1 + 1;
1697 fps_num = GST_READ_UINT32_BE (hdr);
1698 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1699 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1700 fps_denom, fps_num);
1702 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1703 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1704 if (par_num > 0 && par_denom > 0) {
1705 if (par_num > par_denom) {
1706 videocontext->display_width =
1707 videocontext->pixel_width * par_num / par_denom;
1708 videocontext->display_height = videocontext->pixel_height;
1709 } else if (par_num < par_denom) {
1710 videocontext->display_width = videocontext->pixel_width;
1711 videocontext->display_height =
1712 videocontext->pixel_height * par_denom / par_num;
1714 videocontext->display_width = 0;
1715 videocontext->display_height = 0;
1718 videocontext->display_width = 0;
1719 videocontext->display_height = 0;
1722 gst_buffer_unmap (buf0, &map);
1726 gst_buffer_unref (buf0);
1732 kate_streamheader_to_codecdata (const GValue * streamheader,
1733 GstMatroskaTrackContext * context)
1735 GstBuffer *buf0 = NULL;
1737 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1740 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1741 GST_WARNING ("First kate header too small, ignoring");
1742 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1743 GST_WARNING ("First header not a kate identification header, ignoring");
1747 gst_buffer_unref (buf0);
1753 flac_streamheader_to_codecdata (const GValue * streamheader,
1754 GstMatroskaTrackContext * context)
1761 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1762 GST_WARNING ("No or invalid streamheader field in the caps");
1766 bufarr = g_value_peek_pointer (streamheader);
1767 if (bufarr->len < 2) {
1768 GST_WARNING ("Too few headers in streamheader field");
1772 context->xiph_headers_to_skip = bufarr->len + 1;
1774 bufval = &g_array_index (bufarr, GValue, 0);
1775 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1776 GST_WARNING ("streamheaders array does not contain GstBuffers");
1780 buffer = g_value_peek_pointer (bufval);
1782 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1783 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1784 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1785 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1786 GST_WARNING ("Invalid streamheader for FLAC");
1790 gst_matroska_mux_free_codec_priv (context);
1791 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1792 context->codec_priv = g_malloc (context->codec_priv_size);
1793 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1795 for (i = 1; i < bufarr->len; i++) {
1797 bufval = &g_array_index (bufarr, GValue, i);
1799 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1800 gst_matroska_mux_free_codec_priv (context);
1801 GST_WARNING ("streamheaders array does not contain GstBuffers");
1805 buffer = g_value_peek_pointer (bufval);
1807 old_size = context->codec_priv_size;
1808 context->codec_priv_size += gst_buffer_get_size (buffer);
1810 context->codec_priv = g_realloc (context->codec_priv,
1811 context->codec_priv_size);
1812 gst_buffer_extract (buffer, 0,
1813 (guint8 *) context->codec_priv + old_size, -1);
1820 speex_streamheader_to_codecdata (const GValue * streamheader,
1821 GstMatroskaTrackContext * context)
1828 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1829 GST_WARNING ("No or invalid streamheader field in the caps");
1833 bufarr = g_value_peek_pointer (streamheader);
1834 if (bufarr->len != 2) {
1835 GST_WARNING ("Too few headers in streamheader field");
1839 context->xiph_headers_to_skip = bufarr->len + 1;
1841 bufval = &g_array_index (bufarr, GValue, 0);
1842 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1843 GST_WARNING ("streamheaders array does not contain GstBuffers");
1847 buffer = g_value_peek_pointer (bufval);
1849 if (gst_buffer_get_size (buffer) < 80
1850 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1851 GST_WARNING ("Invalid streamheader for Speex");
1855 gst_matroska_mux_free_codec_priv (context);
1856 context->codec_priv_size = gst_buffer_get_size (buffer);
1857 context->codec_priv = g_malloc (context->codec_priv_size);
1858 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1860 bufval = &g_array_index (bufarr, GValue, 1);
1862 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1863 gst_matroska_mux_free_codec_priv (context);
1864 GST_WARNING ("streamheaders array does not contain GstBuffers");
1868 buffer = g_value_peek_pointer (bufval);
1870 old_size = context->codec_priv_size;
1871 context->codec_priv_size += gst_buffer_get_size (buffer);
1872 context->codec_priv = g_realloc (context->codec_priv,
1873 context->codec_priv_size);
1874 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1880 opus_streamheader_to_codecdata (const GValue * streamheader,
1881 GstMatroskaTrackContext * context)
1887 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1890 bufarr = g_value_peek_pointer (streamheader);
1891 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1894 /* Opus headers are not in-band */
1895 context->xiph_headers_to_skip = 0;
1897 bufval = &g_array_index (bufarr, GValue, 0);
1898 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1899 goto wrong_content_type;
1901 buf = g_value_peek_pointer (bufval);
1903 gst_matroska_mux_free_codec_priv (context);
1905 context->codec_priv_size = gst_buffer_get_size (buf);
1906 context->codec_priv = g_malloc0 (context->codec_priv_size);
1907 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1909 context->codec_delay =
1910 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1911 context->codec_delay =
1912 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1913 context->seek_preroll = 80 * GST_MSECOND;
1920 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1921 G_VALUE_TYPE_NAME (streamheader));
1926 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1931 GST_WARNING ("streamheaders array does not contain GstBuffers");
1937 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1941 guint8 channel_mapping_family;
1942 guint8 stream_count, coupled_count, channel_mapping[256];
1946 /* Opus headers are not in-band */
1947 context->xiph_headers_to_skip = 0;
1949 context->codec_delay = 0;
1950 context->seek_preroll = 80 * GST_MSECOND;
1952 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1953 &channel_mapping_family, &stream_count, &coupled_count,
1955 GST_WARNING ("Failed to parse caps for Opus");
1960 gst_codec_utils_opus_create_header (rate, channels,
1961 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1964 GST_WARNING ("Failed to create Opus header from caps");
1968 gst_buffer_map (buffer, &map, GST_MAP_READ);
1969 context->codec_priv_size = map.size;
1970 context->codec_priv = g_malloc (context->codec_priv_size);
1971 memcpy (context->codec_priv, map.data, map.size);
1972 gst_buffer_unmap (buffer, &map);
1973 gst_buffer_unref (buffer);
1979 * gst_matroska_mux_audio_pad_setcaps:
1980 * @pad: Pad which got the caps.
1983 * Setcaps function for audio sink pad.
1985 * Returns: %TRUE on success.
1988 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1990 GstMatroskaTrackContext *context = NULL;
1991 GstMatroskaTrackAudioContext *audiocontext;
1992 GstMatroskaMux *mux;
1993 GstMatroskaPad *collect_pad;
1994 const gchar *mimetype;
1995 gint samplerate = 0, channels = 0;
1996 GstStructure *structure;
1997 const GValue *codec_data = NULL;
1998 GstBuffer *buf = NULL;
1999 const gchar *stream_format = NULL;
2002 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2004 if ((old_caps = gst_pad_get_current_caps (pad))) {
2005 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
2006 && !gst_caps_is_equal (caps, old_caps)) {
2007 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2008 ("Caps changes are not supported by Matroska"));
2009 gst_caps_unref (old_caps);
2012 gst_caps_unref (old_caps);
2013 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
2014 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2015 ("Caps on pad %" GST_PTR_FORMAT
2016 " arrived late. Headers were already written", pad));
2021 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2022 g_assert (collect_pad);
2023 context = collect_pad->track;
2025 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
2026 audiocontext = (GstMatroskaTrackAudioContext *) context;
2028 structure = gst_caps_get_structure (caps, 0);
2029 mimetype = gst_structure_get_name (structure);
2032 gst_structure_get_int (structure, "rate", &samplerate);
2033 gst_structure_get_int (structure, "channels", &channels);
2035 audiocontext->samplerate = samplerate;
2036 audiocontext->channels = channels;
2037 audiocontext->bitdepth = 0;
2038 context->default_duration = 0;
2040 codec_data = gst_structure_get_value (structure, "codec_data");
2042 buf = gst_value_get_buffer (codec_data);
2044 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
2045 * data and other settings
2049 if (!strcmp (mimetype, "audio/mpeg")) {
2050 gint mpegversion = 0;
2052 gst_structure_get_int (structure, "mpegversion", &mpegversion);
2053 switch (mpegversion) {
2059 gst_structure_get_int (structure, "layer", &layer);
2061 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
2062 GST_WARNING_OBJECT (mux,
2063 "Unable to determine MPEG audio version, assuming 1");
2069 else if (layer == 2)
2071 else if (version == 2)
2076 context->default_duration =
2077 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
2081 gst_matroska_mux_set_codec_id (context,
2082 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
2085 gst_matroska_mux_set_codec_id (context,
2086 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
2089 gst_matroska_mux_set_codec_id (context,
2090 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
2099 stream_format = gst_structure_get_string (structure, "stream-format");
2100 /* check this is raw aac */
2101 if (stream_format) {
2102 if (strcmp (stream_format, "raw") != 0) {
2103 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
2107 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
2112 gst_matroska_mux_set_codec_id (context,
2113 GST_MATROSKA_CODEC_ID_AUDIO_AAC);
2114 context->codec_priv_size = gst_buffer_get_size (buf);
2115 context->codec_priv = g_malloc (context->codec_priv_size);
2116 gst_buffer_extract (buf, 0, context->codec_priv,
2117 context->codec_priv_size);
2119 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
2126 } else if (!strcmp (mimetype, "audio/x-raw")) {
2129 gst_audio_info_init (&info);
2130 if (!gst_audio_info_from_caps (&info, caps)) {
2131 GST_DEBUG_OBJECT (mux,
2132 "broken caps, rejected by gst_audio_info_from_caps");
2136 switch (GST_AUDIO_INFO_FORMAT (&info)) {
2137 case GST_AUDIO_FORMAT_U8:
2138 case GST_AUDIO_FORMAT_S16BE:
2139 case GST_AUDIO_FORMAT_S16LE:
2140 case GST_AUDIO_FORMAT_S24BE:
2141 case GST_AUDIO_FORMAT_S24LE:
2142 case GST_AUDIO_FORMAT_S32BE:
2143 case GST_AUDIO_FORMAT_S32LE:
2144 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
2145 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
2148 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
2149 gst_matroska_mux_set_codec_id (context,
2150 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
2152 gst_matroska_mux_set_codec_id (context,
2153 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
2155 case GST_AUDIO_FORMAT_F32LE:
2156 case GST_AUDIO_FORMAT_F64LE:
2157 gst_matroska_mux_set_codec_id (context,
2158 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
2162 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
2166 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
2167 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
2168 const GValue *streamheader;
2170 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
2172 gst_matroska_mux_free_codec_priv (context);
2174 streamheader = gst_structure_get_value (structure, "streamheader");
2175 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
2176 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2177 ("vorbis stream headers missing or malformed"));
2180 } else if (!strcmp (mimetype, "audio/x-flac")) {
2181 const GValue *streamheader;
2183 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
2185 gst_matroska_mux_free_codec_priv (context);
2187 streamheader = gst_structure_get_value (structure, "streamheader");
2188 if (!flac_streamheader_to_codecdata (streamheader, context)) {
2189 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2190 ("flac stream headers missing or malformed"));
2193 } else if (!strcmp (mimetype, "audio/x-speex")) {
2194 const GValue *streamheader;
2196 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
2197 gst_matroska_mux_free_codec_priv (context);
2199 streamheader = gst_structure_get_value (structure, "streamheader");
2200 if (!speex_streamheader_to_codecdata (streamheader, context)) {
2201 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2202 ("speex stream headers missing or malformed"));
2205 } else if (!strcmp (mimetype, "audio/x-opus")) {
2206 const GValue *streamheader;
2208 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
2210 streamheader = gst_structure_get_value (structure, "streamheader");
2212 gst_matroska_mux_free_codec_priv (context);
2213 if (!opus_streamheader_to_codecdata (streamheader, context)) {
2214 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2215 ("opus stream headers missing or malformed"));
2219 /* no streamheader, but we need to have one, so we make one up
2221 gst_matroska_mux_free_codec_priv (context);
2222 if (!opus_make_codecdata (context, caps)) {
2223 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2224 ("opus stream headers missing or malformed"));
2228 } else if (!strcmp (mimetype, "audio/x-ac3")) {
2229 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
2230 } else if (!strcmp (mimetype, "audio/x-eac3")) {
2231 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
2232 } else if (!strcmp (mimetype, "audio/x-dts")) {
2233 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
2234 } else if (!strcmp (mimetype, "audio/x-tta")) {
2237 /* TTA frame duration */
2238 context->default_duration = 1.04489795918367346939 * GST_SECOND;
2240 gst_structure_get_int (structure, "width", &width);
2241 audiocontext->bitdepth = width;
2242 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
2244 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
2246 const GValue *mdpr_data;
2248 gst_structure_get_int (structure, "raversion", &raversion);
2249 switch (raversion) {
2251 gst_matroska_mux_set_codec_id (context,
2252 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
2255 gst_matroska_mux_set_codec_id (context,
2256 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2259 gst_matroska_mux_set_codec_id (context,
2260 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2266 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2267 if (mdpr_data != NULL) {
2268 guint8 *priv_data = NULL;
2269 guint priv_data_size = 0;
2271 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2273 priv_data_size = gst_buffer_get_size (codec_data_buf);
2274 priv_data = g_malloc0 (priv_data_size);
2276 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2278 gst_matroska_mux_free_codec_priv (context);
2280 context->codec_priv = priv_data;
2281 context->codec_priv_size = priv_data_size;
2284 } else if (!strcmp (mimetype, "audio/x-wma")
2285 || !strcmp (mimetype, "audio/x-alaw")
2286 || !strcmp (mimetype, "audio/x-mulaw")
2287 || !strcmp (mimetype, "audio/x-adpcm")
2288 || !strcmp (mimetype, "audio/G722")) {
2290 guint codec_priv_size;
2292 gint block_align = 0;
2295 if (samplerate == 0 || channels == 0) {
2296 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2300 if (!strcmp (mimetype, "audio/x-wma")) {
2304 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2305 || !gst_structure_get_int (structure, "block_align", &block_align)
2306 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2307 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2312 switch (wmaversion) {
2314 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2317 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2320 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2323 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2327 if (gst_structure_get_int (structure, "depth", &depth))
2328 audiocontext->bitdepth = depth;
2329 } else if (!strcmp (mimetype, "audio/x-alaw")
2330 || !strcmp (mimetype, "audio/x-mulaw")) {
2331 audiocontext->bitdepth = 8;
2332 if (!strcmp (mimetype, "audio/x-alaw"))
2333 format = GST_RIFF_WAVE_FORMAT_ALAW;
2335 format = GST_RIFF_WAVE_FORMAT_MULAW;
2337 block_align = channels;
2338 bitrate = block_align * samplerate;
2339 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2342 layout = gst_structure_get_string (structure, "layout");
2344 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2348 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2349 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2353 if (!strcmp (layout, "dvi")) {
2354 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2355 } else if (!strcmp (layout, "g726")) {
2356 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2357 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2358 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2362 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2366 } else if (!strcmp (mimetype, "audio/G722")) {
2367 format = GST_RIFF_WAVE_FORMAT_ADPCM_G722;
2369 g_assert (format != 0);
2371 codec_priv_size = WAVEFORMATEX_SIZE;
2373 codec_priv_size += gst_buffer_get_size (buf);
2375 /* serialize waveformatex structure */
2376 codec_priv = g_malloc0 (codec_priv_size);
2377 GST_WRITE_UINT16_LE (codec_priv, format);
2378 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2379 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2380 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2381 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2382 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2384 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2386 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2388 /* process codec private/initialization data, if any */
2390 gst_buffer_extract (buf, 0,
2391 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2394 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2395 gst_matroska_mux_free_codec_priv (context);
2396 context->codec_priv = (gpointer) codec_priv;
2397 context->codec_priv_size = codec_priv_size;
2405 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2406 GST_PAD_NAME (pad), caps);
2411 /* we probably don't have the data at start,
2412 * so have to reserve (a maximum) space to write this at the end.
2413 * bit spacy, but some formats can hold quite some */
2414 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2417 * gst_matroska_mux_subtitle_pad_setcaps:
2418 * @pad: Pad which got the caps.
2421 * Setcaps function for subtitle sink pad.
2423 * Returns: %TRUE on success.
2426 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2428 /* There is now (at least) one such alement (kateenc), and I'm going
2429 to handle it here and claim it works when it can be piped back
2430 through GStreamer and VLC */
2432 GstMatroskaTrackContext *context = NULL;
2433 GstMatroskaTrackSubtitleContext *scontext;
2434 GstMatroskaMux *mux;
2435 GstMatroskaPad *collect_pad;
2436 GstCollectData *data;
2437 const gchar *mimetype;
2438 GstStructure *structure;
2439 const GValue *value = NULL;
2440 GstBuffer *buf = NULL;
2441 gboolean ret = TRUE;
2444 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2446 if ((old_caps = gst_pad_get_current_caps (pad))) {
2447 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
2448 && !gst_caps_is_equal (caps, old_caps)) {
2449 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2450 ("Caps changes are not supported by Matroska"));
2451 gst_caps_unref (old_caps);
2454 gst_caps_unref (old_caps);
2455 } else if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER) {
2456 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2457 ("Caps on pad %" GST_PTR_FORMAT
2458 " arrived late. Headers were already written", pad));
2463 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2464 g_assert (collect_pad);
2465 data = (GstCollectData *) (collect_pad);
2467 context = collect_pad->track;
2469 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2470 scontext = (GstMatroskaTrackSubtitleContext *) context;
2472 structure = gst_caps_get_structure (caps, 0);
2473 mimetype = gst_structure_get_name (structure);
2476 scontext->check_utf8 = 1;
2477 scontext->invalid_utf8 = 0;
2478 context->default_duration = 0;
2480 if (!strcmp (mimetype, "subtitle/x-kate")) {
2481 const GValue *streamheader;
2483 gst_matroska_mux_set_codec_id (context,
2484 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2486 gst_matroska_mux_free_codec_priv (context);
2488 streamheader = gst_structure_get_value (structure, "streamheader");
2489 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2490 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2491 ("kate stream headers missing or malformed"));
2495 } else if (!strcmp (mimetype, "text/x-raw")) {
2496 gst_matroska_mux_set_codec_id (context,
2497 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2498 } else if (!strcmp (mimetype, "application/x-ssa")) {
2499 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2500 } else if (!strcmp (mimetype, "application/x-ass")) {
2501 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2502 } else if (!strcmp (mimetype, "application/x-usf")) {
2503 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2504 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2505 gst_matroska_mux_set_codec_id (context,
2506 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2512 /* maybe some private data, e.g. vobsub */
2513 value = gst_structure_get_value (structure, "codec_data");
2515 buf = gst_value_get_buffer (value);
2518 guint8 *priv_data = NULL;
2520 gst_buffer_map (buf, &map, GST_MAP_READ);
2522 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2523 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2524 " exceeded maximum (%d); discarding", pad,
2525 SUBTITLE_MAX_CODEC_PRIVATE);
2526 gst_buffer_unmap (buf, &map);
2530 gst_matroska_mux_free_codec_priv (context);
2532 priv_data = g_malloc0 (map.size);
2533 memcpy (priv_data, map.data, map.size);
2534 context->codec_priv = priv_data;
2535 context->codec_priv_size = map.size;
2536 gst_buffer_unmap (buf, &map);
2539 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2540 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2542 /* This pad is sparse. Now that we have caps on it, we can tell collectpads
2543 * not to actually wait for data when muxing */
2544 GST_COLLECT_PADS_STREAM_LOCK (mux->collect);
2545 GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_LOCKED);
2546 gst_collect_pads_set_waiting (mux->collect, data, FALSE);
2547 GST_COLLECT_PADS_STREAM_UNLOCK (mux->collect);
2556 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2557 GST_PAD_NAME (pad), caps);
2564 * gst_matroska_mux_request_new_pad:
2565 * @element: #GstMatroskaMux.
2566 * @templ: #GstPadTemplate.
2567 * @pad_name: New pad name.
2569 * Request pad function for sink templates.
2571 * Returns: New #GstPad.
2574 gst_matroska_mux_request_new_pad (GstElement * element,
2575 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2577 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2578 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2579 GstMatroskaPad *collect_pad;
2580 GstMatroskamuxPad *newpad;
2582 const gchar *pad_name = NULL;
2583 GstMatroskaCapsFunc capsfunc = NULL;
2584 GstMatroskaTrackContext *context = NULL;
2586 const gchar *id = NULL;
2588 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2589 /* don't mix named and unnamed pads, if the pad already exists we fail when
2590 * trying to add it */
2591 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2592 pad_name = req_name;
2594 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2597 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2598 context = (GstMatroskaTrackContext *)
2599 g_new0 (GstMatroskaTrackAudioContext, 1);
2600 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2601 context->name = g_strdup ("Audio");
2602 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2603 /* don't mix named and unnamed pads, if the pad already exists we fail when
2604 * trying to add it */
2605 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2606 pad_name = req_name;
2608 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2611 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2612 context = (GstMatroskaTrackContext *)
2613 g_new0 (GstMatroskaTrackVideoContext, 1);
2614 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2615 context->name = g_strdup ("Video");
2616 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2617 /* don't mix named and unnamed pads, if the pad already exists we fail when
2618 * trying to add it */
2619 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2620 pad_name = req_name;
2622 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2625 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2626 context = (GstMatroskaTrackContext *)
2627 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2628 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2629 context->name = g_strdup ("Subtitle");
2630 /* setcaps may only provide proper one a lot later */
2631 id = "S_SUB_UNKNOWN";
2633 GST_WARNING_OBJECT (mux, "This is not our template!");
2637 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2638 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2640 gst_matroskamux_pad_init (newpad);
2641 collect_pad = (GstMatroskaPad *)
2642 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2643 sizeof (GstMatroskaPad),
2644 (GstCollectDataDestroyNotify) gst_matroska_pad_free, TRUE);
2646 collect_pad->mux = mux;
2647 collect_pad->track = context;
2648 gst_matroska_pad_reset (collect_pad, FALSE);
2650 gst_matroska_mux_set_codec_id (collect_pad->track, id);
2651 collect_pad->track->dts_only = FALSE;
2653 collect_pad->capsfunc = capsfunc;
2654 gst_pad_set_active (GST_PAD (newpad), TRUE);
2655 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2656 goto pad_add_failed;
2662 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2664 return GST_PAD (newpad);
2669 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2671 gst_object_unref (newpad);
2677 * gst_matroska_mux_release_pad:
2678 * @element: #GstMatroskaMux.
2679 * @pad: Pad to release.
2681 * Release a previously requested pad.
2684 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2686 GstMatroskaMux *mux;
2689 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2691 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2692 GstCollectData *cdata = (GstCollectData *) walk->data;
2693 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2695 if (cdata->pad == pad) {
2697 * observed duration, this will remain GST_CLOCK_TIME_NONE
2698 * only if the pad is reset
2700 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2702 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2703 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2704 collected_duration =
2705 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2708 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2709 && mux->duration < collected_duration)
2710 mux->duration = collected_duration;
2716 gst_collect_pads_remove_pad (mux->collect, pad);
2717 if (gst_element_remove_pad (element, pad))
2722 gst_matroska_mux_write_mastering_metadata (GstMatroskaMux * mux,
2723 GstMatroskaTrackVideoContext * videocontext)
2725 GstEbmlWrite *ebml = mux->ebml_write;
2727 GstVideoMasteringDisplayInfo *minfo = &videocontext->mastering_display_info;
2729 const gdouble chroma_scale = 50000;
2730 const gdouble luma_scale = 50000;
2732 if (!videocontext->mastering_display_info_present)
2736 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_MASTERINGMETADATA);
2738 value = (gdouble) minfo->display_primaries[0].x / chroma_scale;
2739 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYX, value);
2741 value = (gdouble) minfo->display_primaries[0].y / chroma_scale;
2742 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYRCHROMATICITYY, value);
2744 value = (gdouble) minfo->display_primaries[1].x / chroma_scale;
2745 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYX, value);
2747 value = (gdouble) minfo->display_primaries[1].y / chroma_scale;
2748 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYGCHROMATICITYY, value);
2750 value = (gdouble) minfo->display_primaries[2].x / chroma_scale;
2751 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYX, value);
2753 value = (gdouble) minfo->display_primaries[2].y / chroma_scale;
2754 gst_ebml_write_float (ebml, GST_MATROSKA_ID_PRIMARYBCHROMATICITYY, value);
2756 value = (gdouble) minfo->white_point.x / chroma_scale;
2757 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYX, value);
2759 value = (gdouble) minfo->white_point.y / chroma_scale;
2760 gst_ebml_write_float (ebml, GST_MATROSKA_ID_WHITEPOINTCHROMATICITYY, value);
2762 value = (gdouble) minfo->max_display_mastering_luminance / luma_scale;
2763 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMAX, value);
2765 value = (gdouble) minfo->min_display_mastering_luminance / luma_scale;
2766 gst_ebml_write_float (ebml, GST_MATROSKA_ID_LUMINANCEMIN, value);
2768 gst_ebml_write_master_finish (ebml, master);
2773 gst_matroska_mux_write_colour (GstMatroskaMux * mux,
2774 GstMatroskaTrackVideoContext * videocontext)
2776 GstEbmlWrite *ebml = mux->ebml_write;
2778 guint matrix_id = 0;
2780 guint transfer_id = 0;
2781 guint primaries_id = 0;
2783 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_VIDEOCOLOUR);
2785 switch (videocontext->colorimetry.range) {
2786 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
2789 case GST_VIDEO_COLOR_RANGE_16_235:
2792 case GST_VIDEO_COLOR_RANGE_0_255:
2796 matrix_id = gst_video_color_matrix_to_iso (videocontext->colorimetry.matrix);
2798 gst_video_transfer_function_to_iso (videocontext->colorimetry.transfer);
2800 gst_video_color_primaries_to_iso (videocontext->colorimetry.primaries);
2802 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEORANGE, range_id);
2803 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS,
2805 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS,
2807 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPRIMARIES, primaries_id);
2808 if (videocontext->content_light_level.max_content_light_level &&
2809 videocontext->content_light_level.max_frame_average_light_level) {
2810 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXCLL,
2811 videocontext->content_light_level.max_content_light_level);
2812 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_MAXFALL,
2813 videocontext->content_light_level.max_frame_average_light_level);
2816 gst_matroska_mux_write_mastering_metadata (mux, videocontext);
2817 gst_ebml_write_master_finish (ebml, master);
2821 * gst_matroska_mux_track_header:
2822 * @mux: #GstMatroskaMux
2823 * @context: Tack context.
2825 * Write a track header.
2828 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2829 GstMatroskaTrackContext * context)
2831 GstEbmlWrite *ebml = mux->ebml_write;
2834 /* TODO: check if everything necessary is written and check default values */
2836 /* track type goes before the type-specific stuff */
2837 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2838 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2840 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2841 if (context->default_duration) {
2842 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2843 context->default_duration);
2845 if (context->language) {
2846 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2850 /* FIXME: until we have a nice way of getting the codecname
2851 * out of the caps, I'm not going to enable this. Too much
2852 * (useless, double, boring) work... */
2853 /* TODO: Use value from tags if any */
2854 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2855 context->codec_name); */
2856 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2858 /* type-specific stuff */
2859 switch (context->type) {
2860 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2861 GstMatroskaTrackVideoContext *videocontext =
2862 (GstMatroskaTrackVideoContext *) context;
2864 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2865 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2866 videocontext->pixel_width);
2867 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2868 videocontext->pixel_height);
2869 if (videocontext->display_width && videocontext->display_height) {
2870 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2871 videocontext->display_width);
2872 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2873 videocontext->display_height);
2875 switch (videocontext->interlace_mode) {
2876 case GST_MATROSKA_INTERLACE_MODE_INTERLACED:
2877 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2879 case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE:
2880 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 2);
2886 if (videocontext->fourcc) {
2887 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2889 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2890 (gpointer) & fcc_le, 4);
2892 gst_matroska_mux_write_colour (mux, videocontext);
2893 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2894 guint64 stereo_mode = 0;
2896 switch (videocontext->multiview_mode) {
2897 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2899 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2900 if (videocontext->multiview_flags &
2901 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2902 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2904 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2906 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2907 if (videocontext->multiview_flags &
2908 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2909 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2911 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2913 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2914 if (videocontext->multiview_flags &
2915 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2916 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2918 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2920 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2921 if (videocontext->multiview_flags &
2922 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2923 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2925 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2926 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2927 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2928 GST_FIXME_OBJECT (mux,
2929 "Frame-by-frame stereoscopic mode not fully implemented");
2932 GST_WARNING_OBJECT (mux,
2933 "Multiview mode %d not supported in Matroska/WebM",
2934 videocontext->multiview_mode);
2938 if (stereo_mode != 0)
2939 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2942 gst_ebml_write_master_finish (ebml, master);
2947 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2948 GstMatroskaTrackAudioContext *audiocontext =
2949 (GstMatroskaTrackAudioContext *) context;
2951 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2952 if (audiocontext->samplerate != 8000)
2953 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2954 audiocontext->samplerate);
2955 if (audiocontext->channels != 1)
2956 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2957 audiocontext->channels);
2958 if (audiocontext->bitdepth) {
2959 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2960 audiocontext->bitdepth);
2963 gst_ebml_write_master_finish (ebml, master);
2968 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2972 /* doesn't need type-specific data */
2976 GST_DEBUG_OBJECT (mux, "Wrote track header. Codec %s", context->codec_id);
2978 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2979 if (context->codec_priv)
2980 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2981 context->codec_priv, context->codec_priv_size);
2983 if (context->seek_preroll) {
2984 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2985 context->seek_preroll);
2988 if (context->codec_delay) {
2989 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2990 context->codec_delay);
2995 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2997 guint64 title_master;
3000 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
3002 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
3003 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
3004 GST_MATROSKA_MUX_CHAPLANG);
3006 gst_ebml_write_master_finish (ebml, title_master);
3009 static GstTocEntry *
3010 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
3011 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
3012 guint64 * master_edition)
3014 guint64 master_chapteratom;
3021 GstTocEntry *internal_chapter, *internal_nested;
3024 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
3026 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
3028 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
3029 /* create uid for the parent */
3031 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
3033 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID,
3034 g_ascii_strtoull (gst_toc_entry_get_uid (edition), NULL, 10));
3035 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
3036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
3037 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
3040 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
3041 tags = gst_toc_entry_get_tags (entry);
3043 tags = gst_tag_list_copy (tags);
3046 /* build internal chapter */
3047 uid = gst_matroska_mux_create_uid (mux);
3048 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
3049 internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
3051 /* Write the chapter entry */
3052 master_chapteratom =
3053 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
3055 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
3056 /* Store the user provided UID in the ChapterStringUID */
3057 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPTERSTRINGUID,
3058 gst_toc_entry_get_uid (entry));
3059 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
3060 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
3061 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
3062 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
3064 /* write current ChapterDisplays before the nested chapters */
3065 if (G_LIKELY (tags != NULL)) {
3066 count = gst_tag_list_get_tag_size (tags, GST_TAG_TITLE);
3068 for (i = 0; i < count; ++i) {
3069 gst_tag_list_get_string_index (tags, GST_TAG_TITLE, i, &title);
3070 /* FIXME: handle ChapterLanguage entries */
3071 gst_matroska_mux_write_chapter_title (title, ebml);
3075 /* remove title tag */
3076 if (G_LIKELY (count > 0))
3077 gst_tag_list_remove_tag (tags, GST_TAG_TITLE);
3079 gst_toc_entry_set_tags (internal_chapter, tags);
3082 /* Write nested chapters */
3083 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3085 internal_nested = gst_matroska_mux_write_chapter (mux, NULL, cur->data,
3088 gst_toc_entry_append_sub_entry (internal_chapter, internal_nested);
3091 gst_ebml_write_master_finish (ebml, master_chapteratom);
3093 return internal_chapter;
3096 static GstTocEntry *
3097 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
3098 GstTocEntry * edition, GList * chapters, GstEbmlWrite * ebml,
3099 guint64 * master_chapters)
3101 guint64 master_edition = 0;
3104 GstTocEntry *internal_edition, *internal_chapter;
3105 GstTagList *tags = NULL;
3107 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT,
3108 gst_matroska_mux_create_uid (mux));
3110 if (edition != NULL) {
3111 /* Edition entry defined, get its tags */
3112 tags = gst_toc_entry_get_tags (edition);
3114 tags = gst_tag_list_copy (tags);
3118 internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
3120 gst_toc_entry_set_tags (internal_edition, tags);
3123 for (cur = g_list_first (chapters); cur != NULL; cur = cur->next) {
3124 internal_chapter = gst_matroska_mux_write_chapter (mux, internal_edition,
3125 cur->data, ebml, master_chapters, &master_edition);
3127 gst_toc_entry_append_sub_entry (internal_edition, internal_chapter);
3130 if (G_LIKELY (master_edition != 0))
3131 gst_ebml_write_master_finish (ebml, master_edition);
3133 return internal_edition;
3137 * gst_matroska_mux_start:
3138 * @mux: #GstMatroskaMux
3140 * Start a new matroska file (write headers etc...)
3143 gst_matroska_mux_start (GstMatroskaMux * mux, GstMatroskaPad * first_pad,
3144 GstBuffer * first_pad_buf)
3146 GstEbmlWrite *ebml = mux->ebml_write;
3147 const gchar *doctype;
3148 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
3149 GST_MATROSKA_ID_TRACKS,
3150 GST_MATROSKA_ID_CHAPTERS,
3151 GST_MATROSKA_ID_CUES,
3152 GST_MATROSKA_ID_TAGS,
3155 const gchar *media_type;
3156 gboolean audio_only;
3157 guint64 master, child;
3161 GstClockTime earliest_time = GST_CLOCK_TIME_NONE;
3162 GstClockTime duration = 0;
3163 guint32 segment_uid[4];
3168 /* if not streaming, check if downstream is seekable */
3169 if (!mux->ebml_write->streamable) {
3173 query = gst_query_new_seeking (GST_FORMAT_BYTES);
3174 if (gst_pad_peer_query (mux->srcpad, query)) {
3175 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
3176 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
3178 /* assume seeking is not supported if query not handled downstream */
3179 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
3183 mux->ebml_write->streamable = TRUE;
3184 g_object_notify (G_OBJECT (mux), "streamable");
3185 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
3186 "streamable=false. Will ignore that and create streamable output "
3189 gst_query_unref (query);
3192 /* stream-start (FIXME: create id based on input ids) */
3193 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
3194 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
3197 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
3199 media_type = (audio_only) ? "audio/webm" : "video/webm";
3201 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
3203 ebml->caps = gst_caps_new_empty_simple (media_type);
3204 gst_pad_set_caps (mux->srcpad, ebml->caps);
3205 /* we start with a EBML header */
3206 doctype = mux->doctype;
3207 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
3208 doctype, mux->doctype_version);
3209 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
3211 /* the rest of the header is cached */
3212 gst_ebml_write_set_cache (ebml, 0x1000);
3214 /* start a segment */
3216 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
3217 mux->segment_master = ebml->pos;
3219 if (!mux->ebml_write->streamable) {
3220 /* seekhead (table of contents) - we set the positions later */
3221 mux->seekhead_pos = ebml->pos;
3222 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
3223 for (i = 0; seekhead_id[i] != 0; i++) {
3224 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
3225 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
3226 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
3227 gst_ebml_write_master_finish (ebml, child);
3229 gst_ebml_write_master_finish (ebml, master);
3232 if (mux->ebml_write->streamable) {
3233 const GstTagList *tags;
3234 gboolean has_main_tags;
3237 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3238 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3240 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
3241 guint64 master_tags, master_tag;
3243 GST_DEBUG_OBJECT (mux, "Writing tags");
3245 mux->tags_pos = ebml->pos;
3246 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3247 if (has_main_tags) {
3248 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3249 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3250 gst_ebml_write_master_finish (ebml, master_tag);
3252 gst_matroska_mux_write_streams_tags (mux);
3253 gst_ebml_write_master_finish (ebml, master_tags);
3258 mux->info_pos = ebml->pos;
3259 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
3261 /* WebM does not support SegmentUID field on SegmentInfo */
3262 if (!mux->is_webm) {
3263 for (i = 0; i < 4; i++) {
3264 segment_uid[i] = g_random_int ();
3266 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
3267 (guint8 *) segment_uid, 16);
3270 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
3271 mux->duration_pos = ebml->pos;
3273 if (!mux->ebml_write->streamable) {
3274 for (collected = mux->collect->data; collected;
3275 collected = g_slist_next (collected)) {
3276 GstMatroskaPad *collect_pad;
3278 gint64 trackduration;
3280 collect_pad = (GstMatroskaPad *) collected->data;
3281 thepad = collect_pad->collect.pad;
3283 /* Query the total length of the track. */
3284 GST_DEBUG_OBJECT (thepad, "querying peer duration");
3285 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
3286 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
3287 GST_TIME_ARGS (trackduration));
3288 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
3289 duration = (GstClockTime) trackduration;
3293 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3294 gst_guint64_to_gdouble (duration) /
3295 gst_guint64_to_gdouble (mux->time_scale));
3297 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
3298 "GStreamer matroskamux version " PACKAGE_VERSION);
3299 if (mux->writing_app && mux->writing_app[0]) {
3300 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
3302 if (mux->creation_time != NULL) {
3303 time = g_date_time_to_unix (mux->creation_time) * GST_SECOND;
3304 time += g_date_time_get_microsecond (mux->creation_time) * GST_USECOND;
3306 time = g_get_real_time () * GST_USECOND;
3308 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time);
3309 gst_ebml_write_master_finish (ebml, master);
3312 mux->tracks_pos = ebml->pos;
3313 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
3315 for (collected = mux->collect->data; collected;
3316 collected = g_slist_next (collected)) {
3317 GstMatroskaPad *collect_pad;
3320 collect_pad = (GstMatroskaPad *) collected->data;
3322 /* This will cause an error at a later time */
3323 if (collect_pad->track->codec_id == NULL)
3326 /* Find the smallest timestamp so we can offset all streams by this to
3328 if (mux->offset_to_zero) {
3331 if (collect_pad == first_pad)
3332 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3334 buf = gst_collect_pads_peek (mux->collect, collected->data);
3337 ts = gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3339 if (earliest_time == GST_CLOCK_TIME_NONE)
3341 else if (ts != GST_CLOCK_TIME_NONE && ts < earliest_time)
3346 gst_buffer_unref (buf);
3349 /* For audio tracks, use the first buffers duration as the default
3350 * duration if we didn't get any better idea from the caps event already
3352 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO &&
3353 collect_pad->track->default_duration == 0) {
3354 if (collect_pad == first_pad)
3355 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3357 buf = gst_collect_pads_peek (mux->collect, collected->data);
3359 if (buf && GST_BUFFER_DURATION_IS_VALID (buf))
3360 collect_pad->track->default_duration =
3361 GST_BUFFER_DURATION (buf) + collect_pad->track->codec_delay;
3363 gst_buffer_unref (buf);
3366 collect_pad->track->num = tracknum++;
3367 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
3368 gst_matroska_mux_track_header (mux, collect_pad->track);
3369 gst_ebml_write_master_finish (ebml, child);
3370 /* some remaining pad/track setup */
3371 collect_pad->default_duration_scaled =
3372 gst_util_uint64_scale (collect_pad->track->default_duration,
3373 1, mux->time_scale);
3375 gst_ebml_write_master_finish (ebml, master);
3377 mux->earliest_time = earliest_time == GST_CLOCK_TIME_NONE ? 0 : earliest_time;
3380 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3381 if (toc != NULL && !mux->ebml_write->streamable) {
3382 guint64 master_chapters = 0;
3383 GstTocEntry *internal_edition;
3384 GList *cur, *chapters;
3386 GST_DEBUG ("Writing chapters");
3388 /* There are two UIDs for Chapters:
3389 * - The ChapterUID is a mandatory unsigned integer which internally
3390 * refers to a given chapter. Except for the title & language which use
3391 * dedicated fields, this UID can also be used to add tags to the Chapter.
3392 * The tags come in a separate section of the container.
3393 * - The ChapterStringUID is an optional UTF-8 string which also uniquely
3394 * refers to a chapter but from an external perspective. It can act as a
3395 * "WebVTT cue identifier" which "can be used to reference a specific cue,
3396 * for example from script or CSS".
3398 * The ChapterUID will be generated and checked for unicity, while the
3399 * ChapterStringUID will receive the user defined UID.
3401 * In order to be able to refer to chapters from the tags section,
3402 * we must maintain an internal Toc tree with the generated ChapterUID
3403 * (see gst_matroska_mux_write_toc_entry_tags) */
3405 /* Check whether we have editions or chapters at the root level. */
3406 cur = gst_toc_get_entries (toc);
3408 mux->chapters_pos = ebml->pos;
3410 mux->internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
3412 if (gst_toc_entry_get_entry_type (cur->data) ==
3413 GST_TOC_ENTRY_TYPE_EDITION) {
3414 /* Editions at the root level */
3415 for (; cur != NULL; cur = cur->next) {
3416 chapters = gst_toc_entry_get_sub_entries (cur->data);
3417 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3418 cur->data, chapters, ebml, &master_chapters);
3419 gst_toc_append_entry (mux->internal_toc, internal_edition);
3422 /* Chapters at the root level */
3423 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3424 NULL, cur, ebml, &master_chapters);
3425 gst_toc_append_entry (mux->internal_toc, internal_edition);
3428 /* close master element if any edition was written */
3429 if (G_LIKELY (master_chapters != 0))
3430 gst_ebml_write_master_finish (ebml, master_chapters);
3434 /* lastly, flush the cache */
3435 gst_ebml_write_flush_cache (ebml, FALSE, 0);
3438 gst_toc_unref (toc);
3441 /* TODO: more sensible tag mappings */
3444 const gchar *matroska_tagname;
3445 const gchar *gstreamer_tagname;
3447 gst_matroska_tag_conv[] = {
3449 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
3450 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
3451 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
3452 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
3453 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
3454 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
3455 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
3456 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
3457 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
3458 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
3459 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
3460 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
3461 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
3462 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
3463 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
3466 /* Every stagefright implementation on android up to and including 6.0.1 is using
3467 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
3468 so before outputting tags and tag elements we better make sure that there are
3469 actually tags we are going to write */
3471 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3474 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3475 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3477 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3478 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3479 if (strcmp (tagname_gst, tag) == 0) {
3480 GValue src = { 0, };
3483 if (!gst_tag_list_copy_value (&src, list, tag))
3485 dest = gst_value_serialize (&src);
3487 g_value_unset (&src);
3499 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3502 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3504 guint64 simpletag_master;
3506 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3507 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3508 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3510 if (strcmp (tagname_gst, tag) == 0) {
3511 GValue src = { 0, };
3514 if (!gst_tag_list_copy_value (&src, list, tag))
3516 if ((dest = gst_value_serialize (&src))) {
3518 simpletag_master = gst_ebml_write_master_start (ebml,
3519 GST_MATROSKA_ID_SIMPLETAG);
3520 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3521 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3522 gst_ebml_write_master_finish (ebml, simpletag_master);
3525 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3527 g_value_unset (&src);
3534 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3536 guint64 master_tag, master_targets;
3539 ebml = mux->ebml_write;
3541 if (G_UNLIKELY (mpad->tags == NULL
3542 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3545 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3546 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3548 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3550 gst_ebml_write_master_finish (ebml, master_targets);
3551 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3552 gst_ebml_write_master_finish (ebml, master_tag);
3556 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3560 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3561 GstMatroskaPad *collect_pad;
3563 collect_pad = (GstMatroskaPad *) walk->data;
3565 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3570 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3574 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3575 GstMatroskaPad *collect_pad;
3577 collect_pad = (GstMatroskaPad *) walk->data;
3578 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3585 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3586 const GstTocEntry * entry, guint64 * master_tags, gboolean * has_tags)
3588 guint64 master_tag, master_targets;
3591 const GstTagList *tags;
3593 ebml = mux->ebml_write;
3595 tags = gst_toc_entry_get_tags (entry);
3596 if (G_UNLIKELY (tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags))) {
3599 if (*master_tags == 0) {
3600 mux->tags_pos = ebml->pos;
3601 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3604 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3606 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3608 if (gst_toc_entry_get_entry_type (entry) == GST_TOC_ENTRY_TYPE_EDITION)
3609 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3610 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3612 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3613 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3615 gst_ebml_write_master_finish (ebml, master_targets);
3616 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3617 gst_ebml_write_master_finish (ebml, master_tag);
3620 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3622 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags,
3628 * gst_matroska_mux_finish:
3629 * @mux: #GstMatroskaMux
3631 * Finish a new matroska file (write index etc...)
3634 gst_matroska_mux_finish (GstMatroskaMux * mux)
3636 GstEbmlWrite *ebml = mux->ebml_write;
3638 guint64 duration = 0;
3640 const GstTagList *tags, *toc_tags;
3642 gboolean has_main_tags, toc_has_tags = FALSE;
3645 /* finish last cluster */
3647 gst_ebml_write_master_finish (ebml, mux->cluster);
3651 if (mux->index != NULL) {
3653 guint64 master, pointentry_master, trackpos_master;
3655 mux->cues_pos = ebml->pos;
3656 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3657 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3659 for (n = 0; n < mux->num_indexes; n++) {
3660 GstMatroskaIndex *idx = &mux->index[n];
3662 pointentry_master = gst_ebml_write_master_start (ebml,
3663 GST_MATROSKA_ID_POINTENTRY);
3664 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3665 idx->time / mux->time_scale);
3666 trackpos_master = gst_ebml_write_master_start (ebml,
3667 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3668 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3669 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3670 idx->pos - mux->segment_master);
3671 gst_ebml_write_master_finish (ebml, trackpos_master);
3672 gst_ebml_write_master_finish (ebml, pointentry_master);
3675 gst_ebml_write_master_finish (ebml, master);
3676 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3680 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3681 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3682 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3684 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux) || toc != NULL) {
3685 guint64 master_tags = 0, master_tag;
3687 GST_DEBUG_OBJECT (mux, "Writing tags");
3689 if (has_main_tags) {
3690 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3691 mux->tags_pos = ebml->pos;
3692 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3693 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3696 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3697 if (mux->internal_toc != NULL) {
3698 toc_tags = gst_toc_get_tags (mux->internal_toc);
3699 toc_has_tags = (toc_tags != NULL);
3700 gst_tag_list_foreach (toc_tags, gst_matroska_mux_write_simple_tag,
3704 gst_ebml_write_master_finish (ebml, master_tag);
3707 if (mux->internal_toc != NULL) {
3708 for (cur = gst_toc_get_entries (mux->internal_toc); cur != NULL;
3710 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags,
3715 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3716 mux->tags_pos = ebml->pos;
3717 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3719 gst_matroska_mux_write_streams_tags (mux);
3721 if (master_tags != 0)
3722 gst_ebml_write_master_finish (ebml, master_tags);
3725 /* update seekhead. We know that:
3726 * - a seekhead contains 5 entries.
3727 * - order of entries is as above.
3728 * - a seekhead has a 4-byte header + 8-byte length
3729 * - each entry is 2-byte master, 2-byte ID pointer,
3730 * 2-byte length pointer, all 8/1-byte length, 4-
3731 * byte ID and 8-byte length pointer, where the
3732 * length pointer starts at 20.
3733 * - all entries are local to the segment (so pos - segment_master).
3734 * - so each entry is at 12 + 20 + num * 28. */
3735 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3736 mux->info_pos - mux->segment_master);
3737 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3738 mux->tracks_pos - mux->segment_master);
3739 if (toc != NULL && mux->chapters_pos > 0) {
3740 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3741 mux->chapters_pos - mux->segment_master);
3744 guint64 my_pos = ebml->pos;
3746 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3747 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3748 gst_ebml_write_seek (ebml, my_pos);
3750 if (mux->index != NULL) {
3751 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3752 mux->cues_pos - mux->segment_master);
3755 guint64 my_pos = ebml->pos;
3757 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3758 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3759 gst_ebml_write_seek (ebml, my_pos);
3762 if (mux->tags_pos != 0 || toc_has_tags) {
3763 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3764 mux->tags_pos - mux->segment_master);
3767 guint64 my_pos = ebml->pos;
3769 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3770 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3771 gst_ebml_write_seek (ebml, my_pos);
3775 gst_toc_unref (toc);
3779 * - first get the overall duration
3780 * (a released track may have left a duration in here)
3781 * - write some track header data for subtitles
3783 duration = mux->duration;
3785 for (collected = mux->collect->data; collected;
3786 collected = g_slist_next (collected)) {
3787 GstMatroskaPad *collect_pad;
3789 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3790 * since this means buffer without timestamps that is not possible
3792 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3794 collect_pad = (GstMatroskaPad *) collected->data;
3796 GST_DEBUG_OBJECT (mux,
3797 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3798 " end ts %" GST_TIME_FORMAT, collect_pad,
3799 GST_TIME_ARGS (collect_pad->start_ts),
3800 GST_TIME_ARGS (collect_pad->end_ts));
3802 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3803 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3804 collected_duration =
3805 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3806 GST_DEBUG_OBJECT (collect_pad->collect.pad,
3807 "final track duration: %" GST_TIME_FORMAT,
3808 GST_TIME_ARGS (collected_duration));
3810 GST_WARNING_OBJECT (collect_pad->collect.pad,
3811 "unable to get final track duration");
3813 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3814 duration < collected_duration)
3815 duration = collected_duration;
3819 /* seek back (optional, but do anyway) */
3820 gst_ebml_write_seek (ebml, pos);
3822 /* update duration */
3823 if (duration != 0) {
3824 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3825 GST_TIME_ARGS (duration));
3826 pos = mux->ebml_write->pos;
3827 gst_ebml_write_seek (ebml, mux->duration_pos);
3828 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3829 gst_guint64_to_gdouble (duration) /
3830 gst_guint64_to_gdouble (mux->time_scale));
3831 gst_ebml_write_seek (ebml, pos);
3834 guint64 my_pos = ebml->pos;
3836 gst_ebml_write_seek (ebml, mux->duration_pos);
3837 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3838 gst_ebml_write_seek (ebml, my_pos);
3840 GST_DEBUG_OBJECT (mux, "finishing segment");
3841 /* finish segment - this also writes element length */
3842 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3846 * gst_matroska_mux_buffer_header:
3847 * @track: Track context.
3848 * @relative_timestamp: relative timestamp of the buffer
3849 * @flags: Buffer flags.
3851 * Create a buffer containing buffer header.
3853 * Returns: New buffer.
3856 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3857 gint16 relative_timestamp, int flags)
3860 guint8 *data = g_malloc (4);
3862 hdr = gst_buffer_new_wrapped (data, 4);
3863 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3864 data[0] = track->num | 0x80;
3865 /* time relative to clustertime */
3866 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3874 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3875 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3876 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3879 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3880 GstMatroskaPad * collect_pad, GstBuffer * buf)
3882 GstMatroskaTrackVideoContext *ctx =
3883 (GstMatroskaTrackVideoContext *) collect_pad->track;
3888 guint32 next_parse_offset;
3889 GstBuffer *ret = NULL;
3890 gboolean is_muxing_unit = FALSE;
3892 gst_buffer_map (buf, &map, GST_MAP_READ);
3897 gst_buffer_unmap (buf, &map);
3898 gst_buffer_unref (buf);
3902 /* Check if this buffer contains a picture or end-of-sequence packet */
3903 while (size >= 13) {
3904 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3905 gst_buffer_unmap (buf, &map);
3906 gst_buffer_unref (buf);
3910 parse_code = GST_READ_UINT8 (data + 4);
3911 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3912 if (ctx->dirac_unit) {
3913 gst_buffer_unref (ctx->dirac_unit);
3914 ctx->dirac_unit = NULL;
3916 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3917 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3918 is_muxing_unit = TRUE;
3922 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3924 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3927 data += next_parse_offset;
3928 size -= next_parse_offset;
3931 if (ctx->dirac_unit)
3932 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3934 ctx->dirac_unit = gst_buffer_ref (buf);
3936 gst_buffer_unmap (buf, &map);
3938 if (is_muxing_unit) {
3939 ret = gst_buffer_make_writable (ctx->dirac_unit);
3940 ctx->dirac_unit = NULL;
3941 gst_buffer_copy_into (ret, buf,
3942 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3943 gst_buffer_unref (buf);
3945 gst_buffer_unref (buf);
3953 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3957 GValue streamheader = { 0 };
3958 GValue bufval = { 0 };
3959 GstBuffer *streamheader_buffer;
3960 GstEbmlWrite *ebml = mux->ebml_write;
3962 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3963 caps = gst_caps_copy (mux->ebml_write->caps);
3964 s = gst_caps_get_structure (caps, 0);
3965 g_value_init (&streamheader, GST_TYPE_ARRAY);
3966 g_value_init (&bufval, GST_TYPE_BUFFER);
3967 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3968 gst_value_set_buffer (&bufval, streamheader_buffer);
3969 gst_value_array_append_value (&streamheader, &bufval);
3970 g_value_unset (&bufval);
3971 gst_structure_set_value (s, "streamheader", &streamheader);
3972 g_value_unset (&streamheader);
3973 gst_caps_replace (&ebml->caps, caps);
3974 gst_buffer_unref (streamheader_buffer);
3975 gst_pad_set_caps (mux->srcpad, caps);
3976 gst_caps_unref (caps);
3980 * gst_matroska_mux_write_data:
3981 * @mux: #GstMatroskaMux
3982 * @collect_pad: #GstMatroskaPad with the data
3984 * Write collected data (called from gst_matroska_mux_collected).
3986 * Returns: Result of the gst_pad_push issued to write the data.
3988 static GstFlowReturn
3989 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3992 GstEbmlWrite *ebml = mux->ebml_write;
3995 gboolean write_duration;
3996 guint64 cluster_time_scaled;
3997 gint16 relative_timestamp;
3998 gint64 relative_timestamp64;
3999 guint64 block_duration, duration_diff = 0;
4000 gboolean is_video_keyframe = FALSE;
4001 gboolean is_video_invisible = FALSE;
4002 gboolean is_audio_only = FALSE;
4003 gboolean is_min_duration_reached = FALSE;
4004 gboolean is_max_duration_exceeded = FALSE;
4005 GstMatroskamuxPad *pad;
4007 GstClockTime buffer_timestamp;
4008 GstAudioClippingMeta *cmeta = NULL;
4011 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
4013 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
4014 if (collect_pad->track->xiph_headers_to_skip > 0) {
4015 --collect_pad->track->xiph_headers_to_skip;
4016 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_HEADER)) {
4017 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
4018 gst_buffer_unref (buf);
4023 /* for dirac we have to queue up everything up to a picture unit */
4024 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) {
4025 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
4028 } else if (!strcmp (collect_pad->track->codec_id,
4029 GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
4030 /* Remove the 'Frame container atom' header' */
4031 buf = gst_buffer_make_writable (buf);
4032 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
4036 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
4037 if (buffer_timestamp >= mux->earliest_time) {
4038 buffer_timestamp -= mux->earliest_time;
4040 buffer_timestamp = 0;
4043 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
4044 * this would wreak havoc with time stored in matroska file */
4045 /* TODO: maybe calculate a timestamp by using the previous timestamp
4046 * and default duration */
4047 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4048 GST_WARNING_OBJECT (collect_pad->collect.pad,
4049 "Invalid buffer timestamp; dropping buffer");
4050 gst_buffer_unref (buf);
4054 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4055 && collect_pad->track->codec_delay) {
4056 /* All timestamps should include the codec delay */
4057 if (buffer_timestamp > collect_pad->track->codec_delay) {
4058 buffer_timestamp += collect_pad->track->codec_delay;
4060 buffer_timestamp = 0;
4061 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
4065 /* set the timestamp for outgoing buffers */
4066 ebml->timestamp = buffer_timestamp;
4068 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
4069 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
4070 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
4071 GST_TIME_ARGS (buffer_timestamp));
4072 is_video_keyframe = TRUE;
4073 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
4074 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
4075 || !strcmp (collect_pad->track->codec_id,
4076 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
4077 GST_LOG_OBJECT (mux,
4078 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
4079 GST_TIME_ARGS (buffer_timestamp));
4080 is_video_invisible = TRUE;
4084 /* From this point on we use the buffer_timestamp to do cluster and other
4085 * related arithmetic, so apply the timestamp offset if we have one */
4086 buffer_timestamp += mux->cluster_timestamp_offset;
4088 is_audio_only = (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
4089 (mux->num_streams == 1);
4090 is_min_duration_reached = (mux->min_cluster_duration == 0
4091 || (buffer_timestamp > mux->cluster_time
4092 && (buffer_timestamp - mux->cluster_time) >=
4093 mux->min_cluster_duration));
4094 is_max_duration_exceeded = (mux->max_cluster_duration > 0
4095 && buffer_timestamp > mux->cluster_time
4096 && (buffer_timestamp - mux->cluster_time) >=
4097 MIN (G_MAXINT16 * mux->time_scale, mux->max_cluster_duration));
4100 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
4101 * or when we may be reaching the limit of the relative timestamp */
4102 if (is_max_duration_exceeded || (is_video_keyframe
4103 && is_min_duration_reached) || mux->force_key_unit_event
4104 || (is_audio_only && is_min_duration_reached)) {
4105 if (!mux->ebml_write->streamable)
4106 gst_ebml_write_master_finish (ebml, mux->cluster);
4108 /* Forward the GstForceKeyUnit event after finishing the cluster */
4109 if (mux->force_key_unit_event) {
4110 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
4111 mux->force_key_unit_event = NULL;
4113 cluster_time_scaled =
4114 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4116 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
4117 mux->cluster_pos = ebml->pos;
4118 gst_ebml_write_set_cache (ebml, 0x20);
4120 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4121 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4122 cluster_time_scaled);
4123 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
4124 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
4125 gst_ebml_write_flush_cache (ebml, is_video_keyframe
4126 || is_audio_only, buffer_timestamp);
4127 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
4128 mux->prev_cluster_size);
4129 /* cluster_time needs to be identical in value to what's stored in the
4130 * matroska so we need to have it with the same precision as what's
4131 * possible with the set timecodescale rather than just using the
4133 * If this is not done the rounding of relative_timestamp will be
4134 * incorrect and possibly making the timestamps get out of order if tw
4135 * buffers arrive at the same millisecond (assuming default timecodescale
4138 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4142 cluster_time_scaled =
4143 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale);
4144 mux->cluster_pos = ebml->pos;
4145 gst_ebml_write_set_cache (ebml, 0x20);
4146 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
4147 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
4148 cluster_time_scaled);
4149 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
4150 /* cluster_time needs to be identical in value to what's stored in the
4151 * matroska so we need to have it with the same precision as what's
4152 * possible with the set timecodescale rather than just using the
4154 * If this is not done the rounding of relative_timestamp will be
4155 * incorrect and possibly making the timestamps get out of order if tw
4156 * buffers arrive at the same millisecond (assuming default timecodescale
4159 gst_util_uint64_scale (cluster_time_scaled, mux->time_scale, 1);
4162 /* We currently write index entries for all video tracks or for the audio
4163 * track in a single-track audio file. This could be improved by keeping the
4164 * index only for the *first* video track. */
4166 /* TODO: index is useful for every track, should contain the number of
4167 * the block in the cluster which contains the timestamp, should also work
4168 * for files with multiple audio tracks.
4170 if (!mux->ebml_write->streamable && (is_video_keyframe || is_audio_only)) {
4173 if (mux->min_index_interval != 0) {
4174 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
4175 if (mux->index[last_idx].track == collect_pad->track->num)
4180 if (last_idx < 0 || mux->min_index_interval == 0 ||
4181 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
4182 >= mux->min_index_interval)) {
4183 GstMatroskaIndex *idx;
4185 if (mux->num_indexes % 32 == 0) {
4186 mux->index = g_renew (GstMatroskaIndex, mux->index,
4187 mux->num_indexes + 32);
4189 idx = &mux->index[mux->num_indexes++];
4191 idx->pos = mux->cluster_pos;
4192 idx->time = buffer_timestamp;
4193 idx->track = collect_pad->track->num;
4197 /* Check if the duration differs from the default duration. */
4198 write_duration = FALSE;
4200 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
4201 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
4202 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
4204 /* small difference should be ok. */
4205 if (block_duration > collect_pad->default_duration_scaled + 1 ||
4206 block_duration < collect_pad->default_duration_scaled - 1) {
4207 write_duration = TRUE;
4211 /* write the block, for doctype v2 use SimpleBlock if possible
4212 * one slice (*breath*).
4213 * FIXME: Need to do correct lacing! */
4214 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
4215 if (relative_timestamp64 >= 0) {
4216 /* round the timestamp */
4217 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
4218 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
4221 /* round the timestamp */
4222 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
4223 relative_timestamp =
4224 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
4228 if (is_video_invisible)
4231 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
4232 cmeta = gst_buffer_get_audio_clipping_meta (buf);
4233 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
4235 /* Start clipping is done via header and CodecDelay */
4236 if (cmeta && !cmeta->end)
4240 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
4241 if (is_video_keyframe)
4245 gst_matroska_mux_create_buffer_header (collect_pad->track,
4246 relative_timestamp, flags);
4247 gst_ebml_write_set_cache (ebml, 0x40);
4248 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
4249 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4250 gst_ebml_write_buffer (ebml, hdr);
4251 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4252 gst_ebml_write_buffer (ebml, buf);
4254 return gst_ebml_last_write_result (ebml);
4256 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
4257 /* write and call order slightly unnatural,
4258 * but avoids seek and minizes pushing */
4259 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
4261 gst_matroska_mux_create_buffer_header (collect_pad->track,
4262 relative_timestamp, flags);
4264 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
4266 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4268 /* Start clipping is done via header and CodecDelay */
4271 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
4272 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
4276 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
4277 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4278 gst_ebml_write_buffer (ebml, hdr);
4279 gst_ebml_write_master_finish_full (ebml, blockgroup,
4280 gst_buffer_get_size (buf));
4281 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4282 gst_ebml_write_buffer (ebml, buf);
4284 return gst_ebml_last_write_result (ebml);
4289 * gst_matroska_mux_handle_buffer:
4290 * @pads: #GstCollectPads
4291 * @uuser_data: #GstMatroskaMux
4293 * Collectpads callback.
4295 * Returns: #GstFlowReturn
4297 static GstFlowReturn
4298 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
4299 GstBuffer * buf, gpointer user_data)
4301 GstClockTime buffer_timestamp;
4302 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
4303 GstEbmlWrite *ebml = mux->ebml_write;
4304 GstMatroskaPad *best = (GstMatroskaPad *) data;
4305 GstFlowReturn ret = GST_FLOW_OK;
4306 GST_DEBUG_OBJECT (mux, "Collected pads");
4308 /* start with a header */
4309 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
4310 if (mux->collect->data == NULL) {
4311 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
4312 ("No input streams configured"));
4313 return GST_FLOW_ERROR;
4315 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
4316 gst_ebml_start_streamheader (ebml);
4317 gst_matroska_mux_start (mux, best, buf);
4318 gst_matroska_mux_stop_streamheader (mux);
4319 mux->state = GST_MATROSKA_MUX_STATE_DATA;
4322 /* if there is no best pad, we have reached EOS */
4324 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
4325 if (!mux->ebml_write->streamable) {
4326 gst_matroska_mux_finish (mux);
4328 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
4330 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
4335 if (best->track->codec_id == NULL) {
4336 GST_ERROR_OBJECT (best->collect.pad, "No codec-id for pad");
4337 ret = GST_FLOW_NOT_NEGOTIATED;
4341 /* if we have a best stream, should also have a buffer */
4344 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
4345 if (buffer_timestamp >= mux->earliest_time) {
4346 buffer_timestamp -= mux->earliest_time;
4348 GST_ERROR_OBJECT (mux,
4349 "PTS before first PTS (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
4350 GST_TIME_ARGS (buffer_timestamp), GST_TIME_ARGS (mux->earliest_time));
4351 buffer_timestamp = 0;
4354 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
4355 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
4356 GST_TIME_ARGS (buffer_timestamp),
4357 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
4359 /* make note of first and last encountered timestamps, so we can calculate
4360 * the actual duration later when we send an updated header on eos */
4361 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4362 GstClockTime start_ts = buffer_timestamp;
4363 GstClockTime end_ts = start_ts;
4365 if (GST_BUFFER_DURATION_IS_VALID (buf))
4366 end_ts += GST_BUFFER_DURATION (buf);
4367 else if (best->track->default_duration)
4368 end_ts += best->track->default_duration;
4370 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
4371 best->end_ts = end_ts;
4373 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
4374 start_ts < best->start_ts))
4375 best->start_ts = start_ts;
4378 /* write one buffer */
4379 ret = gst_matroska_mux_write_data (mux, best, buf);
4387 * gst_matroska_mux_change_state:
4388 * @element: #GstMatroskaMux
4389 * @transition: State change transition.
4391 * Change the muxer state.
4393 * Returns: #GstStateChangeReturn
4395 static GstStateChangeReturn
4396 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
4398 GstStateChangeReturn ret;
4399 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
4401 switch (transition) {
4402 case GST_STATE_CHANGE_NULL_TO_READY:
4404 case GST_STATE_CHANGE_READY_TO_PAUSED:
4405 gst_collect_pads_start (mux->collect);
4407 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4409 case GST_STATE_CHANGE_PAUSED_TO_READY:
4410 gst_collect_pads_stop (mux->collect);
4416 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4418 switch (transition) {
4419 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4421 case GST_STATE_CHANGE_PAUSED_TO_READY:
4422 gst_matroska_mux_reset (GST_ELEMENT (mux));
4424 case GST_STATE_CHANGE_READY_TO_NULL:
4434 gst_matroska_mux_set_property (GObject * object,
4435 guint prop_id, const GValue * value, GParamSpec * pspec)
4437 GstMatroskaMux *mux;
4439 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4440 mux = GST_MATROSKA_MUX (object);
4443 case PROP_WRITING_APP:
4444 if (!g_value_get_string (value)) {
4445 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
4448 g_free (mux->writing_app);
4449 mux->writing_app = g_value_dup_string (value);
4451 case PROP_DOCTYPE_VERSION:
4452 mux->doctype_version = g_value_get_int (value);
4454 case PROP_MIN_INDEX_INTERVAL:
4455 mux->min_index_interval = g_value_get_int64 (value);
4457 case PROP_STREAMABLE:
4458 mux->ebml_write->streamable = g_value_get_boolean (value);
4460 case PROP_TIMECODESCALE:
4461 mux->time_scale = g_value_get_int64 (value);
4463 case PROP_MIN_CLUSTER_DURATION:
4464 mux->min_cluster_duration = g_value_get_int64 (value);
4466 case PROP_MAX_CLUSTER_DURATION:
4467 mux->max_cluster_duration = g_value_get_int64 (value);
4469 case PROP_OFFSET_TO_ZERO:
4470 mux->offset_to_zero = g_value_get_boolean (value);
4472 case PROP_CREATION_TIME:
4473 g_clear_pointer (&mux->creation_time, g_date_time_unref);
4474 mux->creation_time = g_value_dup_boxed (value);
4476 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4477 mux->cluster_timestamp_offset = g_value_get_uint64 (value);
4480 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4486 gst_matroska_mux_get_property (GObject * object,
4487 guint prop_id, GValue * value, GParamSpec * pspec)
4489 GstMatroskaMux *mux;
4491 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4492 mux = GST_MATROSKA_MUX (object);
4495 case PROP_WRITING_APP:
4496 g_value_set_string (value, mux->writing_app);
4498 case PROP_DOCTYPE_VERSION:
4499 g_value_set_int (value, mux->doctype_version);
4501 case PROP_MIN_INDEX_INTERVAL:
4502 g_value_set_int64 (value, mux->min_index_interval);
4504 case PROP_STREAMABLE:
4505 g_value_set_boolean (value, mux->ebml_write->streamable);
4507 case PROP_TIMECODESCALE:
4508 g_value_set_int64 (value, mux->time_scale);
4510 case PROP_MIN_CLUSTER_DURATION:
4511 g_value_set_int64 (value, mux->min_cluster_duration);
4513 case PROP_MAX_CLUSTER_DURATION:
4514 g_value_set_int64 (value, mux->max_cluster_duration);
4516 case PROP_OFFSET_TO_ZERO:
4517 g_value_set_boolean (value, mux->offset_to_zero);
4519 case PROP_CREATION_TIME:
4520 g_value_set_boxed (value, mux->creation_time);
4522 case PROP_CLUSTER_TIMESTAMP_OFFSET:
4523 g_value_set_uint64 (value, mux->cluster_timestamp_offset);
4526 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);