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>
6 *
7 * matroska-mux.c: matroska file/stream muxer
8 *
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.
13 *
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.
18 *
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.
23 */
24
25 /* TODO: - check everywhere that we don't write invalid values
26 * - make sure timestamps are correctly scaled everywhere
27 */
28
29 /**
30 * SECTION:element-matroskamux
31 *
32 * matroskamux muxes different input streams into a Matroska file.
33 *
34 * <refsect2>
35 * <title>Example launch line</title>
36 * |[
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.
39 * |[
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.
42 * </refsect2>
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <math.h>
50 #include <stdio.h>
51 #include <string.h>
52
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>
57
58 #include "matroska-mux.h"
59 #include "matroska-ids.h"
60
61 #define GST_MATROSKA_MUX_CHAPLANG "und"
62
63 GST_DEBUG_CATEGORY_STATIC (matroskamux_debug);
64 #define GST_CAT_DEFAULT matroskamux_debug
65
66 enum
67 {
68 PROP_0,
69 PROP_WRITING_APP,
70 PROP_DOCTYPE_VERSION,
71 PROP_MIN_INDEX_INTERVAL,
72 PROP_STREAMABLE,
73 PROP_TIMECODESCALE,
74 PROP_MIN_CLUSTER_DURATION,
75 PROP_MAX_CLUSTER_DURATION
76 };
77
78 #define DEFAULT_DOCTYPE_VERSION 2
79 #define DEFAULT_WRITING_APP "GStreamer Matroska muxer"
80 #define DEFAULT_MIN_INDEX_INTERVAL 0
81 #define DEFAULT_STREAMABLE FALSE
82 #define DEFAULT_TIMECODESCALE GST_MSECOND
83 #define DEFAULT_MIN_CLUSTER_DURATION 500 * GST_MSECOND
84 #define DEFAULT_MAX_CLUSTER_DURATION 65535 * GST_MSECOND
85
86 /* WAVEFORMATEX is gst_riff_strf_auds + an extra guint16 extension size */
87 #define WAVEFORMATEX_SIZE (2 + sizeof (gst_riff_strf_auds))
88
89 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
90 GST_PAD_SRC,
91 GST_PAD_ALWAYS,
92 GST_STATIC_CAPS ("video/x-matroska; video/x-matroska-3d; audio/x-matroska")
93 );
94
95 #define COMMON_VIDEO_CAPS \
96 "width = (int) [ 16, MAX ], " \
97 "height = (int) [ 16, MAX ] "
98
99 /* FIXME:
100 * * require codec data, etc as needed
101 */
102
103 static GstStaticPadTemplate videosink_templ =
104 GST_STATIC_PAD_TEMPLATE ("video_%u",
105 GST_PAD_SINK,
106 GST_PAD_REQUEST,
107 GST_STATIC_CAPS ("video/mpeg, "
108 "mpegversion = (int) { 1, 2, 4 }, "
109 "systemstream = (boolean) false, "
110 COMMON_VIDEO_CAPS "; "
111 "video/x-h264, stream-format=avc, alignment=au, "
112 COMMON_VIDEO_CAPS "; "
113 "video/x-h265, stream-format=hvc1, alignment=au, "
114 COMMON_VIDEO_CAPS "; "
115 "video/x-divx, "
116 COMMON_VIDEO_CAPS "; "
117 "video/x-huffyuv, "
118 COMMON_VIDEO_CAPS "; "
119 "video/x-dv, "
120 COMMON_VIDEO_CAPS "; "
121 "video/x-h263, "
122 COMMON_VIDEO_CAPS "; "
123 "video/x-msmpeg, "
124 COMMON_VIDEO_CAPS "; "
125 "image/jpeg, "
126 COMMON_VIDEO_CAPS "; "
127 "video/x-theora; "
128 "video/x-dirac, "
129 COMMON_VIDEO_CAPS "; "
130 "video/x-pn-realvideo, "
131 "rmversion = (int) [1, 4], "
132 COMMON_VIDEO_CAPS "; "
133 "video/x-vp8, "
134 COMMON_VIDEO_CAPS "; "
135 "video/x-vp9, "
136 COMMON_VIDEO_CAPS "; "
137 "video/x-raw, "
138 "format = (string) { YUY2, I420, YV12, UYVY, AYUV, GRAY8, BGR, RGB }, "
139 COMMON_VIDEO_CAPS "; "
140 "video/x-prores, "
141 COMMON_VIDEO_CAPS "; "
142 "video/x-wmv, " "wmvversion = (int) [ 1, 3 ], " COMMON_VIDEO_CAPS "; "
143 "video/x-av1, " COMMON_VIDEO_CAPS)
144 );
145
146 #define COMMON_AUDIO_CAPS \
147 "channels = (int) [ 1, MAX ], " \
148 "rate = (int) [ 1, MAX ]"
149
150 /* FIXME:
151 * * require codec data, etc as needed
152 */
153 static GstStaticPadTemplate audiosink_templ =
154 GST_STATIC_PAD_TEMPLATE ("audio_%u",
155 GST_PAD_SINK,
156 GST_PAD_REQUEST,
157 GST_STATIC_CAPS ("audio/mpeg, "
158 "mpegversion = (int) 1, "
159 "layer = (int) [ 1, 3 ], "
160 COMMON_AUDIO_CAPS "; "
161 "audio/mpeg, "
162 "mpegversion = (int) { 2, 4 }, "
163 "stream-format = (string) raw, "
164 COMMON_AUDIO_CAPS "; "
165 "audio/x-ac3, "
166 COMMON_AUDIO_CAPS "; "
167 "audio/x-eac3, "
168 COMMON_AUDIO_CAPS "; "
169 "audio/x-dts, "
170 COMMON_AUDIO_CAPS "; "
171 "audio/x-vorbis, "
172 COMMON_AUDIO_CAPS "; "
173 "audio/x-flac, "
174 COMMON_AUDIO_CAPS "; "
175 "audio/x-opus; "
176 "audio/x-speex, "
177 COMMON_AUDIO_CAPS "; "
178 "audio/x-raw, "
179 "format = (string) { U8, S16BE, S16LE, S24BE, S24LE, S32BE, S32LE, F32LE, F64LE }, "
180 "layout = (string) interleaved, "
181 COMMON_AUDIO_CAPS ";"
182 "audio/x-tta, "
183 "width = (int) { 8, 16, 24 }, "
184 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
185 "audio/x-pn-realaudio, "
186 "raversion = (int) { 1, 2, 8 }, " COMMON_AUDIO_CAPS "; "
187 "audio/x-wma, " "wmaversion = (int) [ 1, 3 ], "
188 "block_align = (int) [ 0, 65535 ], bitrate = (int) [ 0, 524288 ], "
189 COMMON_AUDIO_CAPS ";"
190 "audio/x-alaw, "
191 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
192 "audio/x-mulaw, "
193 "channels = (int) {1, 2}, " "rate = (int) [ 8000, 192000 ]; "
194 "audio/x-adpcm, "
195 "layout = (string)dvi, "
196 "block_align = (int)[64, 8192], "
197 "channels = (int) { 1, 2 }, " "rate = (int) [ 8000, 96000 ]; "
198 "audio/G722, "
199 "channels = (int)1," "rate = (int)16000; "
200 "audio/x-adpcm, "
201 "layout = (string)g726, " "channels = (int)1," "rate = (int)8000; ")
202 );
203
204 static GstStaticPadTemplate subtitlesink_templ =
205 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
206 GST_PAD_SINK,
207 GST_PAD_REQUEST,
208 GST_STATIC_CAPS ("subtitle/x-kate; "
209 "text/x-raw, format=utf8; application/x-ssa; application/x-ass; "
210 "application/x-usf; subpicture/x-dvd; "
211 "application/x-subtitle-unknown")
212 );
213
214 static gpointer parent_class; /* NULL */
215
216 /* Matroska muxer destructor */
217 static void gst_matroska_mux_class_init (GstMatroskaMuxClass * klass);
218 static void gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class);
219 static void gst_matroska_mux_finalize (GObject * object);
220
221 /* Pads collected callback */
222 static GstFlowReturn gst_matroska_mux_handle_buffer (GstCollectPads * pads,
223 GstCollectData * data, GstBuffer * buf, gpointer user_data);
224 static gboolean gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
225 GstCollectData * data, GstEvent * event, gpointer user_data);
226
227 /* pad functions */
228 static gboolean gst_matroska_mux_handle_src_event (GstPad * pad,
229 GstObject * parent, GstEvent * event);
230 static GstPad *gst_matroska_mux_request_new_pad (GstElement * element,
231 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
232 static void gst_matroska_mux_release_pad (GstElement * element, GstPad * pad);
233
234 /* gst internal change state handler */
235 static GstStateChangeReturn
236 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition);
237
238 /* gobject bla bla */
239 static void gst_matroska_mux_set_property (GObject * object,
240 guint prop_id, const GValue * value, GParamSpec * pspec);
241 static void gst_matroska_mux_get_property (GObject * object,
242 guint prop_id, GValue * value, GParamSpec * pspec);
243
244 /* reset muxer */
245 static void gst_matroska_mux_reset (GstElement * element);
246
247 /* uid generation */
248 static guint64 gst_matroska_mux_create_uid (GstMatroskaMux * mux);
249
250 static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
251 GstMatroskaTrackContext * context);
252 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
253 GstMatroskaTrackContext * context);
254 static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
255 GstMatroskaTrackContext * context);
256 static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
257 GstMatroskaTrackContext * context);
258 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
259 GstMatroskaTrackContext * context);
260 static void
261 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
262 gpointer data);
263 static gboolean gst_matroska_mux_tag_list_is_empty (const GstTagList * list);
264 static void gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux);
265 static gboolean gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux);
266
267 /* Cannot use boilerplate macros here because we need the full init function
268 * signature with the additional class argument, so we use the right template
269 * for the sink caps */
270 GType
gst_matroska_mux_get_type(void)271 gst_matroska_mux_get_type (void)
272 {
273 static GType object_type; /* 0 */
274
275 if (object_type == 0) {
276 static const GTypeInfo object_info = {
277 sizeof (GstMatroskaMuxClass),
278 NULL, /* base_init */
279 NULL, /* base_finalize */
280 (GClassInitFunc) gst_matroska_mux_class_init,
281 NULL, /* class_finalize */
282 NULL, /* class_data */
283 sizeof (GstMatroskaMux),
284 0, /* n_preallocs */
285 (GInstanceInitFunc) gst_matroska_mux_init
286 };
287 const GInterfaceInfo iface_info = { NULL };
288
289 object_type = g_type_register_static (GST_TYPE_ELEMENT,
290 "GstMatroskaMux", &object_info, (GTypeFlags) 0);
291
292 g_type_add_interface_static (object_type, GST_TYPE_TAG_SETTER, &iface_info);
293 g_type_add_interface_static (object_type, GST_TYPE_TOC_SETTER, &iface_info);
294 }
295
296 return object_type;
297 }
298
299 static void
gst_matroska_mux_class_init(GstMatroskaMuxClass * klass)300 gst_matroska_mux_class_init (GstMatroskaMuxClass * klass)
301 {
302 GObjectClass *gobject_class;
303 GstElementClass *gstelement_class;
304
305 gobject_class = (GObjectClass *) klass;
306 gstelement_class = (GstElementClass *) klass;
307
308 gst_element_class_add_static_pad_template (gstelement_class,
309 &videosink_templ);
310 gst_element_class_add_static_pad_template (gstelement_class,
311 &audiosink_templ);
312 gst_element_class_add_static_pad_template (gstelement_class,
313 &subtitlesink_templ);
314 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
315 gst_element_class_set_static_metadata (gstelement_class, "Matroska muxer",
316 "Codec/Muxer",
317 "Muxes video/audio/subtitle streams into a matroska stream",
318 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
319
320 GST_DEBUG_CATEGORY_INIT (matroskamux_debug, "matroskamux", 0,
321 "Matroska muxer");
322
323 gobject_class->finalize = gst_matroska_mux_finalize;
324
325 gobject_class->get_property = gst_matroska_mux_get_property;
326 gobject_class->set_property = gst_matroska_mux_set_property;
327
328 g_object_class_install_property (gobject_class, PROP_WRITING_APP,
329 g_param_spec_string ("writing-app", "Writing application.",
330 "The name the application that creates the matroska file.",
331 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
332 g_object_class_install_property (gobject_class, PROP_DOCTYPE_VERSION,
333 g_param_spec_int ("version", "DocType version",
334 "This parameter determines what Matroska features can be used.",
335 1, 2, DEFAULT_DOCTYPE_VERSION,
336 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
337 g_object_class_install_property (gobject_class, PROP_MIN_INDEX_INTERVAL,
338 g_param_spec_int64 ("min-index-interval", "Minimum time between index "
339 "entries", "An index entry is created every so many nanoseconds.",
340 0, G_MAXINT64, DEFAULT_MIN_INDEX_INTERVAL,
341 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
342 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
343 g_param_spec_boolean ("streamable", "Determines whether output should "
344 "be streamable", "If set to true, the output should be as if it is "
345 "to be streamed and hence no indexes written or duration written.",
346 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
347 g_object_class_install_property (gobject_class, PROP_TIMECODESCALE,
348 g_param_spec_int64 ("timecodescale", "Timecode Scale",
349 "TimecodeScale used to calculate the Raw Timecode of a Block", 1,
350 GST_SECOND, DEFAULT_TIMECODESCALE,
351 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
352 g_object_class_install_property (gobject_class, PROP_MIN_CLUSTER_DURATION,
353 g_param_spec_int64 ("min-cluster-duration", "Minimum cluster duration",
354 "Desidered cluster duration as nanoseconds. A new cluster will be "
355 "created irrespective of this property if a force key unit event "
356 "is received. 0 means create a new cluster for each video keyframe "
357 "or for each audio buffer in audio only streams.", 0,
358 G_MAXINT64, DEFAULT_MIN_CLUSTER_DURATION,
359 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
360 g_object_class_install_property (gobject_class, PROP_MAX_CLUSTER_DURATION,
361 g_param_spec_int64 ("max-cluster-duration", "Maximum cluster duration",
362 "A new cluster will be created if its duration exceeds this value. "
363 "0 means no maximum duration.", 0,
364 G_MAXINT64, DEFAULT_MAX_CLUSTER_DURATION,
365 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
366
367 gstelement_class->change_state =
368 GST_DEBUG_FUNCPTR (gst_matroska_mux_change_state);
369 gstelement_class->request_new_pad =
370 GST_DEBUG_FUNCPTR (gst_matroska_mux_request_new_pad);
371 gstelement_class->release_pad =
372 GST_DEBUG_FUNCPTR (gst_matroska_mux_release_pad);
373
374 parent_class = g_type_class_peek_parent (klass);
375 }
376
377 /*
378 * Start of pad option handler code
379 */
380 #define DEFAULT_PAD_FRAME_DURATION TRUE
381
382 enum
383 {
384 PROP_PAD_0,
385 PROP_PAD_FRAME_DURATION
386 };
387
388 typedef struct
389 {
390 GstPad parent;
391 gboolean frame_duration;
392 gboolean frame_duration_user;
393 } GstMatroskamuxPad;
394
395 typedef GstPadClass GstMatroskamuxPadClass;
396
397 GType gst_matroskamux_pad_get_type (void);
398 G_DEFINE_TYPE (GstMatroskamuxPad, gst_matroskamux_pad, GST_TYPE_PAD);
399
400 #define GST_TYPE_MATROSKAMUX_PAD (gst_matroskamux_pad_get_type())
401 #define GST_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_MATROSKAMUX_PAD,GstMatroskamuxPad))
402 #define GST_MATROSKAMUX_PAD_CAST(pad) ((GstMatroskamuxPad *) pad)
403 #define GST_IS_MATROSKAMUX_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_MATROSKAMUX_PAD))
404
405 static void
gst_matroskamux_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)406 gst_matroskamux_pad_get_property (GObject * object, guint prop_id,
407 GValue * value, GParamSpec * pspec)
408 {
409 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
410
411 switch (prop_id) {
412 case PROP_PAD_FRAME_DURATION:
413 g_value_set_boolean (value, pad->frame_duration);
414 break;
415 default:
416 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 break;
418 }
419 }
420
421 static void
gst_matroskamux_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)422 gst_matroskamux_pad_set_property (GObject * object, guint prop_id,
423 const GValue * value, GParamSpec * pspec)
424 {
425 GstMatroskamuxPad *pad = GST_MATROSKAMUX_PAD (object);
426
427 switch (prop_id) {
428 case PROP_PAD_FRAME_DURATION:
429 pad->frame_duration = g_value_get_boolean (value);
430 pad->frame_duration_user = TRUE;
431 break;
432 default:
433 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
434 break;
435 }
436 }
437
438 static void
gst_matroskamux_pad_class_init(GstMatroskamuxPadClass * klass)439 gst_matroskamux_pad_class_init (GstMatroskamuxPadClass * klass)
440 {
441 GObjectClass *gobject_class = (GObjectClass *) klass;
442
443 gobject_class->set_property = gst_matroskamux_pad_set_property;
444 gobject_class->get_property = gst_matroskamux_pad_get_property;
445
446 g_object_class_install_property (gobject_class, PROP_PAD_FRAME_DURATION,
447 g_param_spec_boolean ("frame-duration", "Frame duration",
448 "Default frame duration", DEFAULT_PAD_FRAME_DURATION,
449 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
450 }
451
452 static void
gst_matroskamux_pad_init(GstMatroskamuxPad * pad)453 gst_matroskamux_pad_init (GstMatroskamuxPad * pad)
454 {
455 pad->frame_duration = DEFAULT_PAD_FRAME_DURATION;
456 pad->frame_duration_user = FALSE;
457 }
458
459 /*
460 * End of pad option handler code
461 **/
462
463 static void
gst_matroska_mux_init(GstMatroskaMux * mux,gpointer g_class)464 gst_matroska_mux_init (GstMatroskaMux * mux, gpointer g_class)
465 {
466 GstPadTemplate *templ;
467
468 templ =
469 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
470 mux->srcpad = gst_pad_new_from_template (templ, "src");
471
472 gst_pad_set_event_function (mux->srcpad, gst_matroska_mux_handle_src_event);
473 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
474 gst_pad_use_fixed_caps (mux->srcpad);
475
476 mux->collect = gst_collect_pads_new ();
477 gst_collect_pads_set_clip_function (mux->collect,
478 GST_DEBUG_FUNCPTR (gst_collect_pads_clip_running_time), mux);
479 gst_collect_pads_set_buffer_function (mux->collect,
480 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_buffer), mux);
481 gst_collect_pads_set_event_function (mux->collect,
482 GST_DEBUG_FUNCPTR (gst_matroska_mux_handle_sink_event), mux);
483
484 mux->ebml_write = gst_ebml_write_new (mux->srcpad);
485 mux->doctype = GST_MATROSKA_DOCTYPE_MATROSKA;
486
487 /* property defaults */
488 mux->doctype_version = DEFAULT_DOCTYPE_VERSION;
489 mux->writing_app = g_strdup (DEFAULT_WRITING_APP);
490 mux->min_index_interval = DEFAULT_MIN_INDEX_INTERVAL;
491 mux->ebml_write->streamable = DEFAULT_STREAMABLE;
492 mux->time_scale = DEFAULT_TIMECODESCALE;
493 mux->min_cluster_duration = DEFAULT_MIN_CLUSTER_DURATION;
494 mux->max_cluster_duration = DEFAULT_MAX_CLUSTER_DURATION;
495
496 /* initialize internal variables */
497 mux->index = NULL;
498 mux->num_streams = 0;
499 mux->num_a_streams = 0;
500 mux->num_t_streams = 0;
501 mux->num_v_streams = 0;
502 mux->internal_toc = NULL;
503
504 /* initialize remaining variables */
505 gst_matroska_mux_reset (GST_ELEMENT (mux));
506 }
507
508
509 /**
510 * gst_matroska_mux_finalize:
511 * @object: #GstMatroskaMux that should be finalized.
512 *
513 * Finalize matroska muxer.
514 */
515 static void
gst_matroska_mux_finalize(GObject * object)516 gst_matroska_mux_finalize (GObject * object)
517 {
518 GstMatroskaMux *mux = GST_MATROSKA_MUX (object);
519
520 gst_event_replace (&mux->force_key_unit_event, NULL);
521
522 gst_object_unref (mux->collect);
523 gst_object_unref (mux->ebml_write);
524 g_free (mux->writing_app);
525
526 if (mux->internal_toc) {
527 gst_toc_unref (mux->internal_toc);
528 mux->internal_toc = NULL;
529 }
530
531 G_OBJECT_CLASS (parent_class)->finalize (object);
532 }
533
534
535 /**
536 * gst_matroska_mux_create_uid:
537 * @mux: #GstMatroskaMux to generate UID for.
538 *
539 * Generate new track UID.
540 *
541 * Returns: New track UID.
542 */
543 static guint64
gst_matroska_mux_create_uid(GstMatroskaMux * mux)544 gst_matroska_mux_create_uid (GstMatroskaMux * mux)
545 {
546 return (((guint64) g_random_int ()) << 32) | g_random_int ();
547 }
548
549
550 /**
551 * gst_matroska_pad_reset:
552 * @collect_pad: the #GstMatroskaPad
553 *
554 * Reset and/or release resources of a matroska collect pad.
555 */
556 static void
gst_matroska_pad_reset(GstMatroskaPad * collect_pad,gboolean full)557 gst_matroska_pad_reset (GstMatroskaPad * collect_pad, gboolean full)
558 {
559 gchar *name = NULL;
560 GstMatroskaTrackType type = 0;
561
562 /* free track information */
563 if (collect_pad->track != NULL) {
564 /* retrieve for optional later use */
565 name = collect_pad->track->name;
566 type = collect_pad->track->type;
567 /* extra for video */
568 if (type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
569 GstMatroskaTrackVideoContext *ctx =
570 (GstMatroskaTrackVideoContext *) collect_pad->track;
571
572 if (ctx->dirac_unit) {
573 gst_buffer_unref (ctx->dirac_unit);
574 ctx->dirac_unit = NULL;
575 }
576 }
577 g_free (collect_pad->track->codec_id);
578 g_free (collect_pad->track->codec_name);
579 if (full)
580 g_free (collect_pad->track->name);
581 g_free (collect_pad->track->language);
582 g_free (collect_pad->track->codec_priv);
583 g_free (collect_pad->track);
584 collect_pad->track = NULL;
585 if (collect_pad->tags) {
586 gst_tag_list_unref (collect_pad->tags);
587 collect_pad->tags = NULL;
588 }
589 }
590
591 if (!full && type != 0) {
592 GstMatroskaTrackContext *context;
593
594 /* create a fresh context */
595 switch (type) {
596 case GST_MATROSKA_TRACK_TYPE_VIDEO:
597 context = (GstMatroskaTrackContext *)
598 g_new0 (GstMatroskaTrackVideoContext, 1);
599 break;
600 case GST_MATROSKA_TRACK_TYPE_AUDIO:
601 context = (GstMatroskaTrackContext *)
602 g_new0 (GstMatroskaTrackAudioContext, 1);
603 break;
604 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
605 context = (GstMatroskaTrackContext *)
606 g_new0 (GstMatroskaTrackSubtitleContext, 1);
607 break;
608 default:
609 g_assert_not_reached ();
610 return;
611 }
612
613 context->type = type;
614 context->name = name;
615 context->uid = gst_matroska_mux_create_uid (collect_pad->mux);
616 /* TODO: check default values for the context */
617 context->flags = GST_MATROSKA_TRACK_ENABLED | GST_MATROSKA_TRACK_DEFAULT;
618 collect_pad->track = context;
619 collect_pad->start_ts = GST_CLOCK_TIME_NONE;
620 collect_pad->end_ts = GST_CLOCK_TIME_NONE;
621 collect_pad->tags = gst_tag_list_new_empty ();
622 gst_tag_list_set_scope (collect_pad->tags, GST_TAG_SCOPE_STREAM);
623 }
624 }
625
626 /**
627 * gst_matroska_pad_free:
628 * @collect_pad: the #GstMatroskaPad
629 *
630 * Release resources of a matroska collect pad.
631 */
632 static void
gst_matroska_pad_free(GstPad * collect_pad)633 gst_matroska_pad_free (GstPad * collect_pad)
634 {
635 gst_matroska_pad_reset ((GstMatroskaPad *) collect_pad, TRUE);
636 }
637
638
639 /**
640 * gst_matroska_mux_reset:
641 * @element: #GstMatroskaMux that should be reseted.
642 *
643 * Reset matroska muxer back to initial state.
644 */
645 static void
gst_matroska_mux_reset(GstElement * element)646 gst_matroska_mux_reset (GstElement * element)
647 {
648 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
649 GSList *walk;
650
651 /* reset EBML write */
652 gst_ebml_write_reset (mux->ebml_write);
653
654 /* reset input */
655 mux->state = GST_MATROSKA_MUX_STATE_START;
656
657 /* clean up existing streams */
658
659 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
660 GstMatroskaPad *collect_pad;
661
662 collect_pad = (GstMatroskaPad *) walk->data;
663
664 /* reset collect pad to pristine state */
665 gst_matroska_pad_reset (collect_pad, FALSE);
666 }
667
668 /* reset indexes */
669 mux->num_indexes = 0;
670 g_free (mux->index);
671 mux->index = NULL;
672
673 /* reset timers */
674 mux->duration = 0;
675
676 /* reset cluster */
677 mux->cluster = 0;
678 mux->cluster_time = 0;
679 mux->cluster_pos = 0;
680 mux->prev_cluster_size = 0;
681
682 /* reset tags */
683 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
684
685 mux->tags_pos = 0;
686
687 /* reset chapters */
688 gst_toc_setter_reset (GST_TOC_SETTER (mux));
689 if (mux->internal_toc) {
690 gst_toc_unref (mux->internal_toc);
691 mux->internal_toc = NULL;
692 }
693
694 mux->chapters_pos = 0;
695 }
696
697 /**
698 * gst_matroska_mux_handle_src_event:
699 * @pad: Pad which received the event.
700 * @event: Received event.
701 *
702 * handle events - copied from oggmux without understanding
703 *
704 * Returns: %TRUE on success.
705 */
706 static gboolean
gst_matroska_mux_handle_src_event(GstPad * pad,GstObject * parent,GstEvent * event)707 gst_matroska_mux_handle_src_event (GstPad * pad, GstObject * parent,
708 GstEvent * event)
709 {
710 GstEventType type;
711
712 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
713
714 switch (type) {
715 case GST_EVENT_SEEK:
716 /* disable seeking for now */
717 return FALSE;
718 default:
719 break;
720 }
721
722 return gst_pad_event_default (pad, parent, event);
723 }
724
725
726 static void
gst_matroska_mux_free_codec_priv(GstMatroskaTrackContext * context)727 gst_matroska_mux_free_codec_priv (GstMatroskaTrackContext * context)
728 {
729 if (context->codec_priv != NULL) {
730 g_free (context->codec_priv);
731 context->codec_priv = NULL;
732 context->codec_priv_size = 0;
733 }
734 }
735
736 static void
gst_matroska_mux_build_vobsub_private(GstMatroskaTrackContext * context,const guint * clut)737 gst_matroska_mux_build_vobsub_private (GstMatroskaTrackContext * context,
738 const guint * clut)
739 {
740 gchar *clutv[17];
741 gchar *sclut;
742 gint i;
743 guint32 col;
744 gdouble y, u, v;
745 guint8 r, g, b;
746
747 /* produce comma-separated list in hex format */
748 for (i = 0; i < 16; ++i) {
749 col = clut[i];
750 /* replicate vobsub's slightly off RGB conversion calculation */
751 y = (((col >> 16) & 0xff) - 16) * 255 / 219;
752 u = ((col >> 8) & 0xff) - 128;
753 v = (col & 0xff) - 128;
754 r = CLAMP (1.0 * y + 1.4022 * u, 0, 255);
755 g = CLAMP (1.0 * y - 0.3456 * u - 0.7145 * v, 0, 255);
756 b = CLAMP (1.0 * y + 1.7710 * v, 0, 255);
757 clutv[i] = g_strdup_printf ("%02x%02x%02x", r, g, b);
758 }
759 clutv[i] = NULL;
760 sclut = g_strjoinv (",", clutv);
761
762 /* build codec private; only palette for now */
763 gst_matroska_mux_free_codec_priv (context);
764 context->codec_priv = (guint8 *) g_strdup_printf ("palette: %s", sclut);
765 /* include terminating 0 */
766 context->codec_priv_size = strlen ((gchar *) context->codec_priv) + 1;
767 g_free (sclut);
768 for (i = 0; i < 16; ++i) {
769 g_free (clutv[i]);
770 }
771 }
772
773
774 /**
775 * gst_matroska_mux_handle_sink_event:
776 * @pad: Pad which received the event.
777 * @event: Received event.
778 *
779 * handle events - informational ones like tags
780 *
781 * Returns: %TRUE on success.
782 */
783 static gboolean
gst_matroska_mux_handle_sink_event(GstCollectPads * pads,GstCollectData * data,GstEvent * event,gpointer user_data)784 gst_matroska_mux_handle_sink_event (GstCollectPads * pads,
785 GstCollectData * data, GstEvent * event, gpointer user_data)
786 {
787 GstMatroskaPad *collect_pad;
788 GstMatroskaTrackContext *context;
789 GstMatroskaMux *mux;
790 GstPad *pad;
791 GstTagList *list;
792 gboolean ret = TRUE;
793
794 mux = GST_MATROSKA_MUX (user_data);
795 collect_pad = (GstMatroskaPad *) data;
796 pad = data->pad;
797 context = collect_pad->track;
798 g_assert (context);
799
800 switch (GST_EVENT_TYPE (event)) {
801 case GST_EVENT_CAPS:{
802 GstCaps *caps;
803
804 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
805 gst_event_parse_caps (event, &caps);
806
807 ret = collect_pad->capsfunc (pad, caps);
808 gst_event_unref (event);
809 event = NULL;
810 break;
811 }
812 case GST_EVENT_TAG:{
813 gchar *lang = NULL;
814
815 GST_DEBUG_OBJECT (mux, "received tag event");
816 gst_event_parse_tag (event, &list);
817
818 /* Matroska wants ISO 639-2B code, taglist most likely contains 639-1 */
819 if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) {
820 const gchar *lang_code;
821
822 lang_code = gst_tag_get_language_code_iso_639_2B (lang);
823 if (lang_code) {
824 GST_INFO_OBJECT (pad, "Setting language to '%s'", lang_code);
825 g_free (context->language);
826 context->language = g_strdup (lang_code);
827 } else {
828 GST_WARNING_OBJECT (pad, "Did not get language code for '%s'", lang);
829 }
830 g_free (lang);
831 }
832
833 /* FIXME: what about stream-specific tags? */
834 if (gst_tag_list_get_scope (list) == GST_TAG_SCOPE_GLOBAL) {
835 gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), list,
836 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
837 } else {
838 gst_tag_list_insert (collect_pad->tags, list, GST_TAG_MERGE_REPLACE);
839 }
840
841 gst_event_unref (event);
842 /* handled this, don't want collectpads to forward it downstream */
843 event = NULL;
844 ret = TRUE;
845 break;
846 }
847 case GST_EVENT_TOC:{
848 GstToc *toc, *old_toc;
849
850 if (mux->chapters_pos > 0)
851 break;
852
853 GST_DEBUG_OBJECT (mux, "received toc event");
854 gst_event_parse_toc (event, &toc, NULL);
855
856 if (toc != NULL) {
857 old_toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
858 if (old_toc != NULL) {
859 if (old_toc != toc)
860 GST_INFO_OBJECT (pad, "Replacing TOC with a new one");
861 gst_toc_unref (old_toc);
862 }
863
864 gst_toc_setter_set_toc (GST_TOC_SETTER (mux), toc);
865 gst_toc_unref (toc);
866 }
867
868 gst_event_unref (event);
869 /* handled this, don't want collectpads to forward it downstream */
870 event = NULL;
871 break;
872 }
873 case GST_EVENT_CUSTOM_DOWNSTREAM:
874 case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:{
875 const GstStructure *structure;
876
877 structure = gst_event_get_structure (event);
878 if (gst_structure_has_name (structure, "GstForceKeyUnit")) {
879 gst_event_replace (&mux->force_key_unit_event, NULL);
880 mux->force_key_unit_event = event;
881 event = NULL;
882 } else if (gst_structure_has_name (structure, "application/x-gst-dvd") &&
883 !strcmp ("dvd-spu-clut-change",
884 gst_structure_get_string (structure, "event"))) {
885 gchar name[16];
886 gint i, value;
887 guint clut[16];
888
889 GST_DEBUG_OBJECT (pad, "New DVD colour table received");
890 if (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
891 GST_DEBUG_OBJECT (pad, "... discarding");
892 break;
893 }
894 /* first transform event data into table form */
895 for (i = 0; i < 16; i++) {
896 g_snprintf (name, sizeof (name), "clut%02d", i);
897 if (!gst_structure_get_int (structure, name, &value)) {
898 GST_ERROR_OBJECT (mux, "dvd-spu-clut-change event did not "
899 "contain %s field", name);
900 goto break_hard;
901 }
902 clut[i] = value;
903 }
904
905 /* transform into private data for stream; text form */
906 gst_matroska_mux_build_vobsub_private (context, clut);
907 }
908 }
909 /* fall through */
910 default:
911 break;
912 }
913
914 break_hard:
915 if (event != NULL)
916 return gst_collect_pads_event_default (pads, data, event, FALSE);
917
918 return ret;
919 }
920
921 static void
gst_matroska_mux_set_codec_id(GstMatroskaTrackContext * context,const char * id)922 gst_matroska_mux_set_codec_id (GstMatroskaTrackContext * context,
923 const char *id)
924 {
925 g_assert (context && id);
926 g_free (context->codec_id);
927 context->codec_id = g_strdup (id);
928 }
929
930 /**
931 * gst_matroska_mux_video_pad_setcaps:
932 * @pad: Pad which got the caps.
933 * @caps: New caps.
934 *
935 * Setcaps function for video sink pad.
936 *
937 * Returns: %TRUE on success.
938 */
939 static gboolean
gst_matroska_mux_video_pad_setcaps(GstPad * pad,GstCaps * caps)940 gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
941 {
942 GstMatroskaTrackContext *context = NULL;
943 GstMatroskaTrackVideoContext *videocontext;
944 GstMatroskaMux *mux;
945 GstMatroskaPad *collect_pad;
946 GstStructure *structure;
947 const gchar *mimetype;
948 const gchar *interlace_mode, *s;
949 const GValue *value = NULL;
950 GstBuffer *codec_buf = NULL;
951 gint width, height, pixel_width, pixel_height;
952 gint fps_d, fps_n;
953 guint multiview_flags;
954 GstCaps *old_caps;
955
956 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
957
958 if ((old_caps = gst_pad_get_current_caps (pad))) {
959 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
960 && !gst_caps_is_equal (caps, old_caps)) {
961 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
962 ("Caps changed are not supported by Matroska"));
963 gst_caps_unref (old_caps);
964 goto refuse_caps;
965 }
966 gst_caps_unref (old_caps);
967 }
968
969 /* find context */
970 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
971 g_assert (collect_pad);
972 context = collect_pad->track;
973 g_assert (context);
974 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO);
975 videocontext = (GstMatroskaTrackVideoContext *) context;
976
977 /* gst -> matroska ID'ing */
978 structure = gst_caps_get_structure (caps, 0);
979
980 mimetype = gst_structure_get_name (structure);
981
982 interlace_mode = gst_structure_get_string (structure, "interlace-mode");
983 if (interlace_mode != NULL) {
984 if (strcmp (interlace_mode, "progressive") == 0)
985 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE;
986 else
987 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_INTERLACED;
988 } else {
989 videocontext->interlace_mode = GST_MATROSKA_INTERLACE_MODE_UNKNOWN;
990 }
991
992 if (!strcmp (mimetype, "video/x-theora")) {
993 /* we'll extract the details later from the theora identification header */
994 goto skip_details;
995 }
996
997 /* get general properties */
998 /* spec says it is mandatory */
999 if (!gst_structure_get_int (structure, "width", &width) ||
1000 !gst_structure_get_int (structure, "height", &height))
1001 goto refuse_caps;
1002
1003 videocontext->pixel_width = width;
1004 videocontext->pixel_height = height;
1005
1006 if (GST_MATROSKAMUX_PAD_CAST (pad)->frame_duration
1007 && gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)
1008 && fps_n > 0) {
1009 context->default_duration =
1010 gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
1011 GST_LOG_OBJECT (pad, "default duration = %" GST_TIME_FORMAT,
1012 GST_TIME_ARGS (context->default_duration));
1013 } else {
1014 context->default_duration = 0;
1015 }
1016 if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
1017 &pixel_width, &pixel_height)) {
1018 if (pixel_width > pixel_height) {
1019 videocontext->display_width = width * pixel_width / pixel_height;
1020 videocontext->display_height = height;
1021 } else if (pixel_width < pixel_height) {
1022 videocontext->display_width = width;
1023 videocontext->display_height = height * pixel_height / pixel_width;
1024 } else {
1025 videocontext->display_width = 0;
1026 videocontext->display_height = 0;
1027 }
1028 } else {
1029 videocontext->display_width = 0;
1030 videocontext->display_height = 0;
1031 }
1032
1033 if ((s = gst_structure_get_string (structure, "colorimetry"))) {
1034 if (!gst_video_colorimetry_from_string (&videocontext->colorimetry, s)) {
1035 GST_WARNING_OBJECT (pad, "Could not parse colorimetry %s", s);
1036 }
1037 }
1038
1039 /* Collect stereoscopic info, if any */
1040 if ((s = gst_structure_get_string (structure, "multiview-mode")))
1041 videocontext->multiview_mode =
1042 gst_video_multiview_mode_from_caps_string (s);
1043 gst_structure_get_flagset (structure, "multiview-flags", &multiview_flags,
1044 NULL);
1045 videocontext->multiview_flags = multiview_flags;
1046
1047
1048 skip_details:
1049
1050 videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
1051 videocontext->fourcc = 0;
1052
1053 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1054 * data and other settings
1055 * - add new formats
1056 */
1057
1058 /* extract codec_data, may turn out needed */
1059 value = gst_structure_get_value (structure, "codec_data");
1060 if (value)
1061 codec_buf = (GstBuffer *) gst_value_get_buffer (value);
1062
1063 /* find type */
1064 if (!strcmp (mimetype, "video/x-raw")) {
1065 const gchar *fstr;
1066 gst_matroska_mux_set_codec_id (context,
1067 GST_MATROSKA_CODEC_ID_VIDEO_UNCOMPRESSED);
1068 fstr = gst_structure_get_string (structure, "format");
1069 if (fstr) {
1070 if (strlen (fstr) == 4)
1071 videocontext->fourcc = GST_STR_FOURCC (fstr);
1072 else if (!strcmp (fstr, "GRAY8"))
1073 videocontext->fourcc = GST_MAKE_FOURCC ('Y', '8', '0', '0');
1074 else if (!strcmp (fstr, "BGR"))
1075 videocontext->fourcc = GST_MAKE_FOURCC ('B', 'G', 'R', 24);
1076 else if (!strcmp (fstr, "RGB"))
1077 videocontext->fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', 24);
1078 }
1079 } else if (!strcmp (mimetype, "video/x-huffyuv") /* MS/VfW compatibility cases */
1080 ||!strcmp (mimetype, "video/x-divx")
1081 || !strcmp (mimetype, "video/x-dv")
1082 || !strcmp (mimetype, "video/x-h263")
1083 || !strcmp (mimetype, "video/x-msmpeg")
1084 || !strcmp (mimetype, "video/x-wmv")
1085 || !strcmp (mimetype, "image/jpeg")) {
1086 gst_riff_strf_vids *bih;
1087 gint size = sizeof (gst_riff_strf_vids);
1088 guint32 fourcc = 0;
1089
1090 if (!strcmp (mimetype, "video/x-huffyuv"))
1091 fourcc = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
1092 else if (!strcmp (mimetype, "video/x-dv"))
1093 fourcc = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
1094 else if (!strcmp (mimetype, "video/x-h263"))
1095 fourcc = GST_MAKE_FOURCC ('H', '2', '6', '3');
1096 else if (!strcmp (mimetype, "video/x-divx")) {
1097 gint divxversion;
1098
1099 gst_structure_get_int (structure, "divxversion", &divxversion);
1100 switch (divxversion) {
1101 case 3:
1102 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
1103 break;
1104 case 4:
1105 fourcc = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
1106 break;
1107 case 5:
1108 fourcc = GST_MAKE_FOURCC ('D', 'X', '5', '0');
1109 break;
1110 }
1111 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1112 gint msmpegversion;
1113
1114 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
1115 switch (msmpegversion) {
1116 case 41:
1117 fourcc = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
1118 break;
1119 case 42:
1120 fourcc = GST_MAKE_FOURCC ('M', 'P', '4', '2');
1121 break;
1122 case 43:
1123 goto msmpeg43;
1124 break;
1125 }
1126 } else if (!strcmp (mimetype, "video/x-wmv")) {
1127 gint wmvversion;
1128 const gchar *fstr;
1129
1130 fstr = gst_structure_get_string (structure, "format");
1131 if (fstr && strlen (fstr) == 4) {
1132 fourcc = GST_STR_FOURCC (fstr);
1133 } else if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
1134 if (wmvversion == 2) {
1135 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
1136 } else if (wmvversion == 1) {
1137 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
1138 } else if (wmvversion == 3) {
1139 fourcc = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
1140 }
1141 }
1142 } else if (!strcmp (mimetype, "image/jpeg")) {
1143 fourcc = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
1144 }
1145
1146 if (!fourcc)
1147 goto refuse_caps;
1148
1149 bih = g_new0 (gst_riff_strf_vids, 1);
1150 GST_WRITE_UINT32_LE (&bih->size, size);
1151 GST_WRITE_UINT32_LE (&bih->width, videocontext->pixel_width);
1152 GST_WRITE_UINT32_LE (&bih->height, videocontext->pixel_height);
1153 GST_WRITE_UINT32_LE (&bih->compression, fourcc);
1154 GST_WRITE_UINT16_LE (&bih->planes, (guint16) 1);
1155 GST_WRITE_UINT16_LE (&bih->bit_cnt, (guint16) 24);
1156 GST_WRITE_UINT32_LE (&bih->image_size, videocontext->pixel_width *
1157 videocontext->pixel_height * 3);
1158
1159 /* process codec private/initialization data, if any */
1160 if (codec_buf) {
1161 size += gst_buffer_get_size (codec_buf);
1162 bih = g_realloc (bih, size);
1163 GST_WRITE_UINT32_LE (&bih->size, size);
1164 gst_buffer_extract (codec_buf, 0,
1165 (guint8 *) bih + sizeof (gst_riff_strf_vids), -1);
1166 }
1167
1168 gst_matroska_mux_set_codec_id (context,
1169 GST_MATROSKA_CODEC_ID_VIDEO_VFW_FOURCC);
1170 gst_matroska_mux_free_codec_priv (context);
1171 context->codec_priv = (gpointer) bih;
1172 context->codec_priv_size = size;
1173 context->dts_only = TRUE;
1174 } else if (!strcmp (mimetype, "video/x-h264")) {
1175 gst_matroska_mux_set_codec_id (context,
1176 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC);
1177 gst_matroska_mux_free_codec_priv (context);
1178 /* Create avcC header */
1179 if (codec_buf != NULL) {
1180 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1181 context->codec_priv = g_malloc0 (context->codec_priv_size);
1182 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1183 }
1184 } else if (!strcmp (mimetype, "video/x-h265")) {
1185 gst_matroska_mux_set_codec_id (context,
1186 GST_MATROSKA_CODEC_ID_VIDEO_MPEGH_HEVC);
1187 gst_matroska_mux_free_codec_priv (context);
1188 /* Create hvcC header */
1189 if (codec_buf != NULL) {
1190 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1191 context->codec_priv = g_malloc0 (context->codec_priv_size);
1192 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1193 }
1194 } else if (!strcmp (mimetype, "video/x-theora")) {
1195 const GValue *streamheader;
1196
1197 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_THEORA);
1198
1199 gst_matroska_mux_free_codec_priv (context);
1200
1201 streamheader = gst_structure_get_value (structure, "streamheader");
1202 if (!theora_streamheader_to_codecdata (streamheader, context)) {
1203 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1204 ("theora stream headers missing or malformed"));
1205 goto refuse_caps;
1206 }
1207 } else if (!strcmp (mimetype, "video/x-dirac")) {
1208 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC);
1209 } else if (!strcmp (mimetype, "video/x-vp8")) {
1210 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP8);
1211 } else if (!strcmp (mimetype, "video/x-vp9")) {
1212 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_VP9);
1213 } else if (!strcmp (mimetype, "video/x-av1")) {
1214 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_AV1);
1215 gst_matroska_mux_free_codec_priv (context);
1216 /* Create av1C header */
1217 if (codec_buf != NULL)
1218 gst_buffer_extract_dup (codec_buf, 0, gst_buffer_get_size (codec_buf),
1219 &context->codec_priv, &context->codec_priv_size);
1220 } else if (!strcmp (mimetype, "video/mpeg")) {
1221 gint mpegversion;
1222
1223 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1224 switch (mpegversion) {
1225 case 1:
1226 gst_matroska_mux_set_codec_id (context,
1227 GST_MATROSKA_CODEC_ID_VIDEO_MPEG1);
1228 break;
1229 case 2:
1230 gst_matroska_mux_set_codec_id (context,
1231 GST_MATROSKA_CODEC_ID_VIDEO_MPEG2);
1232 break;
1233 case 4:
1234 gst_matroska_mux_set_codec_id (context,
1235 GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP);
1236 break;
1237 default:
1238 goto refuse_caps;
1239 }
1240
1241 /* global headers may be in codec data */
1242 if (codec_buf != NULL) {
1243 gst_matroska_mux_free_codec_priv (context);
1244 context->codec_priv_size = gst_buffer_get_size (codec_buf);
1245 context->codec_priv = g_malloc0 (context->codec_priv_size);
1246 gst_buffer_extract (codec_buf, 0, context->codec_priv, -1);
1247 }
1248 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
1249 msmpeg43:
1250 /* can only make it here if preceding case verified it was version 3 */
1251 gst_matroska_mux_set_codec_id (context,
1252 GST_MATROSKA_CODEC_ID_VIDEO_MSMPEG4V3);
1253 } else if (!strcmp (mimetype, "video/x-pn-realvideo")) {
1254 gint rmversion;
1255 const GValue *mdpr_data;
1256
1257 gst_structure_get_int (structure, "rmversion", &rmversion);
1258 switch (rmversion) {
1259 case 1:
1260 gst_matroska_mux_set_codec_id (context,
1261 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO1);
1262 break;
1263 case 2:
1264 gst_matroska_mux_set_codec_id (context,
1265 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO2);
1266 break;
1267 case 3:
1268 gst_matroska_mux_set_codec_id (context,
1269 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO3);
1270 break;
1271 case 4:
1272 gst_matroska_mux_set_codec_id (context,
1273 GST_MATROSKA_CODEC_ID_VIDEO_REALVIDEO4);
1274 break;
1275 default:
1276 goto refuse_caps;
1277 }
1278
1279 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
1280 if (mdpr_data != NULL) {
1281 guint8 *priv_data = NULL;
1282 guint priv_data_size = 0;
1283
1284 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
1285
1286 priv_data_size = gst_buffer_get_size (codec_data_buf);
1287 priv_data = g_malloc0 (priv_data_size);
1288
1289 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
1290
1291 gst_matroska_mux_free_codec_priv (context);
1292 context->codec_priv = priv_data;
1293 context->codec_priv_size = priv_data_size;
1294 }
1295 } else if (strcmp (mimetype, "video/x-prores") == 0) {
1296 const gchar *variant;
1297
1298 gst_matroska_mux_free_codec_priv (context);
1299
1300 variant = gst_structure_get_string (structure, "format");
1301 if (!variant || !g_strcmp0 (variant, "standard"))
1302 context->codec_priv = g_strdup ("apcn");
1303 else if (!g_strcmp0 (variant, "hq"))
1304 context->codec_priv = g_strdup ("apch");
1305 else if (!g_strcmp0 (variant, "lt"))
1306 context->codec_priv = g_strdup ("apcs");
1307 else if (!g_strcmp0 (variant, "proxy"))
1308 context->codec_priv = g_strdup ("apco");
1309 else if (!g_strcmp0 (variant, "4444"))
1310 context->codec_priv = g_strdup ("ap4h");
1311 else {
1312 GST_WARNING_OBJECT (mux, "Unhandled prores format: %s", variant);
1313
1314 goto refuse_caps;
1315 }
1316
1317 context->codec_priv_size = sizeof (guint32);
1318 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_VIDEO_PRORES);
1319 }
1320
1321 return TRUE;
1322
1323 /* ERRORS */
1324 refuse_caps:
1325 {
1326 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
1327 GST_PAD_NAME (pad), caps);
1328 return FALSE;
1329 }
1330 }
1331
1332 /* N > 0 to expect a particular number of headers, negative if the
1333 number of headers is variable */
1334 static gboolean
xiphN_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context,GstBuffer ** p_buf0,int N)1335 xiphN_streamheader_to_codecdata (const GValue * streamheader,
1336 GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
1337 {
1338 GstBuffer **buf = NULL;
1339 GArray *bufarr;
1340 guint8 *priv_data;
1341 guint bufi, i, offset, priv_data_size;
1342
1343 if (streamheader == NULL)
1344 goto no_stream_headers;
1345
1346 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1347 goto wrong_type;
1348
1349 bufarr = g_value_peek_pointer (streamheader);
1350 if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
1351 goto wrong_count;
1352 if (N > 0 && bufarr->len != N)
1353 goto wrong_count;
1354
1355 context->xiph_headers_to_skip = bufarr->len;
1356
1357 buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
1358 for (i = 0; i < bufarr->len; i++) {
1359 GValue *bufval = &g_array_index (bufarr, GValue, i);
1360
1361 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1362 g_free (buf);
1363 goto wrong_content_type;
1364 }
1365
1366 buf[i] = g_value_peek_pointer (bufval);
1367 }
1368
1369 priv_data_size = 1;
1370 if (bufarr->len > 0) {
1371 for (i = 0; i < bufarr->len - 1; i++) {
1372 priv_data_size += gst_buffer_get_size (buf[i]) / 0xff + 1;
1373 }
1374 }
1375
1376 for (i = 0; i < bufarr->len; ++i) {
1377 priv_data_size += gst_buffer_get_size (buf[i]);
1378 }
1379
1380 priv_data = g_malloc0 (priv_data_size);
1381
1382 priv_data[0] = bufarr->len - 1;
1383 offset = 1;
1384
1385 if (bufarr->len > 0) {
1386 for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
1387 for (i = 0; i < gst_buffer_get_size (buf[bufi]) / 0xff; ++i) {
1388 priv_data[offset++] = 0xff;
1389 }
1390 priv_data[offset++] = gst_buffer_get_size (buf[bufi]) % 0xff;
1391 }
1392 }
1393
1394 for (i = 0; i < bufarr->len; ++i) {
1395 gst_buffer_extract (buf[i], 0, priv_data + offset, -1);
1396 offset += gst_buffer_get_size (buf[i]);
1397 }
1398
1399 gst_matroska_mux_free_codec_priv (context);
1400 context->codec_priv = priv_data;
1401 context->codec_priv_size = priv_data_size;
1402
1403 if (p_buf0)
1404 *p_buf0 = gst_buffer_ref (buf[0]);
1405
1406 g_free (buf);
1407
1408 return TRUE;
1409
1410 /* ERRORS */
1411 no_stream_headers:
1412 {
1413 GST_WARNING ("required streamheaders missing in sink caps!");
1414 return FALSE;
1415 }
1416 wrong_type:
1417 {
1418 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1419 G_VALUE_TYPE_NAME (streamheader));
1420 return FALSE;
1421 }
1422 wrong_count:
1423 {
1424 GST_WARNING ("got %u streamheaders, not %d as expected", bufarr->len, N);
1425 return FALSE;
1426 }
1427 wrong_content_type:
1428 {
1429 GST_WARNING ("streamheaders array does not contain GstBuffers");
1430 return FALSE;
1431 }
1432 }
1433
1434 static gboolean
vorbis_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1435 vorbis_streamheader_to_codecdata (const GValue * streamheader,
1436 GstMatroskaTrackContext * context)
1437 {
1438 GstBuffer *buf0 = NULL;
1439
1440 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1441 return FALSE;
1442
1443 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 4) {
1444 GST_WARNING ("First vorbis header too small, ignoring");
1445 } else {
1446 if (gst_buffer_memcmp (buf0, 1, "vorbis", 6) == 0) {
1447 GstMatroskaTrackAudioContext *audiocontext;
1448 GstMapInfo map;
1449 guint8 *hdr;
1450
1451 gst_buffer_map (buf0, &map, GST_MAP_READ);
1452 hdr = map.data + 1 + 6 + 4;
1453 audiocontext = (GstMatroskaTrackAudioContext *) context;
1454 audiocontext->channels = GST_READ_UINT8 (hdr);
1455 audiocontext->samplerate = GST_READ_UINT32_LE (hdr + 1);
1456 gst_buffer_unmap (buf0, &map);
1457 }
1458 }
1459
1460 if (buf0)
1461 gst_buffer_unref (buf0);
1462
1463 return TRUE;
1464 }
1465
1466 static gboolean
theora_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1467 theora_streamheader_to_codecdata (const GValue * streamheader,
1468 GstMatroskaTrackContext * context)
1469 {
1470 GstBuffer *buf0 = NULL;
1471
1472 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, 3))
1473 return FALSE;
1474
1475 if (buf0 == NULL || gst_buffer_get_size (buf0) < 1 + 6 + 26) {
1476 GST_WARNING ("First theora header too small, ignoring");
1477 } else if (gst_buffer_memcmp (buf0, 0, "\200theora\003\002", 9) != 0) {
1478 GST_WARNING ("First header not a theora identification header, ignoring");
1479 } else {
1480 GstMatroskaTrackVideoContext *videocontext;
1481 guint fps_num, fps_denom, par_num, par_denom;
1482 GstMapInfo map;
1483 guint8 *hdr;
1484
1485 gst_buffer_map (buf0, &map, GST_MAP_READ);
1486 hdr = map.data + 1 + 6 + 3 + 2 + 2;
1487
1488 videocontext = (GstMatroskaTrackVideoContext *) context;
1489 videocontext->pixel_width = GST_READ_UINT32_BE (hdr) >> 8;
1490 videocontext->pixel_height = GST_READ_UINT32_BE (hdr + 3) >> 8;
1491 hdr += 3 + 3 + 1 + 1;
1492 fps_num = GST_READ_UINT32_BE (hdr);
1493 fps_denom = GST_READ_UINT32_BE (hdr + 4);
1494 context->default_duration = gst_util_uint64_scale_int (GST_SECOND,
1495 fps_denom, fps_num);
1496 hdr += 4 + 4;
1497 par_num = GST_READ_UINT32_BE (hdr) >> 8;
1498 par_denom = GST_READ_UINT32_BE (hdr + 3) >> 8;
1499 if (par_num > 0 && par_denom > 0) {
1500 if (par_num > par_denom) {
1501 videocontext->display_width =
1502 videocontext->pixel_width * par_num / par_denom;
1503 videocontext->display_height = videocontext->pixel_height;
1504 } else if (par_num < par_denom) {
1505 videocontext->display_width = videocontext->pixel_width;
1506 videocontext->display_height =
1507 videocontext->pixel_height * par_denom / par_num;
1508 } else {
1509 videocontext->display_width = 0;
1510 videocontext->display_height = 0;
1511 }
1512 } else {
1513 videocontext->display_width = 0;
1514 videocontext->display_height = 0;
1515 }
1516
1517 gst_buffer_unmap (buf0, &map);
1518 }
1519
1520 if (buf0)
1521 gst_buffer_unref (buf0);
1522
1523 return TRUE;
1524 }
1525
1526 static gboolean
kate_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1527 kate_streamheader_to_codecdata (const GValue * streamheader,
1528 GstMatroskaTrackContext * context)
1529 {
1530 GstBuffer *buf0 = NULL;
1531
1532 if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
1533 return FALSE;
1534
1535 if (buf0 == NULL || gst_buffer_get_size (buf0) < 64) { /* Kate ID header is 64 bytes */
1536 GST_WARNING ("First kate header too small, ignoring");
1537 } else if (gst_buffer_memcmp (buf0, 0, "\200kate\0\0\0", 8) != 0) {
1538 GST_WARNING ("First header not a kate identification header, ignoring");
1539 }
1540
1541 if (buf0)
1542 gst_buffer_unref (buf0);
1543
1544 return TRUE;
1545 }
1546
1547 static gboolean
flac_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1548 flac_streamheader_to_codecdata (const GValue * streamheader,
1549 GstMatroskaTrackContext * context)
1550 {
1551 GArray *bufarr;
1552 gint i;
1553 GValue *bufval;
1554 GstBuffer *buffer;
1555
1556 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1557 GST_WARNING ("No or invalid streamheader field in the caps");
1558 return FALSE;
1559 }
1560
1561 bufarr = g_value_peek_pointer (streamheader);
1562 if (bufarr->len < 2) {
1563 GST_WARNING ("Too few headers in streamheader field");
1564 return FALSE;
1565 }
1566
1567 context->xiph_headers_to_skip = bufarr->len + 1;
1568
1569 bufval = &g_array_index (bufarr, GValue, 0);
1570 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1571 GST_WARNING ("streamheaders array does not contain GstBuffers");
1572 return FALSE;
1573 }
1574
1575 buffer = g_value_peek_pointer (bufval);
1576
1577 /* Need at least OggFLAC mapping header, fLaC marker and STREAMINFO block */
1578 if (gst_buffer_get_size (buffer) < 9 + 4 + 4 + 34
1579 || gst_buffer_memcmp (buffer, 1, "FLAC", 4) != 0
1580 || gst_buffer_memcmp (buffer, 9, "fLaC", 4) != 0) {
1581 GST_WARNING ("Invalid streamheader for FLAC");
1582 return FALSE;
1583 }
1584
1585 gst_matroska_mux_free_codec_priv (context);
1586 context->codec_priv_size = gst_buffer_get_size (buffer) - 9;
1587 context->codec_priv = g_malloc (context->codec_priv_size);
1588 gst_buffer_extract (buffer, 9, context->codec_priv, -1);
1589
1590 for (i = 1; i < bufarr->len; i++) {
1591 guint old_size;
1592 bufval = &g_array_index (bufarr, GValue, i);
1593
1594 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1595 gst_matroska_mux_free_codec_priv (context);
1596 GST_WARNING ("streamheaders array does not contain GstBuffers");
1597 return FALSE;
1598 }
1599
1600 buffer = g_value_peek_pointer (bufval);
1601
1602 old_size = context->codec_priv_size;
1603 context->codec_priv_size += gst_buffer_get_size (buffer);
1604
1605 context->codec_priv = g_realloc (context->codec_priv,
1606 context->codec_priv_size);
1607 gst_buffer_extract (buffer, 0,
1608 (guint8 *) context->codec_priv + old_size, -1);
1609 }
1610
1611 return TRUE;
1612 }
1613
1614 static gboolean
speex_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1615 speex_streamheader_to_codecdata (const GValue * streamheader,
1616 GstMatroskaTrackContext * context)
1617 {
1618 GArray *bufarr;
1619 GValue *bufval;
1620 GstBuffer *buffer;
1621 guint old_size;
1622
1623 if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
1624 GST_WARNING ("No or invalid streamheader field in the caps");
1625 return FALSE;
1626 }
1627
1628 bufarr = g_value_peek_pointer (streamheader);
1629 if (bufarr->len != 2) {
1630 GST_WARNING ("Too few headers in streamheader field");
1631 return FALSE;
1632 }
1633
1634 context->xiph_headers_to_skip = bufarr->len + 1;
1635
1636 bufval = &g_array_index (bufarr, GValue, 0);
1637 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1638 GST_WARNING ("streamheaders array does not contain GstBuffers");
1639 return FALSE;
1640 }
1641
1642 buffer = g_value_peek_pointer (bufval);
1643
1644 if (gst_buffer_get_size (buffer) < 80
1645 || gst_buffer_memcmp (buffer, 0, "Speex ", 8) != 0) {
1646 GST_WARNING ("Invalid streamheader for Speex");
1647 return FALSE;
1648 }
1649
1650 gst_matroska_mux_free_codec_priv (context);
1651 context->codec_priv_size = gst_buffer_get_size (buffer);
1652 context->codec_priv = g_malloc (context->codec_priv_size);
1653 gst_buffer_extract (buffer, 0, context->codec_priv, -1);
1654
1655 bufval = &g_array_index (bufarr, GValue, 1);
1656
1657 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1658 gst_matroska_mux_free_codec_priv (context);
1659 GST_WARNING ("streamheaders array does not contain GstBuffers");
1660 return FALSE;
1661 }
1662
1663 buffer = g_value_peek_pointer (bufval);
1664
1665 old_size = context->codec_priv_size;
1666 context->codec_priv_size += gst_buffer_get_size (buffer);
1667 context->codec_priv = g_realloc (context->codec_priv,
1668 context->codec_priv_size);
1669 gst_buffer_extract (buffer, 0, (guint8 *) context->codec_priv + old_size, -1);
1670
1671 return TRUE;
1672 }
1673
1674 static gboolean
opus_streamheader_to_codecdata(const GValue * streamheader,GstMatroskaTrackContext * context)1675 opus_streamheader_to_codecdata (const GValue * streamheader,
1676 GstMatroskaTrackContext * context)
1677 {
1678 GArray *bufarr;
1679 GValue *bufval;
1680 GstBuffer *buf;
1681
1682 if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
1683 goto wrong_type;
1684
1685 bufarr = g_value_peek_pointer (streamheader);
1686 if (bufarr->len != 1 && bufarr->len != 2) /* one header, and count stored in a byte */
1687 goto wrong_count;
1688
1689 /* Opus headers are not in-band */
1690 context->xiph_headers_to_skip = 0;
1691
1692 bufval = &g_array_index (bufarr, GValue, 0);
1693 if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
1694 goto wrong_content_type;
1695 }
1696 buf = g_value_peek_pointer (bufval);
1697
1698 gst_matroska_mux_free_codec_priv (context);
1699
1700 context->codec_priv_size = gst_buffer_get_size (buf);
1701 context->codec_priv = g_malloc0 (context->codec_priv_size);
1702 gst_buffer_extract (buf, 0, context->codec_priv, -1);
1703
1704 context->codec_delay =
1705 GST_READ_UINT16_LE ((guint8 *) context->codec_priv + 10);
1706 context->codec_delay =
1707 gst_util_uint64_scale_round (context->codec_delay, GST_SECOND, 48000);
1708 context->seek_preroll = 80 * GST_MSECOND;
1709
1710 return TRUE;
1711
1712 /* ERRORS */
1713 wrong_type:
1714 {
1715 GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
1716 G_VALUE_TYPE_NAME (streamheader));
1717 return FALSE;
1718 }
1719 wrong_count:
1720 {
1721 GST_WARNING ("got %u streamheaders, not 1 or 2 as expected", bufarr->len);
1722 return FALSE;
1723 }
1724 wrong_content_type:
1725 {
1726 GST_WARNING ("streamheaders array does not contain GstBuffers");
1727 return FALSE;
1728 }
1729 }
1730
1731 static gboolean
opus_make_codecdata(GstMatroskaTrackContext * context,GstCaps * caps)1732 opus_make_codecdata (GstMatroskaTrackContext * context, GstCaps * caps)
1733 {
1734 guint32 rate;
1735 guint8 channels;
1736 guint8 channel_mapping_family;
1737 guint8 stream_count, coupled_count, channel_mapping[256];
1738 GstBuffer *buffer;
1739 GstMapInfo map;
1740
1741 /* Opus headers are not in-band */
1742 context->xiph_headers_to_skip = 0;
1743
1744 context->codec_delay = 0;
1745 context->seek_preroll = 80 * GST_MSECOND;
1746
1747 if (!gst_codec_utils_opus_parse_caps (caps, &rate, &channels,
1748 &channel_mapping_family, &stream_count, &coupled_count,
1749 channel_mapping)) {
1750 GST_WARNING ("Failed to parse caps for Opus");
1751 return FALSE;
1752 }
1753
1754 buffer =
1755 gst_codec_utils_opus_create_header (rate, channels,
1756 channel_mapping_family, stream_count, coupled_count, channel_mapping, 0,
1757 0);
1758 if (!buffer) {
1759 GST_WARNING ("Failed to create Opus header from caps");
1760 return FALSE;
1761 }
1762
1763 gst_buffer_map (buffer, &map, GST_MAP_READ);
1764 context->codec_priv_size = map.size;
1765 context->codec_priv = g_malloc (context->codec_priv_size);
1766 memcpy (context->codec_priv, map.data, map.size);
1767 gst_buffer_unmap (buffer, &map);
1768 gst_buffer_unref (buffer);
1769
1770 return TRUE;
1771 }
1772
1773 /**
1774 * gst_matroska_mux_audio_pad_setcaps:
1775 * @pad: Pad which got the caps.
1776 * @caps: New caps.
1777 *
1778 * Setcaps function for audio sink pad.
1779 *
1780 * Returns: %TRUE on success.
1781 */
1782 static gboolean
gst_matroska_mux_audio_pad_setcaps(GstPad * pad,GstCaps * caps)1783 gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
1784 {
1785 GstMatroskaTrackContext *context = NULL;
1786 GstMatroskaTrackAudioContext *audiocontext;
1787 GstMatroskaMux *mux;
1788 GstMatroskaPad *collect_pad;
1789 const gchar *mimetype;
1790 gint samplerate = 0, channels = 0;
1791 GstStructure *structure;
1792 const GValue *codec_data = NULL;
1793 GstBuffer *buf = NULL;
1794 const gchar *stream_format = NULL;
1795 GstCaps *old_caps;
1796
1797 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
1798
1799 if ((old_caps = gst_pad_get_current_caps (pad))) {
1800 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
1801 && !gst_caps_is_equal (caps, old_caps)) {
1802 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1803 ("Caps changed are not supported by Matroska"));
1804 gst_caps_unref (old_caps);
1805 goto refuse_caps;
1806 }
1807 gst_caps_unref (old_caps);
1808 }
1809
1810 /* find context */
1811 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
1812 g_assert (collect_pad);
1813 context = collect_pad->track;
1814 g_assert (context);
1815 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_AUDIO);
1816 audiocontext = (GstMatroskaTrackAudioContext *) context;
1817
1818 structure = gst_caps_get_structure (caps, 0);
1819 mimetype = gst_structure_get_name (structure);
1820
1821 /* general setup */
1822 gst_structure_get_int (structure, "rate", &samplerate);
1823 gst_structure_get_int (structure, "channels", &channels);
1824
1825 audiocontext->samplerate = samplerate;
1826 audiocontext->channels = channels;
1827 audiocontext->bitdepth = 0;
1828 context->default_duration = 0;
1829
1830 codec_data = gst_structure_get_value (structure, "codec_data");
1831 if (codec_data)
1832 buf = gst_value_get_buffer (codec_data);
1833
1834 /* TODO: - check if we handle all codecs by the spec, i.e. codec private
1835 * data and other settings
1836 * - add new formats
1837 */
1838
1839 if (!strcmp (mimetype, "audio/mpeg")) {
1840 gint mpegversion = 0;
1841
1842 gst_structure_get_int (structure, "mpegversion", &mpegversion);
1843 switch (mpegversion) {
1844 case 1:{
1845 gint layer;
1846 gint version = 1;
1847 gint spf;
1848
1849 gst_structure_get_int (structure, "layer", &layer);
1850
1851 if (!gst_structure_get_int (structure, "mpegaudioversion", &version)) {
1852 GST_WARNING_OBJECT (mux,
1853 "Unable to determine MPEG audio version, assuming 1");
1854 version = 1;
1855 }
1856
1857 if (layer == 1)
1858 spf = 384;
1859 else if (layer == 2)
1860 spf = 1152;
1861 else if (version == 2)
1862 spf = 576;
1863 else
1864 spf = 1152;
1865
1866 context->default_duration =
1867 gst_util_uint64_scale (GST_SECOND, spf, audiocontext->samplerate);
1868
1869 switch (layer) {
1870 case 1:
1871 gst_matroska_mux_set_codec_id (context,
1872 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L1);
1873 break;
1874 case 2:
1875 gst_matroska_mux_set_codec_id (context,
1876 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L2);
1877 break;
1878 case 3:
1879 gst_matroska_mux_set_codec_id (context,
1880 GST_MATROSKA_CODEC_ID_AUDIO_MPEG1_L3);
1881 break;
1882 default:
1883 goto refuse_caps;
1884 }
1885 break;
1886 }
1887 case 2:
1888 case 4:
1889 stream_format = gst_structure_get_string (structure, "stream-format");
1890 /* check this is raw aac */
1891 if (stream_format) {
1892 if (strcmp (stream_format, "raw") != 0) {
1893 GST_WARNING_OBJECT (mux, "AAC stream-format must be 'raw', not %s",
1894 stream_format);
1895 }
1896 } else {
1897 GST_WARNING_OBJECT (mux, "AAC stream-format not specified, "
1898 "assuming 'raw'");
1899 }
1900
1901 if (buf) {
1902 gst_matroska_mux_set_codec_id (context,
1903 GST_MATROSKA_CODEC_ID_AUDIO_AAC);
1904 context->codec_priv_size = gst_buffer_get_size (buf);
1905 context->codec_priv = g_malloc (context->codec_priv_size);
1906 gst_buffer_extract (buf, 0, context->codec_priv,
1907 context->codec_priv_size);
1908 } else {
1909 GST_DEBUG_OBJECT (mux, "no AAC codec_data; not packetized");
1910 goto refuse_caps;
1911 }
1912 break;
1913 default:
1914 goto refuse_caps;
1915 }
1916 } else if (!strcmp (mimetype, "audio/x-raw")) {
1917 GstAudioInfo info;
1918
1919 gst_audio_info_init (&info);
1920 if (!gst_audio_info_from_caps (&info, caps)) {
1921 GST_DEBUG_OBJECT (mux,
1922 "broken caps, rejected by gst_audio_info_from_caps");
1923 goto refuse_caps;
1924 }
1925
1926 switch (GST_AUDIO_INFO_FORMAT (&info)) {
1927 case GST_AUDIO_FORMAT_U8:
1928 case GST_AUDIO_FORMAT_S16BE:
1929 case GST_AUDIO_FORMAT_S16LE:
1930 case GST_AUDIO_FORMAT_S24BE:
1931 case GST_AUDIO_FORMAT_S24LE:
1932 case GST_AUDIO_FORMAT_S32BE:
1933 case GST_AUDIO_FORMAT_S32LE:
1934 if (GST_AUDIO_INFO_WIDTH (&info) != GST_AUDIO_INFO_DEPTH (&info)) {
1935 GST_DEBUG_OBJECT (mux, "width must be same as depth!");
1936 goto refuse_caps;
1937 }
1938 if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&info))
1939 gst_matroska_mux_set_codec_id (context,
1940 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_BE);
1941 else
1942 gst_matroska_mux_set_codec_id (context,
1943 GST_MATROSKA_CODEC_ID_AUDIO_PCM_INT_LE);
1944 break;
1945 case GST_AUDIO_FORMAT_F32LE:
1946 case GST_AUDIO_FORMAT_F64LE:
1947 gst_matroska_mux_set_codec_id (context,
1948 GST_MATROSKA_CODEC_ID_AUDIO_PCM_FLOAT);
1949 break;
1950
1951 default:
1952 GST_DEBUG_OBJECT (mux, "wrong format in raw audio caps");
1953 goto refuse_caps;
1954 }
1955
1956 audiocontext->bitdepth = GST_AUDIO_INFO_WIDTH (&info);
1957 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
1958 const GValue *streamheader;
1959
1960 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_VORBIS);
1961
1962 gst_matroska_mux_free_codec_priv (context);
1963
1964 streamheader = gst_structure_get_value (structure, "streamheader");
1965 if (!vorbis_streamheader_to_codecdata (streamheader, context)) {
1966 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1967 ("vorbis stream headers missing or malformed"));
1968 goto refuse_caps;
1969 }
1970 } else if (!strcmp (mimetype, "audio/x-flac")) {
1971 const GValue *streamheader;
1972
1973 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_FLAC);
1974
1975 gst_matroska_mux_free_codec_priv (context);
1976
1977 streamheader = gst_structure_get_value (structure, "streamheader");
1978 if (!flac_streamheader_to_codecdata (streamheader, context)) {
1979 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1980 ("flac stream headers missing or malformed"));
1981 goto refuse_caps;
1982 }
1983 } else if (!strcmp (mimetype, "audio/x-speex")) {
1984 const GValue *streamheader;
1985
1986 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
1987 gst_matroska_mux_free_codec_priv (context);
1988
1989 streamheader = gst_structure_get_value (structure, "streamheader");
1990 if (!speex_streamheader_to_codecdata (streamheader, context)) {
1991 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1992 ("speex stream headers missing or malformed"));
1993 goto refuse_caps;
1994 }
1995 } else if (!strcmp (mimetype, "audio/x-opus")) {
1996 const GValue *streamheader;
1997
1998 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_OPUS);
1999
2000 streamheader = gst_structure_get_value (structure, "streamheader");
2001 if (streamheader) {
2002 gst_matroska_mux_free_codec_priv (context);
2003 if (!opus_streamheader_to_codecdata (streamheader, context)) {
2004 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2005 ("opus stream headers missing or malformed"));
2006 goto refuse_caps;
2007 }
2008 } else {
2009 /* no streamheader, but we need to have one, so we make one up
2010 based on caps */
2011 gst_matroska_mux_free_codec_priv (context);
2012 if (!opus_make_codecdata (context, caps)) {
2013 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2014 ("opus stream headers missing or malformed"));
2015 goto refuse_caps;
2016 }
2017 }
2018 } else if (!strcmp (mimetype, "audio/x-ac3")) {
2019 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_AC3);
2020 } else if (!strcmp (mimetype, "audio/x-eac3")) {
2021 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_EAC3);
2022 } else if (!strcmp (mimetype, "audio/x-dts")) {
2023 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_DTS);
2024 } else if (!strcmp (mimetype, "audio/x-tta")) {
2025 gint width;
2026
2027 /* TTA frame duration */
2028 context->default_duration = 1.04489795918367346939 * GST_SECOND;
2029
2030 gst_structure_get_int (structure, "width", &width);
2031 audiocontext->bitdepth = width;
2032 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_TTA);
2033
2034 } else if (!strcmp (mimetype, "audio/x-pn-realaudio")) {
2035 gint raversion;
2036 const GValue *mdpr_data;
2037
2038 gst_structure_get_int (structure, "raversion", &raversion);
2039 switch (raversion) {
2040 case 1:
2041 gst_matroska_mux_set_codec_id (context,
2042 GST_MATROSKA_CODEC_ID_AUDIO_REAL_14_4);
2043 break;
2044 case 2:
2045 gst_matroska_mux_set_codec_id (context,
2046 GST_MATROSKA_CODEC_ID_AUDIO_REAL_28_8);
2047 break;
2048 case 8:
2049 gst_matroska_mux_set_codec_id (context,
2050 GST_MATROSKA_CODEC_ID_AUDIO_REAL_COOK);
2051 break;
2052 default:
2053 goto refuse_caps;
2054 }
2055
2056 mdpr_data = gst_structure_get_value (structure, "mdpr_data");
2057 if (mdpr_data != NULL) {
2058 guint8 *priv_data = NULL;
2059 guint priv_data_size = 0;
2060
2061 GstBuffer *codec_data_buf = g_value_peek_pointer (mdpr_data);
2062
2063 priv_data_size = gst_buffer_get_size (codec_data_buf);
2064 priv_data = g_malloc0 (priv_data_size);
2065
2066 gst_buffer_extract (codec_data_buf, 0, priv_data, -1);
2067
2068 gst_matroska_mux_free_codec_priv (context);
2069
2070 context->codec_priv = priv_data;
2071 context->codec_priv_size = priv_data_size;
2072 }
2073
2074 } else if (!strcmp (mimetype, "audio/x-wma")
2075 || !strcmp (mimetype, "audio/x-alaw")
2076 || !strcmp (mimetype, "audio/x-mulaw")
2077 || !strcmp (mimetype, "audio/x-adpcm")
2078 || !strcmp (mimetype, "audio/G722")) {
2079 guint8 *codec_priv;
2080 guint codec_priv_size;
2081 guint16 format = 0;
2082 gint block_align = 0;
2083 gint bitrate = 0;
2084
2085 if (samplerate == 0 || channels == 0) {
2086 GST_WARNING_OBJECT (mux, "Missing channels/samplerate on caps");
2087 goto refuse_caps;
2088 }
2089
2090 if (!strcmp (mimetype, "audio/x-wma")) {
2091 gint wmaversion;
2092 gint depth;
2093
2094 if (!gst_structure_get_int (structure, "wmaversion", &wmaversion)
2095 || !gst_structure_get_int (structure, "block_align", &block_align)
2096 || !gst_structure_get_int (structure, "bitrate", &bitrate)) {
2097 GST_WARNING_OBJECT (mux, "Missing wmaversion/block_align/bitrate"
2098 " on WMA caps");
2099 goto refuse_caps;
2100 }
2101
2102 switch (wmaversion) {
2103 case 1:
2104 format = GST_RIFF_WAVE_FORMAT_WMAV1;
2105 break;
2106 case 2:
2107 format = GST_RIFF_WAVE_FORMAT_WMAV2;
2108 break;
2109 case 3:
2110 format = GST_RIFF_WAVE_FORMAT_WMAV3;
2111 break;
2112 default:
2113 GST_WARNING_OBJECT (mux, "Unexpected WMA version: %d", wmaversion);
2114 goto refuse_caps;
2115 }
2116
2117 if (gst_structure_get_int (structure, "depth", &depth))
2118 audiocontext->bitdepth = depth;
2119 } else if (!strcmp (mimetype, "audio/x-alaw")
2120 || !strcmp (mimetype, "audio/x-mulaw")) {
2121 audiocontext->bitdepth = 8;
2122 if (!strcmp (mimetype, "audio/x-alaw"))
2123 format = GST_RIFF_WAVE_FORMAT_ALAW;
2124 else
2125 format = GST_RIFF_WAVE_FORMAT_MULAW;
2126
2127 block_align = channels;
2128 bitrate = block_align * samplerate;
2129 } else if (!strcmp (mimetype, "audio/x-adpcm")) {
2130 const char *layout;
2131
2132 layout = gst_structure_get_string (structure, "layout");
2133 if (!layout) {
2134 GST_WARNING_OBJECT (mux, "Missing layout on adpcm caps");
2135 goto refuse_caps;
2136 }
2137
2138 if (!gst_structure_get_int (structure, "block_align", &block_align)) {
2139 GST_WARNING_OBJECT (mux, "Missing block_align on adpcm caps");
2140 goto refuse_caps;
2141 }
2142
2143 if (!strcmp (layout, "dvi")) {
2144 format = GST_RIFF_WAVE_FORMAT_DVI_ADPCM;
2145 } else if (!strcmp (layout, "g726")) {
2146 format = GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM;
2147 if (!gst_structure_get_int (structure, "bitrate", &bitrate)) {
2148 GST_WARNING_OBJECT (mux, "Missing bitrate on adpcm g726 caps");
2149 goto refuse_caps;
2150 }
2151 } else {
2152 GST_WARNING_OBJECT (mux, "Unknown layout on adpcm caps");
2153 goto refuse_caps;
2154 }
2155
2156 } else if (!strcmp (mimetype, "audio/G722")) {
2157 format = GST_RIFF_WAVE_FORMAT_ADPCM_G722;
2158 }
2159 g_assert (format != 0);
2160
2161 codec_priv_size = WAVEFORMATEX_SIZE;
2162 if (buf)
2163 codec_priv_size += gst_buffer_get_size (buf);
2164
2165 /* serialize waveformatex structure */
2166 codec_priv = g_malloc0 (codec_priv_size);
2167 GST_WRITE_UINT16_LE (codec_priv, format);
2168 GST_WRITE_UINT16_LE (codec_priv + 2, channels);
2169 GST_WRITE_UINT32_LE (codec_priv + 4, samplerate);
2170 GST_WRITE_UINT32_LE (codec_priv + 8, bitrate / 8);
2171 GST_WRITE_UINT16_LE (codec_priv + 12, block_align);
2172 GST_WRITE_UINT16_LE (codec_priv + 14, 0);
2173 if (buf)
2174 GST_WRITE_UINT16_LE (codec_priv + 16, gst_buffer_get_size (buf));
2175 else
2176 GST_WRITE_UINT16_LE (codec_priv + 16, 0);
2177
2178 /* process codec private/initialization data, if any */
2179 if (buf) {
2180 gst_buffer_extract (buf, 0,
2181 (guint8 *) codec_priv + WAVEFORMATEX_SIZE, -1);
2182 }
2183
2184 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_AUDIO_ACM);
2185 gst_matroska_mux_free_codec_priv (context);
2186 context->codec_priv = (gpointer) codec_priv;
2187 context->codec_priv_size = codec_priv_size;
2188 }
2189
2190 return TRUE;
2191
2192 /* ERRORS */
2193 refuse_caps:
2194 {
2195 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2196 GST_PAD_NAME (pad), caps);
2197 return FALSE;
2198 }
2199 }
2200
2201 /* we probably don't have the data at start,
2202 * so have to reserve (a maximum) space to write this at the end.
2203 * bit spacy, but some formats can hold quite some */
2204 #define SUBTITLE_MAX_CODEC_PRIVATE 2048 /* must be > 128 */
2205
2206 /**
2207 * gst_matroska_mux_subtitle_pad_setcaps:
2208 * @pad: Pad which got the caps.
2209 * @caps: New caps.
2210 *
2211 * Setcaps function for subtitle sink pad.
2212 *
2213 * Returns: %TRUE on success.
2214 */
2215 static gboolean
gst_matroska_mux_subtitle_pad_setcaps(GstPad * pad,GstCaps * caps)2216 gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
2217 {
2218 /* There is now (at least) one such alement (kateenc), and I'm going
2219 to handle it here and claim it works when it can be piped back
2220 through GStreamer and VLC */
2221
2222 GstMatroskaTrackContext *context = NULL;
2223 GstMatroskaTrackSubtitleContext *scontext;
2224 GstMatroskaMux *mux;
2225 GstMatroskaPad *collect_pad;
2226 const gchar *mimetype;
2227 GstStructure *structure;
2228 const GValue *value = NULL;
2229 GstBuffer *buf = NULL;
2230 gboolean ret = TRUE;
2231 GstCaps *old_caps;
2232
2233 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2234
2235 if ((old_caps = gst_pad_get_current_caps (pad))) {
2236 if (mux->state >= GST_MATROSKA_MUX_STATE_HEADER
2237 && !gst_caps_is_equal (caps, old_caps)) {
2238 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2239 ("Caps changed are not supported by Matroska"));
2240 gst_caps_unref (old_caps);
2241 goto refuse_caps;
2242 }
2243 gst_caps_unref (old_caps);
2244 }
2245
2246 /* find context */
2247 collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
2248 g_assert (collect_pad);
2249 context = collect_pad->track;
2250 g_assert (context);
2251 g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
2252 scontext = (GstMatroskaTrackSubtitleContext *) context;
2253
2254 structure = gst_caps_get_structure (caps, 0);
2255 mimetype = gst_structure_get_name (structure);
2256
2257 /* general setup */
2258 scontext->check_utf8 = 1;
2259 scontext->invalid_utf8 = 0;
2260 context->default_duration = 0;
2261
2262 if (!strcmp (mimetype, "subtitle/x-kate")) {
2263 const GValue *streamheader;
2264
2265 gst_matroska_mux_set_codec_id (context,
2266 GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
2267
2268 gst_matroska_mux_free_codec_priv (context);
2269
2270 streamheader = gst_structure_get_value (structure, "streamheader");
2271 if (!kate_streamheader_to_codecdata (streamheader, context)) {
2272 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2273 ("kate stream headers missing or malformed"));
2274 ret = FALSE;
2275 goto exit;
2276 }
2277 } else if (!strcmp (mimetype, "text/x-raw")) {
2278 gst_matroska_mux_set_codec_id (context,
2279 GST_MATROSKA_CODEC_ID_SUBTITLE_UTF8);
2280 } else if (!strcmp (mimetype, "application/x-ssa")) {
2281 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_SSA);
2282 } else if (!strcmp (mimetype, "application/x-ass")) {
2283 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_ASS);
2284 } else if (!strcmp (mimetype, "application/x-usf")) {
2285 gst_matroska_mux_set_codec_id (context, GST_MATROSKA_CODEC_ID_SUBTITLE_USF);
2286 } else if (!strcmp (mimetype, "subpicture/x-dvd")) {
2287 gst_matroska_mux_set_codec_id (context,
2288 GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB);
2289 } else {
2290 ret = FALSE;
2291 goto exit;
2292 }
2293
2294 /* maybe some private data, e.g. vobsub */
2295 value = gst_structure_get_value (structure, "codec_data");
2296 if (value)
2297 buf = gst_value_get_buffer (value);
2298 if (buf != NULL) {
2299 GstMapInfo map;
2300 guint8 *priv_data = NULL;
2301
2302 gst_buffer_map (buf, &map, GST_MAP_READ);
2303
2304 if (map.size > SUBTITLE_MAX_CODEC_PRIVATE) {
2305 GST_WARNING_OBJECT (mux, "pad %" GST_PTR_FORMAT " subtitle private data"
2306 " exceeded maximum (%d); discarding", pad,
2307 SUBTITLE_MAX_CODEC_PRIVATE);
2308 gst_buffer_unmap (buf, &map);
2309 return TRUE;
2310 }
2311
2312 gst_matroska_mux_free_codec_priv (context);
2313
2314 priv_data = g_malloc0 (map.size);
2315 memcpy (priv_data, map.data, map.size);
2316 context->codec_priv = priv_data;
2317 context->codec_priv_size = map.size;
2318 gst_buffer_unmap (buf, &map);
2319 }
2320
2321 GST_DEBUG_OBJECT (pad, "codec_id %s, codec data size %" G_GSIZE_FORMAT,
2322 GST_STR_NULL (context->codec_id), context->codec_priv_size);
2323
2324 exit:
2325
2326 return ret;
2327
2328 /* ERRORS */
2329 refuse_caps:
2330 {
2331 GST_WARNING_OBJECT (mux, "pad %s refused caps %" GST_PTR_FORMAT,
2332 GST_PAD_NAME (pad), caps);
2333 return FALSE;
2334 }
2335 }
2336
2337
2338 /**
2339 * gst_matroska_mux_request_new_pad:
2340 * @element: #GstMatroskaMux.
2341 * @templ: #GstPadTemplate.
2342 * @pad_name: New pad name.
2343 *
2344 * Request pad function for sink templates.
2345 *
2346 * Returns: New #GstPad.
2347 */
2348 static GstPad *
gst_matroska_mux_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)2349 gst_matroska_mux_request_new_pad (GstElement * element,
2350 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
2351 {
2352 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
2353 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
2354 GstMatroskaPad *collect_pad;
2355 GstMatroskamuxPad *newpad;
2356 gchar *name = NULL;
2357 const gchar *pad_name = NULL;
2358 GstMatroskaCapsFunc capsfunc = NULL;
2359 GstMatroskaTrackContext *context = NULL;
2360 gint pad_id;
2361 gboolean locked = TRUE;
2362 const gchar *id = NULL;
2363
2364 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
2365 /* don't mix named and unnamed pads, if the pad already exists we fail when
2366 * trying to add it */
2367 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
2368 pad_name = req_name;
2369 } else {
2370 name = g_strdup_printf ("audio_%u", mux->num_a_streams++);
2371 pad_name = name;
2372 }
2373 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_audio_pad_setcaps);
2374 context = (GstMatroskaTrackContext *)
2375 g_new0 (GstMatroskaTrackAudioContext, 1);
2376 context->type = GST_MATROSKA_TRACK_TYPE_AUDIO;
2377 context->name = g_strdup ("Audio");
2378 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
2379 /* don't mix named and unnamed pads, if the pad already exists we fail when
2380 * trying to add it */
2381 if (req_name != NULL && sscanf (req_name, "video_%u", &pad_id) == 1) {
2382 pad_name = req_name;
2383 } else {
2384 name = g_strdup_printf ("video_%u", mux->num_v_streams++);
2385 pad_name = name;
2386 }
2387 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_video_pad_setcaps);
2388 context = (GstMatroskaTrackContext *)
2389 g_new0 (GstMatroskaTrackVideoContext, 1);
2390 context->type = GST_MATROSKA_TRACK_TYPE_VIDEO;
2391 context->name = g_strdup ("Video");
2392 } else if (templ == gst_element_class_get_pad_template (klass, "subtitle_%u")) {
2393 /* don't mix named and unnamed pads, if the pad already exists we fail when
2394 * trying to add it */
2395 if (req_name != NULL && sscanf (req_name, "subtitle_%u", &pad_id) == 1) {
2396 pad_name = req_name;
2397 } else {
2398 name = g_strdup_printf ("subtitle_%u", mux->num_t_streams++);
2399 pad_name = name;
2400 }
2401 capsfunc = GST_DEBUG_FUNCPTR (gst_matroska_mux_subtitle_pad_setcaps);
2402 context = (GstMatroskaTrackContext *)
2403 g_new0 (GstMatroskaTrackSubtitleContext, 1);
2404 context->type = GST_MATROSKA_TRACK_TYPE_SUBTITLE;
2405 context->name = g_strdup ("Subtitle");
2406 /* setcaps may only provide proper one a lot later */
2407 id = "S_SUB_UNKNOWN";
2408 locked = FALSE;
2409 } else {
2410 GST_WARNING_OBJECT (mux, "This is not our template!");
2411 return NULL;
2412 }
2413
2414 newpad = g_object_new (GST_TYPE_MATROSKAMUX_PAD,
2415 "name", pad_name, "direction", templ->direction, "template", templ, NULL);
2416
2417 gst_matroskamux_pad_init (newpad);
2418 collect_pad = (GstMatroskaPad *)
2419 gst_collect_pads_add_pad (mux->collect, GST_PAD (newpad),
2420 sizeof (GstMatroskamuxPad),
2421 (GstCollectDataDestroyNotify) gst_matroska_pad_free, locked);
2422
2423 collect_pad->mux = mux;
2424 collect_pad->track = context;
2425 gst_matroska_pad_reset (collect_pad, FALSE);
2426 if (id)
2427 gst_matroska_mux_set_codec_id (collect_pad->track, id);
2428 collect_pad->track->dts_only = FALSE;
2429
2430 collect_pad->capsfunc = capsfunc;
2431 gst_pad_set_active (GST_PAD (newpad), TRUE);
2432 if (!gst_element_add_pad (element, GST_PAD (newpad)))
2433 goto pad_add_failed;
2434
2435 g_free (name);
2436
2437 mux->num_streams++;
2438
2439 GST_DEBUG_OBJECT (newpad, "Added new request pad");
2440
2441 return GST_PAD (newpad);
2442
2443 /* ERROR cases */
2444 pad_add_failed:
2445 {
2446 GST_WARNING_OBJECT (mux, "Adding the new pad '%s' failed", pad_name);
2447 g_free (name);
2448 gst_object_unref (newpad);
2449 return NULL;
2450 }
2451 }
2452
2453 /**
2454 * gst_matroska_mux_release_pad:
2455 * @element: #GstMatroskaMux.
2456 * @pad: Pad to release.
2457 *
2458 * Release a previously requested pad.
2459 */
2460 static void
gst_matroska_mux_release_pad(GstElement * element,GstPad * pad)2461 gst_matroska_mux_release_pad (GstElement * element, GstPad * pad)
2462 {
2463 GstMatroskaMux *mux;
2464 GSList *walk;
2465
2466 mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
2467
2468 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
2469 GstCollectData *cdata = (GstCollectData *) walk->data;
2470 GstMatroskaPad *collect_pad = (GstMatroskaPad *) cdata;
2471
2472 if (cdata->pad == pad) {
2473 /*
2474 * observed duration, this will remain GST_CLOCK_TIME_NONE
2475 * only if the pad is resetted
2476 */
2477 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
2478
2479 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
2480 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
2481 collected_duration =
2482 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
2483 }
2484
2485 if (GST_CLOCK_TIME_IS_VALID (collected_duration)
2486 && mux->duration < collected_duration)
2487 mux->duration = collected_duration;
2488
2489 break;
2490 }
2491 }
2492
2493 gst_collect_pads_remove_pad (mux->collect, pad);
2494 if (gst_element_remove_pad (element, pad))
2495 mux->num_streams--;
2496 }
2497
2498 static void
gst_matroska_mux_write_colour(GstMatroskaMux * mux,GstMatroskaTrackVideoContext * videocontext)2499 gst_matroska_mux_write_colour (GstMatroskaMux * mux,
2500 GstMatroskaTrackVideoContext * videocontext)
2501 {
2502 GstEbmlWrite *ebml = mux->ebml_write;
2503 guint64 master;
2504 guint matrix_id = 0;
2505 guint range_id = 0;
2506 guint transfer_id = 0;
2507 guint primaries_id = 0;
2508
2509 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_VIDEOCOLOUR);
2510
2511 switch (videocontext->colorimetry.matrix) {
2512 case GST_VIDEO_COLOR_MATRIX_RGB:
2513 matrix_id = 0;
2514 break;
2515 case GST_VIDEO_COLOR_MATRIX_BT709:
2516 matrix_id = 1;
2517 break;
2518 case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
2519 matrix_id = 2;
2520 break;
2521 case GST_VIDEO_COLOR_MATRIX_FCC:
2522 matrix_id = 4;
2523 break;
2524 case GST_VIDEO_COLOR_MATRIX_BT601:
2525 matrix_id = 6;
2526 break;
2527 case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
2528 matrix_id = 7;
2529 break;
2530 case GST_VIDEO_COLOR_MATRIX_BT2020:
2531 matrix_id = 9;
2532 break;
2533 }
2534
2535 switch (videocontext->colorimetry.range) {
2536 case GST_VIDEO_COLOR_RANGE_UNKNOWN:
2537 range_id = 0;
2538 break;
2539 case GST_VIDEO_COLOR_RANGE_16_235:
2540 range_id = 1;
2541 break;
2542 case GST_VIDEO_COLOR_RANGE_0_255:
2543 range_id = 2;
2544 }
2545
2546 switch (videocontext->colorimetry.transfer) {
2547 case GST_VIDEO_TRANSFER_BT709:
2548 transfer_id = 1;
2549 break;
2550 /* FIXME: can't tell what the code should be for these */
2551 case GST_VIDEO_TRANSFER_GAMMA18:
2552 case GST_VIDEO_TRANSFER_GAMMA20:
2553 case GST_VIDEO_TRANSFER_ADOBERGB:
2554 case GST_VIDEO_TRANSFER_UNKNOWN:
2555 transfer_id = 2;
2556 break;
2557 /* Adobe RGB transfer is gamma 2.19921875 */
2558 case GST_VIDEO_TRANSFER_GAMMA22:
2559 transfer_id = 4;
2560 break;
2561 case GST_VIDEO_TRANSFER_GAMMA28:
2562 transfer_id = 5;
2563 break;
2564 case GST_VIDEO_TRANSFER_SMPTE240M:
2565 transfer_id = 7;
2566 break;
2567 case GST_VIDEO_TRANSFER_GAMMA10:
2568 transfer_id = 8;
2569 break;
2570 case GST_VIDEO_TRANSFER_LOG100:
2571 transfer_id = 9;
2572 break;
2573 case GST_VIDEO_TRANSFER_LOG316:
2574 transfer_id = 10;
2575 break;
2576 case GST_VIDEO_TRANSFER_SRGB:
2577 transfer_id = 13;
2578 break;
2579 case GST_VIDEO_TRANSFER_BT2020_12:
2580 transfer_id = 15;
2581 break;
2582 }
2583
2584 switch (videocontext->colorimetry.primaries) {
2585 case GST_VIDEO_COLOR_PRIMARIES_BT709:
2586 primaries_id = 1;
2587 break;
2588 /* FIXME: can't tell what the code should be for this one */
2589 case GST_VIDEO_COLOR_PRIMARIES_ADOBERGB:
2590 case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN:
2591 primaries_id = 2;
2592 break;
2593 case GST_VIDEO_COLOR_PRIMARIES_BT470M:
2594 primaries_id = 4;
2595 break;
2596 case GST_VIDEO_COLOR_PRIMARIES_BT470BG:
2597 primaries_id = 5;
2598 break;
2599 case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
2600 primaries_id = 6;
2601 break;
2602 case GST_VIDEO_COLOR_PRIMARIES_SMPTE240M:
2603 primaries_id = 7;
2604 break;
2605 case GST_VIDEO_COLOR_PRIMARIES_FILM:
2606 primaries_id = 8;
2607 break;
2608 case GST_VIDEO_COLOR_PRIMARIES_BT2020:
2609 primaries_id = 9;
2610 break;
2611 case GST_VIDEO_COLOR_PRIMARIES_SMPTEST428:
2612 primaries_id = 10;
2613 break;
2614 case GST_VIDEO_COLOR_PRIMARIES_SMPTERP431:
2615 primaries_id = 11;
2616 break;
2617 case GST_VIDEO_COLOR_PRIMARIES_SMPTEEG432:
2618 primaries_id = 12;
2619 break;
2620 case GST_VIDEO_COLOR_PRIMARIES_EBU3213:
2621 primaries_id = 22;
2622 break;
2623 }
2624
2625 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEORANGE, range_id);
2626 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS,
2627 matrix_id);
2628 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS,
2629 transfer_id);
2630 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPRIMARIES, primaries_id);
2631 gst_ebml_write_master_finish (ebml, master);
2632 }
2633
2634 /**
2635 * gst_matroska_mux_track_header:
2636 * @mux: #GstMatroskaMux
2637 * @context: Tack context.
2638 *
2639 * Write a track header.
2640 */
2641 static void
gst_matroska_mux_track_header(GstMatroskaMux * mux,GstMatroskaTrackContext * context)2642 gst_matroska_mux_track_header (GstMatroskaMux * mux,
2643 GstMatroskaTrackContext * context)
2644 {
2645 GstEbmlWrite *ebml = mux->ebml_write;
2646 guint64 master;
2647
2648 /* TODO: check if everything necessary is written and check default values */
2649
2650 /* track type goes before the type-specific stuff */
2651 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKNUMBER, context->num);
2652 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKTYPE, context->type);
2653
2654 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKUID, context->uid);
2655 if (context->default_duration) {
2656 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TRACKDEFAULTDURATION,
2657 context->default_duration);
2658 }
2659 if (context->language) {
2660 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKLANGUAGE,
2661 context->language);
2662 }
2663
2664 /* FIXME: until we have a nice way of getting the codecname
2665 * out of the caps, I'm not going to enable this. Too much
2666 * (useless, double, boring) work... */
2667 /* TODO: Use value from tags if any */
2668 /*gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CODECNAME,
2669 context->codec_name); */
2670 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TRACKNAME, context->name);
2671
2672 /* type-specific stuff */
2673 switch (context->type) {
2674 case GST_MATROSKA_TRACK_TYPE_VIDEO:{
2675 GstMatroskaTrackVideoContext *videocontext =
2676 (GstMatroskaTrackVideoContext *) context;
2677
2678 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKVIDEO);
2679 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELWIDTH,
2680 videocontext->pixel_width);
2681 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOPIXELHEIGHT,
2682 videocontext->pixel_height);
2683 if (videocontext->display_width && videocontext->display_height) {
2684 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYWIDTH,
2685 videocontext->display_width);
2686 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEODISPLAYHEIGHT,
2687 videocontext->display_height);
2688 }
2689 switch (videocontext->interlace_mode) {
2690 case GST_MATROSKA_INTERLACE_MODE_INTERLACED:
2691 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 1);
2692 break;
2693 case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE:
2694 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOFLAGINTERLACED, 2);
2695 break;
2696 default:
2697 break;
2698 }
2699
2700 if (videocontext->fourcc) {
2701 guint32 fcc_le = GUINT32_TO_LE (videocontext->fourcc);
2702
2703 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
2704 (gpointer) & fcc_le, 4);
2705 }
2706 gst_matroska_mux_write_colour (mux, videocontext);
2707 if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
2708 guint64 stereo_mode = 0;
2709
2710 switch (videocontext->multiview_mode) {
2711 case GST_VIDEO_MULTIVIEW_MODE_MONO:
2712 break;
2713 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
2714 if (videocontext->multiview_flags &
2715 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2716 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
2717 else
2718 stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
2719 break;
2720 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
2721 if (videocontext->multiview_flags &
2722 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2723 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
2724 else
2725 stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
2726 break;
2727 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
2728 if (videocontext->multiview_flags &
2729 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2730 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
2731 else
2732 stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
2733 break;
2734 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
2735 if (videocontext->multiview_flags &
2736 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
2737 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
2738 else
2739 stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
2740 /* FIXME: In frame-by-frame mode, left/right frame buffers need to be
2741 * laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
2742 GST_FIXME_OBJECT (mux,
2743 "Frame-by-frame stereoscopic mode not fully implemented");
2744 break;
2745 default:
2746 GST_WARNING_OBJECT (mux,
2747 "Multiview mode %d not supported in Matroska/WebM",
2748 videocontext->multiview_mode);
2749 break;
2750 }
2751
2752 if (stereo_mode != 0)
2753 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
2754 stereo_mode);
2755 }
2756 gst_ebml_write_master_finish (ebml, master);
2757
2758 break;
2759 }
2760
2761 case GST_MATROSKA_TRACK_TYPE_AUDIO:{
2762 GstMatroskaTrackAudioContext *audiocontext =
2763 (GstMatroskaTrackAudioContext *) context;
2764
2765 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKAUDIO);
2766 if (audiocontext->samplerate != 8000)
2767 gst_ebml_write_float (ebml, GST_MATROSKA_ID_AUDIOSAMPLINGFREQ,
2768 audiocontext->samplerate);
2769 if (audiocontext->channels != 1)
2770 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOCHANNELS,
2771 audiocontext->channels);
2772 if (audiocontext->bitdepth) {
2773 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_AUDIOBITDEPTH,
2774 audiocontext->bitdepth);
2775 }
2776
2777 gst_ebml_write_master_finish (ebml, master);
2778
2779 break;
2780 }
2781
2782 case GST_MATROSKA_TRACK_TYPE_SUBTITLE:{
2783 break;
2784 }
2785 default:
2786 /* doesn't need type-specific data */
2787 break;
2788 }
2789
2790 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CODECID, context->codec_id);
2791 if (context->codec_priv)
2792 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_CODECPRIVATE,
2793 context->codec_priv, context->codec_priv_size);
2794
2795 if (context->seek_preroll) {
2796 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPREROLL,
2797 context->seek_preroll);
2798 }
2799
2800 if (context->codec_delay) {
2801 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CODECDELAY,
2802 context->codec_delay);
2803 }
2804 }
2805
2806 static void
gst_matroska_mux_write_chapter_title(const gchar * title,GstEbmlWrite * ebml)2807 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
2808 {
2809 guint64 title_master;
2810
2811 title_master =
2812 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERDISPLAY);
2813
2814 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPSTRING, title);
2815 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_CHAPLANGUAGE,
2816 GST_MATROSKA_MUX_CHAPLANG);
2817
2818 gst_ebml_write_master_finish (ebml, title_master);
2819 }
2820
2821 static GstTocEntry *
gst_matroska_mux_write_chapter(GstMatroskaMux * mux,GstTocEntry * edition,GstTocEntry * entry,GstEbmlWrite * ebml,guint64 * master_chapters,guint64 * master_edition)2822 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
2823 GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
2824 guint64 * master_edition)
2825 {
2826 guint64 master_chapteratom;
2827 GList *cur;
2828 guint count, i;
2829 gchar *title;
2830 gint64 start, stop;
2831 guint64 uid;
2832 gchar s_uid[32];
2833 GstTocEntry *internal_chapter, *internal_nested;
2834 GstTagList *tags;
2835
2836 if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
2837 *master_chapters =
2838 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERS);
2839
2840 if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
2841 /* create uid for the parent */
2842 *master_edition =
2843 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
2844
2845 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID,
2846 g_ascii_strtoull (gst_toc_entry_get_uid (edition), NULL, 10));
2847 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
2848 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
2849 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
2850 }
2851
2852 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
2853 tags = gst_toc_entry_get_tags (entry);
2854 if (tags != NULL) {
2855 tags = gst_tag_list_copy (tags);
2856 }
2857
2858 /* build internal chapter */
2859 uid = gst_matroska_mux_create_uid (mux);
2860 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
2861 internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
2862
2863 /* Write the chapter entry */
2864 master_chapteratom =
2865 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
2866
2867 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
2868 /* Store the user provided UID in the ChapterStringUID */
2869 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPTERSTRINGUID,
2870 gst_toc_entry_get_uid (entry));
2871 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
2872 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
2873 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
2874 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
2875
2876 /* write current ChapterDisplays before the nested chapters */
2877 if (G_LIKELY (tags != NULL)) {
2878 count = gst_tag_list_get_tag_size (tags, GST_TAG_TITLE);
2879
2880 for (i = 0; i < count; ++i) {
2881 gst_tag_list_get_string_index (tags, GST_TAG_TITLE, i, &title);
2882 /* FIXME: handle ChapterLanguage entries */
2883 gst_matroska_mux_write_chapter_title (title, ebml);
2884 g_free (title);
2885 }
2886
2887 /* remove title tag */
2888 if (G_LIKELY (count > 0))
2889 gst_tag_list_remove_tag (tags, GST_TAG_TITLE);
2890
2891 gst_toc_entry_set_tags (internal_chapter, tags);
2892 }
2893
2894 /* Write nested chapters */
2895 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
2896 cur = cur->next) {
2897 internal_nested = gst_matroska_mux_write_chapter (mux, NULL, cur->data,
2898 ebml, NULL, NULL);
2899
2900 gst_toc_entry_append_sub_entry (internal_chapter, internal_nested);
2901 }
2902
2903 gst_ebml_write_master_finish (ebml, master_chapteratom);
2904
2905 return internal_chapter;
2906 }
2907
2908 static GstTocEntry *
gst_matroska_mux_write_chapter_edition(GstMatroskaMux * mux,GstTocEntry * edition,GList * chapters,GstEbmlWrite * ebml,guint64 * master_chapters)2909 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
2910 GstTocEntry * edition, GList * chapters, GstEbmlWrite * ebml,
2911 guint64 * master_chapters)
2912 {
2913 guint64 master_edition = 0;
2914 gchar s_uid[32];
2915 GList *cur;
2916 GstTocEntry *internal_edition, *internal_chapter;
2917 GstTagList *tags = NULL;
2918
2919 g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT,
2920 gst_matroska_mux_create_uid (mux));
2921
2922 if (edition != NULL) {
2923 /* Edition entry defined, get its tags */
2924 tags = gst_toc_entry_get_tags (edition);
2925 if (tags != NULL) {
2926 tags = gst_tag_list_copy (tags);
2927 }
2928 }
2929
2930 internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
2931 if (tags != NULL) {
2932 gst_toc_entry_set_tags (internal_edition, tags);
2933 }
2934
2935 for (cur = g_list_first (chapters); cur != NULL; cur = cur->next) {
2936 internal_chapter = gst_matroska_mux_write_chapter (mux, internal_edition,
2937 cur->data, ebml, master_chapters, &master_edition);
2938
2939 gst_toc_entry_append_sub_entry (internal_edition, internal_chapter);
2940 }
2941
2942 if (G_LIKELY (master_edition != 0))
2943 gst_ebml_write_master_finish (ebml, master_edition);
2944
2945 return internal_edition;
2946 }
2947
2948 /**
2949 * gst_matroska_mux_start:
2950 * @mux: #GstMatroskaMux
2951 *
2952 * Start a new matroska file (write headers etc...)
2953 */
2954 static void
gst_matroska_mux_start(GstMatroskaMux * mux,GstMatroskaPad * first_pad,GstBuffer * first_pad_buf)2955 gst_matroska_mux_start (GstMatroskaMux * mux, GstMatroskaPad * first_pad,
2956 GstBuffer * first_pad_buf)
2957 {
2958 GstEbmlWrite *ebml = mux->ebml_write;
2959 const gchar *doctype;
2960 guint32 seekhead_id[] = { GST_MATROSKA_ID_SEGMENTINFO,
2961 GST_MATROSKA_ID_TRACKS,
2962 GST_MATROSKA_ID_CHAPTERS,
2963 GST_MATROSKA_ID_CUES,
2964 GST_MATROSKA_ID_TAGS,
2965 0
2966 };
2967 const gchar *media_type;
2968 gboolean audio_only;
2969 guint64 master, child;
2970 GSList *collected;
2971 int i;
2972 guint tracknum = 1;
2973 GstClockTime duration = 0;
2974 guint32 segment_uid[4];
2975 GTimeVal time = { 0, 0 };
2976 gchar s_id[32];
2977 GstToc *toc;
2978
2979 /* if not streaming, check if downstream is seekable */
2980 if (!mux->ebml_write->streamable) {
2981 gboolean seekable;
2982 GstQuery *query;
2983
2984 query = gst_query_new_seeking (GST_FORMAT_BYTES);
2985 if (gst_pad_peer_query (mux->srcpad, query)) {
2986 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
2987 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
2988 } else {
2989 /* assume seeking is not supported if query not handled downstream */
2990 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
2991 seekable = FALSE;
2992 }
2993 if (!seekable) {
2994 mux->ebml_write->streamable = TRUE;
2995 g_object_notify (G_OBJECT (mux), "streamable");
2996 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
2997 "streamable=false. Will ignore that and create streamable output "
2998 "instead");
2999 }
3000 gst_query_unref (query);
3001 }
3002
3003 /* stream-start (FIXME: create id based on input ids) */
3004 g_snprintf (s_id, sizeof (s_id), "matroskamux-%08x", g_random_int ());
3005 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
3006
3007 /* output caps */
3008 audio_only = mux->num_v_streams == 0 && mux->num_a_streams > 0;
3009 if (mux->is_webm) {
3010 media_type = (audio_only) ? "audio/webm" : "video/webm";
3011 } else {
3012 media_type = (audio_only) ? "audio/x-matroska" : "video/x-matroska";
3013 }
3014 ebml->caps = gst_caps_new_empty_simple (media_type);
3015 gst_pad_set_caps (mux->srcpad, ebml->caps);
3016 /* we start with a EBML header */
3017 doctype = mux->doctype;
3018 GST_INFO_OBJECT (ebml, "DocType: %s, Version: %d",
3019 doctype, mux->doctype_version);
3020 gst_ebml_write_header (ebml, doctype, mux->doctype_version);
3021
3022 /* the rest of the header is cached */
3023 gst_ebml_write_set_cache (ebml, 0x1000);
3024
3025 /* start a segment */
3026 mux->segment_pos =
3027 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
3028 mux->segment_master = ebml->pos;
3029
3030 if (!mux->ebml_write->streamable) {
3031 /* seekhead (table of contents) - we set the positions later */
3032 mux->seekhead_pos = ebml->pos;
3033 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);
3034 for (i = 0; seekhead_id[i] != 0; i++) {
3035 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKENTRY);
3036 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKID, seekhead_id[i]);
3037 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_SEEKPOSITION, -1);
3038 gst_ebml_write_master_finish (ebml, child);
3039 }
3040 gst_ebml_write_master_finish (ebml, master);
3041 }
3042
3043 if (mux->ebml_write->streamable) {
3044 const GstTagList *tags;
3045 gboolean has_main_tags;
3046
3047 /* tags */
3048 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3049 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3050
3051 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)) {
3052 guint64 master_tags, master_tag;
3053
3054 GST_DEBUG_OBJECT (mux, "Writing tags");
3055
3056 mux->tags_pos = ebml->pos;
3057 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3058 if (has_main_tags) {
3059 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3060 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3061 gst_ebml_write_master_finish (ebml, master_tag);
3062 }
3063 gst_matroska_mux_write_streams_tags (mux);
3064 gst_ebml_write_master_finish (ebml, master_tags);
3065 }
3066 }
3067
3068 /* segment info */
3069 mux->info_pos = ebml->pos;
3070 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENTINFO);
3071
3072 /* WebM does not support SegmentUID field on SegmentInfo */
3073 if (!mux->is_webm) {
3074 for (i = 0; i < 4; i++) {
3075 segment_uid[i] = g_random_int ();
3076 }
3077 gst_ebml_write_binary (ebml, GST_MATROSKA_ID_SEGMENTUID,
3078 (guint8 *) segment_uid, 16);
3079 }
3080
3081 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TIMECODESCALE, mux->time_scale);
3082 mux->duration_pos = ebml->pos;
3083 /* get duration */
3084 if (!mux->ebml_write->streamable) {
3085 for (collected = mux->collect->data; collected;
3086 collected = g_slist_next (collected)) {
3087 GstMatroskaPad *collect_pad;
3088 GstPad *thepad;
3089 gint64 trackduration;
3090
3091 collect_pad = (GstMatroskaPad *) collected->data;
3092 thepad = collect_pad->collect.pad;
3093
3094 /* Query the total length of the track. */
3095 GST_DEBUG_OBJECT (thepad, "querying peer duration");
3096 if (gst_pad_peer_query_duration (thepad, GST_FORMAT_TIME, &trackduration)) {
3097 GST_DEBUG_OBJECT (thepad, "duration: %" GST_TIME_FORMAT,
3098 GST_TIME_ARGS (trackduration));
3099 if (trackduration != GST_CLOCK_TIME_NONE && trackduration > duration) {
3100 duration = (GstClockTime) trackduration;
3101 }
3102 }
3103 }
3104 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3105 gst_guint64_to_gdouble (duration) /
3106 gst_guint64_to_gdouble (mux->time_scale));
3107 }
3108 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_MUXINGAPP,
3109 "GStreamer matroskamux version " PACKAGE_VERSION);
3110 if (mux->writing_app && mux->writing_app[0]) {
3111 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_WRITINGAPP, mux->writing_app);
3112 }
3113 g_get_current_time (&time);
3114 gst_ebml_write_date (ebml, GST_MATROSKA_ID_DATEUTC, time.tv_sec);
3115 gst_ebml_write_master_finish (ebml, master);
3116
3117 /* tracks */
3118 mux->tracks_pos = ebml->pos;
3119 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKS);
3120
3121 for (collected = mux->collect->data; collected;
3122 collected = g_slist_next (collected)) {
3123 GstMatroskaPad *collect_pad;
3124 GstBuffer *buf;
3125
3126 collect_pad = (GstMatroskaPad *) collected->data;
3127
3128 /* This will cause an error at a later time */
3129 if (collect_pad->track->codec_id == NULL)
3130 continue;
3131
3132 /* For audio tracks, use the first buffers duration as the default
3133 * duration if we didn't get any better idea from the caps event already
3134 */
3135 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO &&
3136 collect_pad->track->default_duration == 0) {
3137 if (collect_pad == first_pad)
3138 buf = first_pad_buf ? gst_buffer_ref (first_pad_buf) : NULL;
3139 else
3140 buf = gst_collect_pads_peek (mux->collect, collected->data);
3141
3142 if (buf && GST_BUFFER_DURATION_IS_VALID (buf))
3143 collect_pad->track->default_duration =
3144 GST_BUFFER_DURATION (buf) + collect_pad->track->codec_delay;
3145 if (buf)
3146 gst_buffer_unref (buf);
3147 }
3148
3149 collect_pad->track->num = tracknum++;
3150 child = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TRACKENTRY);
3151 gst_matroska_mux_track_header (mux, collect_pad->track);
3152 gst_ebml_write_master_finish (ebml, child);
3153 /* some remaining pad/track setup */
3154 collect_pad->default_duration_scaled =
3155 gst_util_uint64_scale (collect_pad->track->default_duration,
3156 1, mux->time_scale);
3157 }
3158 gst_ebml_write_master_finish (ebml, master);
3159
3160 /* chapters */
3161 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3162 if (toc != NULL && !mux->ebml_write->streamable) {
3163 guint64 master_chapters = 0;
3164 GstTocEntry *internal_edition;
3165 GList *cur, *chapters;
3166
3167 GST_DEBUG ("Writing chapters");
3168
3169 /* There are two UIDs for Chapters:
3170 * - The ChapterUID is a mandatory unsigned integer which internally
3171 * refers to a given chapter. Except for the title & language which use
3172 * dedicated fields, this UID can also be used to add tags to the Chapter.
3173 * The tags come in a separate section of the container.
3174 * - The ChapterStringUID is an optional UTF-8 string which also uniquely
3175 * refers to a chapter but from an external perspective. It can act as a
3176 * "WebVTT cue identifier" which "can be used to reference a specific cue,
3177 * for example from script or CSS".
3178 *
3179 * The ChapterUID will be generated and checked for unicity, while the
3180 * ChapterStringUID will receive the user defined UID.
3181 *
3182 * In order to be able to refer to chapters from the tags section,
3183 * we must maintain an internal Toc tree with the generated ChapterUID
3184 * (see gst_matroska_mux_write_toc_entry_tags) */
3185
3186 /* Check whether we have editions or chapters at the root level. */
3187 cur = gst_toc_get_entries (toc);
3188 if (cur != NULL) {
3189 mux->chapters_pos = ebml->pos;
3190
3191 mux->internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
3192
3193 if (gst_toc_entry_get_entry_type (cur->data) ==
3194 GST_TOC_ENTRY_TYPE_EDITION) {
3195 /* Editions at the root level */
3196 for (; cur != NULL; cur = cur->next) {
3197 chapters = gst_toc_entry_get_sub_entries (cur->data);
3198 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3199 cur->data, chapters, ebml, &master_chapters);
3200 gst_toc_append_entry (mux->internal_toc, internal_edition);
3201 }
3202 } else {
3203 /* Chapters at the root level */
3204 internal_edition = gst_matroska_mux_write_chapter_edition (mux,
3205 NULL, cur, ebml, &master_chapters);
3206 gst_toc_append_entry (mux->internal_toc, internal_edition);
3207 }
3208
3209 /* close master element if any edition was written */
3210 if (G_LIKELY (master_chapters != 0))
3211 gst_ebml_write_master_finish (ebml, master_chapters);
3212 }
3213 }
3214
3215 /* lastly, flush the cache */
3216 gst_ebml_write_flush_cache (ebml, FALSE, 0);
3217
3218 if (toc != NULL)
3219 gst_toc_unref (toc);
3220 }
3221
3222 /* TODO: more sensible tag mappings */
3223 static const struct
3224 {
3225 const gchar *matroska_tagname;
3226 const gchar *gstreamer_tagname;
3227 }
3228 gst_matroska_tag_conv[] = {
3229 {
3230 GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, {
3231 GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, {
3232 GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, {
3233 GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, {
3234 GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, {
3235 GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, {
3236 GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, {
3237 GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, {
3238 GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, {
3239 GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, {
3240 GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, {
3241 GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, {
3242 GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, {
3243 GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, {
3244 GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE}
3245 };
3246
3247 /* Every stagefright implementation on android up to and including 6.0.1 is using
3248 libwebm with bug in matroska parsing, where it will choke on empty tag elements;
3249 so before outputting tags and tag elements we better make sure that there are
3250 actually tags we are going to write */
3251 static gboolean
gst_matroska_mux_tag_list_is_empty(const GstTagList * list)3252 gst_matroska_mux_tag_list_is_empty (const GstTagList * list)
3253 {
3254 int i;
3255 for (i = 0; i < gst_tag_list_n_tags (list); i++) {
3256 const gchar *tag = gst_tag_list_nth_tag_name (list, i);
3257 int i;
3258 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3259 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3260 if (strcmp (tagname_gst, tag) == 0) {
3261 GValue src = { 0, };
3262 gchar *dest;
3263
3264 if (!gst_tag_list_copy_value (&src, list, tag))
3265 break;
3266 dest = gst_value_serialize (&src);
3267
3268 g_value_unset (&src);
3269 if (dest) {
3270 g_free (dest);
3271 return FALSE;
3272 }
3273 }
3274 }
3275 }
3276 return TRUE;
3277 }
3278
3279 static void
gst_matroska_mux_write_simple_tag(const GstTagList * list,const gchar * tag,gpointer data)3280 gst_matroska_mux_write_simple_tag (const GstTagList * list, const gchar * tag,
3281 gpointer data)
3282 {
3283 GstEbmlWrite *ebml = (GstEbmlWrite *) data;
3284 guint i;
3285 guint64 simpletag_master;
3286
3287 for (i = 0; i < G_N_ELEMENTS (gst_matroska_tag_conv); i++) {
3288 const gchar *tagname_gst = gst_matroska_tag_conv[i].gstreamer_tagname;
3289 const gchar *tagname_mkv = gst_matroska_tag_conv[i].matroska_tagname;
3290
3291 if (strcmp (tagname_gst, tag) == 0) {
3292 GValue src = { 0, };
3293 gchar *dest;
3294
3295 if (!gst_tag_list_copy_value (&src, list, tag))
3296 break;
3297 if ((dest = gst_value_serialize (&src))) {
3298
3299 simpletag_master = gst_ebml_write_master_start (ebml,
3300 GST_MATROSKA_ID_SIMPLETAG);
3301 gst_ebml_write_ascii (ebml, GST_MATROSKA_ID_TAGNAME, tagname_mkv);
3302 gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_TAGSTRING, dest);
3303 gst_ebml_write_master_finish (ebml, simpletag_master);
3304 g_free (dest);
3305 } else {
3306 GST_WARNING ("Can't transform tag '%s' to string", tagname_mkv);
3307 }
3308 g_value_unset (&src);
3309 break;
3310 }
3311 }
3312 }
3313
3314 static void
gst_matroska_mux_write_stream_tags(GstMatroskaMux * mux,GstMatroskaPad * mpad)3315 gst_matroska_mux_write_stream_tags (GstMatroskaMux * mux, GstMatroskaPad * mpad)
3316 {
3317 guint64 master_tag, master_targets;
3318 GstEbmlWrite *ebml;
3319
3320 ebml = mux->ebml_write;
3321
3322 if (G_UNLIKELY (mpad->tags == NULL
3323 || gst_matroska_mux_tag_list_is_empty (mpad->tags)))
3324 return;
3325
3326 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3327 master_targets = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3328
3329 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETTRACKUID, mpad->track->uid);
3330
3331 gst_ebml_write_master_finish (ebml, master_targets);
3332 gst_tag_list_foreach (mpad->tags, gst_matroska_mux_write_simple_tag, ebml);
3333 gst_ebml_write_master_finish (ebml, master_tag);
3334 }
3335
3336 static void
gst_matroska_mux_write_streams_tags(GstMatroskaMux * mux)3337 gst_matroska_mux_write_streams_tags (GstMatroskaMux * mux)
3338 {
3339 GSList *walk;
3340
3341 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3342 GstMatroskaPad *collect_pad;
3343
3344 collect_pad = (GstMatroskaPad *) walk->data;
3345
3346 gst_matroska_mux_write_stream_tags (mux, collect_pad);
3347 }
3348 }
3349
3350 static gboolean
gst_matroska_mux_streams_have_tags(GstMatroskaMux * mux)3351 gst_matroska_mux_streams_have_tags (GstMatroskaMux * mux)
3352 {
3353 GSList *walk;
3354
3355 for (walk = mux->collect->data; walk; walk = g_slist_next (walk)) {
3356 GstMatroskaPad *collect_pad;
3357
3358 collect_pad = (GstMatroskaPad *) walk->data;
3359 if (!gst_matroska_mux_tag_list_is_empty (collect_pad->tags))
3360 return TRUE;
3361 }
3362 return FALSE;
3363 }
3364
3365 static void
gst_matroska_mux_write_toc_entry_tags(GstMatroskaMux * mux,const GstTocEntry * entry,guint64 * master_tags,gboolean * has_tags)3366 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
3367 const GstTocEntry * entry, guint64 * master_tags, gboolean * has_tags)
3368 {
3369 guint64 master_tag, master_targets;
3370 GstEbmlWrite *ebml;
3371 GList *cur;
3372 const GstTagList *tags;
3373
3374 ebml = mux->ebml_write;
3375
3376 tags = gst_toc_entry_get_tags (entry);
3377 if (G_UNLIKELY (tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags))) {
3378 *has_tags = TRUE;
3379
3380 if (*master_tags == 0) {
3381 mux->tags_pos = ebml->pos;
3382 *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3383 }
3384
3385 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3386 master_targets =
3387 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
3388
3389 if (gst_toc_entry_get_entry_type (entry) == GST_TOC_ENTRY_TYPE_EDITION)
3390 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
3391 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3392 else
3393 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
3394 g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
3395
3396 gst_ebml_write_master_finish (ebml, master_targets);
3397 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3398 gst_ebml_write_master_finish (ebml, master_tag);
3399 }
3400
3401 for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
3402 cur = cur->next) {
3403 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags,
3404 has_tags);
3405 }
3406 }
3407
3408 /**
3409 * gst_matroska_mux_finish:
3410 * @mux: #GstMatroskaMux
3411 *
3412 * Finish a new matroska file (write index etc...)
3413 */
3414 static void
gst_matroska_mux_finish(GstMatroskaMux * mux)3415 gst_matroska_mux_finish (GstMatroskaMux * mux)
3416 {
3417 GstEbmlWrite *ebml = mux->ebml_write;
3418 guint64 pos;
3419 guint64 duration = 0;
3420 GSList *collected;
3421 const GstTagList *tags, *toc_tags;
3422 const GstToc *toc;
3423 gboolean has_main_tags, toc_has_tags = FALSE;
3424 GList *cur;
3425
3426 /* finish last cluster */
3427 if (mux->cluster) {
3428 gst_ebml_write_master_finish (ebml, mux->cluster);
3429 }
3430
3431 /* cues */
3432 if (mux->index != NULL) {
3433 guint n;
3434 guint64 master, pointentry_master, trackpos_master;
3435
3436 mux->cues_pos = ebml->pos;
3437 gst_ebml_write_set_cache (ebml, 12 + 41 * mux->num_indexes);
3438 master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CUES);
3439
3440 for (n = 0; n < mux->num_indexes; n++) {
3441 GstMatroskaIndex *idx = &mux->index[n];
3442
3443 pointentry_master = gst_ebml_write_master_start (ebml,
3444 GST_MATROSKA_ID_POINTENTRY);
3445 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETIME,
3446 idx->time / mux->time_scale);
3447 trackpos_master = gst_ebml_write_master_start (ebml,
3448 GST_MATROSKA_ID_CUETRACKPOSITIONS);
3449 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUETRACK, idx->track);
3450 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CUECLUSTERPOSITION,
3451 idx->pos - mux->segment_master);
3452 gst_ebml_write_master_finish (ebml, trackpos_master);
3453 gst_ebml_write_master_finish (ebml, pointentry_master);
3454 }
3455
3456 gst_ebml_write_master_finish (ebml, master);
3457 gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
3458 }
3459
3460 /* tags */
3461 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
3462 has_main_tags = tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags);
3463 toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
3464
3465 if (has_main_tags || gst_matroska_mux_streams_have_tags (mux) || toc != NULL) {
3466 guint64 master_tags = 0, master_tag;
3467
3468 GST_DEBUG_OBJECT (mux, "Writing tags");
3469
3470 if (has_main_tags) {
3471 /* TODO: maybe limit via the TARGETS id by looking at the source pad */
3472 mux->tags_pos = ebml->pos;
3473 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3474 master_tag = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAG);
3475
3476 if (tags != NULL)
3477 gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
3478 if (mux->internal_toc != NULL) {
3479 toc_tags = gst_toc_get_tags (mux->internal_toc);
3480 toc_has_tags = (toc_tags != NULL);
3481 gst_tag_list_foreach (toc_tags, gst_matroska_mux_write_simple_tag,
3482 ebml);
3483 }
3484
3485 gst_ebml_write_master_finish (ebml, master_tag);
3486 }
3487
3488 if (mux->internal_toc != NULL) {
3489 for (cur = gst_toc_get_entries (mux->internal_toc); cur != NULL;
3490 cur = cur->next) {
3491 gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags,
3492 &toc_has_tags);
3493 }
3494 }
3495
3496 if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
3497 mux->tags_pos = ebml->pos;
3498 master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
3499 }
3500 gst_matroska_mux_write_streams_tags (mux);
3501
3502 if (master_tags != 0)
3503 gst_ebml_write_master_finish (ebml, master_tags);
3504 }
3505
3506 /* update seekhead. We know that:
3507 * - a seekhead contains 5 entries.
3508 * - order of entries is as above.
3509 * - a seekhead has a 4-byte header + 8-byte length
3510 * - each entry is 2-byte master, 2-byte ID pointer,
3511 * 2-byte length pointer, all 8/1-byte length, 4-
3512 * byte ID and 8-byte length pointer, where the
3513 * length pointer starts at 20.
3514 * - all entries are local to the segment (so pos - segment_master).
3515 * - so each entry is at 12 + 20 + num * 28. */
3516 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 32,
3517 mux->info_pos - mux->segment_master);
3518 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 60,
3519 mux->tracks_pos - mux->segment_master);
3520 if (toc != NULL && mux->chapters_pos > 0) {
3521 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 88,
3522 mux->chapters_pos - mux->segment_master);
3523 } else {
3524 /* void'ify */
3525 guint64 my_pos = ebml->pos;
3526
3527 gst_ebml_write_seek (ebml, mux->seekhead_pos + 68);
3528 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3529 gst_ebml_write_seek (ebml, my_pos);
3530 }
3531 if (mux->index != NULL) {
3532 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 116,
3533 mux->cues_pos - mux->segment_master);
3534 } else {
3535 /* void'ify */
3536 guint64 my_pos = ebml->pos;
3537
3538 gst_ebml_write_seek (ebml, mux->seekhead_pos + 96);
3539 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3540 gst_ebml_write_seek (ebml, my_pos);
3541 }
3542
3543 if (tags != NULL || toc_has_tags) {
3544 gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
3545 mux->tags_pos - mux->segment_master);
3546 } else {
3547 /* void'ify */
3548 guint64 my_pos = ebml->pos;
3549
3550 gst_ebml_write_seek (ebml, mux->seekhead_pos + 124);
3551 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 26);
3552 gst_ebml_write_seek (ebml, my_pos);
3553 }
3554
3555 if (toc != NULL) {
3556 gst_toc_unref (toc);
3557 }
3558
3559 /* loop tracks:
3560 * - first get the overall duration
3561 * (a released track may have left a duration in here)
3562 * - write some track header data for subtitles
3563 */
3564 duration = mux->duration;
3565 pos = ebml->pos;
3566 for (collected = mux->collect->data; collected;
3567 collected = g_slist_next (collected)) {
3568 GstMatroskaPad *collect_pad;
3569 /*
3570 * observed duration, this will never remain GST_CLOCK_TIME_NONE
3571 * since this means buffer without timestamps that is not possibile
3572 */
3573 GstClockTime collected_duration = GST_CLOCK_TIME_NONE;
3574
3575 collect_pad = (GstMatroskaPad *) collected->data;
3576
3577 GST_DEBUG_OBJECT (mux,
3578 "Pad %" GST_PTR_FORMAT " start ts %" GST_TIME_FORMAT
3579 " end ts %" GST_TIME_FORMAT, collect_pad,
3580 GST_TIME_ARGS (collect_pad->start_ts),
3581 GST_TIME_ARGS (collect_pad->end_ts));
3582
3583 if (GST_CLOCK_TIME_IS_VALID (collect_pad->start_ts) &&
3584 GST_CLOCK_TIME_IS_VALID (collect_pad->end_ts)) {
3585 collected_duration =
3586 GST_CLOCK_DIFF (collect_pad->start_ts, collect_pad->end_ts);
3587 GST_DEBUG_OBJECT (collect_pad,
3588 "final track duration: %" GST_TIME_FORMAT,
3589 GST_TIME_ARGS (collected_duration));
3590 } else {
3591 GST_WARNING_OBJECT (collect_pad, "unable to get final track duration");
3592 }
3593 if (GST_CLOCK_TIME_IS_VALID (collected_duration) &&
3594 duration < collected_duration)
3595 duration = collected_duration;
3596
3597 }
3598
3599 /* seek back (optional, but do anyway) */
3600 gst_ebml_write_seek (ebml, pos);
3601
3602 /* update duration */
3603 if (duration != 0) {
3604 GST_DEBUG_OBJECT (mux, "final total duration: %" GST_TIME_FORMAT,
3605 GST_TIME_ARGS (duration));
3606 pos = mux->ebml_write->pos;
3607 gst_ebml_write_seek (ebml, mux->duration_pos);
3608 gst_ebml_write_float (ebml, GST_MATROSKA_ID_DURATION,
3609 gst_guint64_to_gdouble (duration) /
3610 gst_guint64_to_gdouble (mux->time_scale));
3611 gst_ebml_write_seek (ebml, pos);
3612 } else {
3613 /* void'ify */
3614 guint64 my_pos = ebml->pos;
3615
3616 gst_ebml_write_seek (ebml, mux->duration_pos);
3617 gst_ebml_write_buffer_header (ebml, GST_EBML_ID_VOID, 8);
3618 gst_ebml_write_seek (ebml, my_pos);
3619 }
3620 GST_DEBUG_OBJECT (mux, "finishing segment");
3621 /* finish segment - this also writes element length */
3622 gst_ebml_write_master_finish (ebml, mux->segment_pos);
3623 }
3624
3625 /**
3626 * gst_matroska_mux_buffer_header:
3627 * @track: Track context.
3628 * @relative_timestamp: relative timestamp of the buffer
3629 * @flags: Buffer flags.
3630 *
3631 * Create a buffer containing buffer header.
3632 *
3633 * Returns: New buffer.
3634 */
3635 static GstBuffer *
gst_matroska_mux_create_buffer_header(GstMatroskaTrackContext * track,gint16 relative_timestamp,int flags)3636 gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
3637 gint16 relative_timestamp, int flags)
3638 {
3639 GstBuffer *hdr;
3640 guint8 *data = g_malloc (4);
3641
3642 hdr = gst_buffer_new_wrapped (data, 4);
3643 /* track num - FIXME: what if num >= 0x80 (unlikely)? */
3644 data[0] = track->num | 0x80;
3645 /* time relative to clustertime */
3646 GST_WRITE_UINT16_BE (data + 1, relative_timestamp);
3647
3648 /* flags */
3649 data[3] = flags;
3650
3651 return hdr;
3652 }
3653
3654 #define DIRAC_PARSE_CODE_SEQUENCE_HEADER 0x00
3655 #define DIRAC_PARSE_CODE_END_OF_SEQUENCE 0x10
3656 #define DIRAC_PARSE_CODE_IS_PICTURE(x) ((x & 0x08) != 0)
3657
3658 static GstBuffer *
gst_matroska_mux_handle_dirac_packet(GstMatroskaMux * mux,GstMatroskaPad * collect_pad,GstBuffer * buf)3659 gst_matroska_mux_handle_dirac_packet (GstMatroskaMux * mux,
3660 GstMatroskaPad * collect_pad, GstBuffer * buf)
3661 {
3662 GstMatroskaTrackVideoContext *ctx =
3663 (GstMatroskaTrackVideoContext *) collect_pad->track;
3664 GstMapInfo map;
3665 guint8 *data;
3666 gsize size;
3667 guint8 parse_code;
3668 guint32 next_parse_offset;
3669 GstBuffer *ret = NULL;
3670 gboolean is_muxing_unit = FALSE;
3671
3672 gst_buffer_map (buf, &map, GST_MAP_READ);
3673 data = map.data;
3674 size = map.size;
3675
3676 if (size < 13) {
3677 gst_buffer_unmap (buf, &map);
3678 gst_buffer_unref (buf);
3679 return ret;
3680 }
3681
3682 /* Check if this buffer contains a picture or end-of-sequence packet */
3683 while (size >= 13) {
3684 if (GST_READ_UINT32_BE (data) != 0x42424344 /* 'BBCD' */ ) {
3685 gst_buffer_unmap (buf, &map);
3686 gst_buffer_unref (buf);
3687 return ret;
3688 }
3689
3690 parse_code = GST_READ_UINT8 (data + 4);
3691 if (parse_code == DIRAC_PARSE_CODE_SEQUENCE_HEADER) {
3692 if (ctx->dirac_unit) {
3693 gst_buffer_unref (ctx->dirac_unit);
3694 ctx->dirac_unit = NULL;
3695 }
3696 } else if (DIRAC_PARSE_CODE_IS_PICTURE (parse_code) ||
3697 parse_code == DIRAC_PARSE_CODE_END_OF_SEQUENCE) {
3698 is_muxing_unit = TRUE;
3699 break;
3700 }
3701
3702 next_parse_offset = GST_READ_UINT32_BE (data + 5);
3703
3704 if (G_UNLIKELY (next_parse_offset == 0 || next_parse_offset > size))
3705 break;
3706
3707 data += next_parse_offset;
3708 size -= next_parse_offset;
3709 }
3710
3711 if (ctx->dirac_unit)
3712 ctx->dirac_unit = gst_buffer_append (ctx->dirac_unit, gst_buffer_ref (buf));
3713 else
3714 ctx->dirac_unit = gst_buffer_ref (buf);
3715
3716 gst_buffer_unmap (buf, &map);
3717
3718 if (is_muxing_unit) {
3719 ret = gst_buffer_make_writable (ctx->dirac_unit);
3720 ctx->dirac_unit = NULL;
3721 gst_buffer_copy_into (ret, buf,
3722 GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
3723 gst_buffer_unref (buf);
3724 } else {
3725 gst_buffer_unref (buf);
3726 ret = NULL;
3727 }
3728
3729 return ret;
3730 }
3731
3732 static void
gst_matroska_mux_stop_streamheader(GstMatroskaMux * mux)3733 gst_matroska_mux_stop_streamheader (GstMatroskaMux * mux)
3734 {
3735 GstCaps *caps;
3736 GstStructure *s;
3737 GValue streamheader = { 0 };
3738 GValue bufval = { 0 };
3739 GstBuffer *streamheader_buffer;
3740 GstEbmlWrite *ebml = mux->ebml_write;
3741
3742 streamheader_buffer = gst_ebml_stop_streamheader (ebml);
3743 caps = gst_caps_copy (mux->ebml_write->caps);
3744 s = gst_caps_get_structure (caps, 0);
3745 g_value_init (&streamheader, GST_TYPE_ARRAY);
3746 g_value_init (&bufval, GST_TYPE_BUFFER);
3747 GST_BUFFER_FLAG_SET (streamheader_buffer, GST_BUFFER_FLAG_HEADER);
3748 gst_value_set_buffer (&bufval, streamheader_buffer);
3749 gst_value_array_append_value (&streamheader, &bufval);
3750 g_value_unset (&bufval);
3751 gst_structure_set_value (s, "streamheader", &streamheader);
3752 g_value_unset (&streamheader);
3753 gst_caps_replace (&ebml->caps, caps);
3754 gst_buffer_unref (streamheader_buffer);
3755 gst_pad_set_caps (mux->srcpad, caps);
3756 gst_caps_unref (caps);
3757 }
3758
3759 /**
3760 * gst_matroska_mux_write_data:
3761 * @mux: #GstMatroskaMux
3762 * @collect_pad: #GstMatroskaPad with the data
3763 *
3764 * Write collected data (called from gst_matroska_mux_collected).
3765 *
3766 * Returns: Result of the gst_pad_push issued to write the data.
3767 */
3768 static GstFlowReturn
gst_matroska_mux_write_data(GstMatroskaMux * mux,GstMatroskaPad * collect_pad,GstBuffer * buf)3769 gst_matroska_mux_write_data (GstMatroskaMux * mux, GstMatroskaPad * collect_pad,
3770 GstBuffer * buf)
3771 {
3772 GstEbmlWrite *ebml = mux->ebml_write;
3773 GstBuffer *hdr;
3774 guint64 blockgroup;
3775 gboolean write_duration;
3776 gint16 relative_timestamp;
3777 gint64 relative_timestamp64;
3778 guint64 block_duration, duration_diff = 0;
3779 gboolean is_video_keyframe = FALSE;
3780 gboolean is_video_invisible = FALSE;
3781 gboolean is_audio_only = FALSE;
3782 gboolean is_min_duration_reached = FALSE;
3783 gboolean is_max_duration_exceeded = FALSE;
3784 GstMatroskamuxPad *pad;
3785 gint flags = 0;
3786 GstClockTime buffer_timestamp;
3787 GstAudioClippingMeta *cmeta = NULL;
3788
3789 /* write data */
3790 pad = GST_MATROSKAMUX_PAD_CAST (collect_pad->collect.pad);
3791
3792 /* vorbis/theora headers are retrieved from caps and put in CodecPrivate */
3793 if (collect_pad->track->xiph_headers_to_skip > 0) {
3794 --collect_pad->track->xiph_headers_to_skip;
3795 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_HEADER)) {
3796 GST_LOG_OBJECT (collect_pad->collect.pad, "dropping streamheader buffer");
3797 gst_buffer_unref (buf);
3798 return GST_FLOW_OK;
3799 }
3800 }
3801
3802 /* for dirac we have to queue up everything up to a picture unit */
3803 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_DIRAC)) {
3804 buf = gst_matroska_mux_handle_dirac_packet (mux, collect_pad, buf);
3805 if (!buf)
3806 return GST_FLOW_OK;
3807 } else if (!strcmp (collect_pad->track->codec_id,
3808 GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
3809 /* Remove the 'Frame container atom' header' */
3810 buf = gst_buffer_make_writable (buf);
3811 gst_buffer_resize (buf, 8, gst_buffer_get_size (buf) - 8);
3812 }
3813
3814 buffer_timestamp =
3815 gst_matroska_track_get_buffer_timestamp (collect_pad->track, buf);
3816
3817 /* hm, invalid timestamp (due to --to be fixed--- element upstream);
3818 * this would wreak havoc with time stored in matroska file */
3819 /* TODO: maybe calculate a timestamp by using the previous timestamp
3820 * and default duration */
3821 if (!GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
3822 GST_WARNING_OBJECT (collect_pad->collect.pad,
3823 "Invalid buffer timestamp; dropping buffer");
3824 gst_buffer_unref (buf);
3825 return GST_FLOW_OK;
3826 }
3827
3828 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
3829 && collect_pad->track->codec_delay) {
3830 /* All timestamps should include the codec delay */
3831 if (buffer_timestamp > collect_pad->track->codec_delay) {
3832 buffer_timestamp += collect_pad->track->codec_delay;
3833 } else {
3834 buffer_timestamp = 0;
3835 duration_diff = collect_pad->track->codec_delay - buffer_timestamp;
3836 }
3837 }
3838
3839 /* set the timestamp for outgoing buffers */
3840 ebml->timestamp = buffer_timestamp;
3841
3842 if (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
3843 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
3844 GST_LOG_OBJECT (mux, "have video keyframe, ts=%" GST_TIME_FORMAT,
3845 GST_TIME_ARGS (buffer_timestamp));
3846 is_video_keyframe = TRUE;
3847 } else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY) &&
3848 (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8)
3849 || !strcmp (collect_pad->track->codec_id,
3850 GST_MATROSKA_CODEC_ID_VIDEO_VP9))) {
3851 GST_LOG_OBJECT (mux,
3852 "have VP8 video invisible frame, " "ts=%" GST_TIME_FORMAT,
3853 GST_TIME_ARGS (buffer_timestamp));
3854 is_video_invisible = TRUE;
3855 }
3856 }
3857
3858 is_audio_only = (collect_pad->track->type == GST_MATROSKA_TRACK_TYPE_AUDIO) &&
3859 (mux->num_streams == 1);
3860 is_min_duration_reached = (mux->min_cluster_duration == 0
3861 || (buffer_timestamp > mux->cluster_time
3862 && (buffer_timestamp - mux->cluster_time) >=
3863 mux->min_cluster_duration));
3864 is_max_duration_exceeded = (mux->max_cluster_duration > 0
3865 && buffer_timestamp > mux->cluster_time
3866 && (buffer_timestamp - mux->cluster_time) >=
3867 MIN (G_MAXINT16 * mux->time_scale, mux->max_cluster_duration));
3868
3869 if (mux->cluster) {
3870 /* start a new cluster at every keyframe, at every GstForceKeyUnit event,
3871 * or when we may be reaching the limit of the relative timestamp */
3872 if (is_max_duration_exceeded || (is_video_keyframe
3873 && is_min_duration_reached) || mux->force_key_unit_event
3874 || (is_audio_only && is_min_duration_reached)) {
3875 if (!mux->ebml_write->streamable)
3876 gst_ebml_write_master_finish (ebml, mux->cluster);
3877
3878 /* Forward the GstForceKeyUnit event after finishing the cluster */
3879 if (mux->force_key_unit_event) {
3880 gst_pad_push_event (mux->srcpad, mux->force_key_unit_event);
3881 mux->force_key_unit_event = NULL;
3882 }
3883
3884 mux->prev_cluster_size = ebml->pos - mux->cluster_pos;
3885 mux->cluster_pos = ebml->pos;
3886 gst_ebml_write_set_cache (ebml, 0x20);
3887 mux->cluster =
3888 gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3889 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3890 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3891 GST_LOG_OBJECT (mux, "cluster timestamp %" G_GUINT64_FORMAT,
3892 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3893 gst_ebml_write_flush_cache (ebml, is_video_keyframe
3894 || is_audio_only, buffer_timestamp);
3895 mux->cluster_time = buffer_timestamp;
3896 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_PREVSIZE,
3897 mux->prev_cluster_size);
3898 }
3899 } else {
3900 /* first cluster */
3901
3902 mux->cluster_pos = ebml->pos;
3903 gst_ebml_write_set_cache (ebml, 0x20);
3904 mux->cluster = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CLUSTER);
3905 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CLUSTERTIMECODE,
3906 gst_util_uint64_scale (buffer_timestamp, 1, mux->time_scale));
3907 gst_ebml_write_flush_cache (ebml, TRUE, buffer_timestamp);
3908 mux->cluster_time = buffer_timestamp;
3909 }
3910
3911 /* We currently write index entries for all video tracks or for the audio
3912 * track in a single-track audio file. This could be improved by keeping the
3913 * index only for the *first* video track. */
3914
3915 /* TODO: index is useful for every track, should contain the number of
3916 * the block in the cluster which contains the timestamp, should also work
3917 * for files with multiple audio tracks.
3918 */
3919 if (!mux->ebml_write->streamable && (is_video_keyframe || is_audio_only)) {
3920 gint last_idx = -1;
3921
3922 if (mux->min_index_interval != 0) {
3923 for (last_idx = mux->num_indexes - 1; last_idx >= 0; last_idx--) {
3924 if (mux->index[last_idx].track == collect_pad->track->num)
3925 break;
3926 }
3927 }
3928
3929 if (last_idx < 0 || mux->min_index_interval == 0 ||
3930 (GST_CLOCK_DIFF (mux->index[last_idx].time, buffer_timestamp)
3931 >= mux->min_index_interval)) {
3932 GstMatroskaIndex *idx;
3933
3934 if (mux->num_indexes % 32 == 0) {
3935 mux->index = g_renew (GstMatroskaIndex, mux->index,
3936 mux->num_indexes + 32);
3937 }
3938 idx = &mux->index[mux->num_indexes++];
3939
3940 idx->pos = mux->cluster_pos;
3941 idx->time = buffer_timestamp;
3942 idx->track = collect_pad->track->num;
3943 }
3944 }
3945
3946 /* Check if the duration differs from the default duration. */
3947 write_duration = FALSE;
3948 block_duration = 0;
3949 if (pad->frame_duration && GST_BUFFER_DURATION_IS_VALID (buf)) {
3950 block_duration = GST_BUFFER_DURATION (buf) + duration_diff;
3951 block_duration = gst_util_uint64_scale (block_duration, 1, mux->time_scale);
3952
3953 /* small difference should be ok. */
3954 if (block_duration > collect_pad->default_duration_scaled + 1 ||
3955 block_duration < collect_pad->default_duration_scaled - 1) {
3956 write_duration = TRUE;
3957 }
3958 }
3959
3960 /* write the block, for doctype v2 use SimpleBlock if possible
3961 * one slice (*breath*).
3962 * FIXME: Need to do correct lacing! */
3963 relative_timestamp64 = buffer_timestamp - mux->cluster_time;
3964 if (relative_timestamp64 >= 0) {
3965 /* round the timestamp */
3966 relative_timestamp64 += gst_util_uint64_scale (mux->time_scale, 1, 2);
3967 relative_timestamp = gst_util_uint64_scale (relative_timestamp64, 1,
3968 mux->time_scale);
3969 } else {
3970 /* round the timestamp */
3971 relative_timestamp64 -= gst_util_uint64_scale (mux->time_scale, 1, 2);
3972 relative_timestamp =
3973 -((gint16) gst_util_uint64_scale (-relative_timestamp64, 1,
3974 mux->time_scale));
3975 }
3976
3977 if (is_video_invisible)
3978 flags |= 0x08;
3979
3980 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) {
3981 cmeta = gst_buffer_get_audio_clipping_meta (buf);
3982 g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);
3983
3984 /* Start clipping is done via header and CodecDelay */
3985 if (cmeta && !cmeta->end)
3986 cmeta = NULL;
3987 }
3988
3989 if (mux->doctype_version > 1 && !write_duration && !cmeta) {
3990 if (is_video_keyframe)
3991 flags |= 0x80;
3992
3993 hdr =
3994 gst_matroska_mux_create_buffer_header (collect_pad->track,
3995 relative_timestamp, flags);
3996 gst_ebml_write_set_cache (ebml, 0x40);
3997 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
3998 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
3999 gst_ebml_write_buffer (ebml, hdr);
4000 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4001 gst_ebml_write_buffer (ebml, buf);
4002
4003 return gst_ebml_last_write_result (ebml);
4004 } else {
4005 gst_ebml_write_set_cache (ebml, gst_buffer_get_size (buf) * 2);
4006 /* write and call order slightly unnatural,
4007 * but avoids seek and minizes pushing */
4008 blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
4009 hdr =
4010 gst_matroska_mux_create_buffer_header (collect_pad->track,
4011 relative_timestamp, flags);
4012 if (write_duration)
4013 gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION, block_duration);
4014
4015 if (!strcmp (collect_pad->track->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)
4016 && cmeta) {
4017 /* Start clipping is done via header and CodecDelay */
4018 if (cmeta->end) {
4019 guint64 end =
4020 gst_util_uint64_scale_round (cmeta->end, GST_SECOND, 48000);
4021 gst_ebml_write_sint (ebml, GST_MATROSKA_ID_DISCARDPADDING, end);
4022 }
4023 }
4024
4025 gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
4026 gst_buffer_get_size (buf) + gst_buffer_get_size (hdr));
4027 gst_ebml_write_buffer (ebml, hdr);
4028 gst_ebml_write_master_finish_full (ebml, blockgroup,
4029 gst_buffer_get_size (buf));
4030 gst_ebml_write_flush_cache (ebml, FALSE, buffer_timestamp);
4031 gst_ebml_write_buffer (ebml, buf);
4032
4033 return gst_ebml_last_write_result (ebml);
4034 }
4035 }
4036
4037 /**
4038 * gst_matroska_mux_handle_buffer:
4039 * @pads: #GstCollectPads
4040 * @uuser_data: #GstMatroskaMux
4041 *
4042 * Collectpads callback.
4043 *
4044 * Returns: #GstFlowReturn
4045 */
4046 static GstFlowReturn
gst_matroska_mux_handle_buffer(GstCollectPads * pads,GstCollectData * data,GstBuffer * buf,gpointer user_data)4047 gst_matroska_mux_handle_buffer (GstCollectPads * pads, GstCollectData * data,
4048 GstBuffer * buf, gpointer user_data)
4049 {
4050 GstClockTime buffer_timestamp;
4051 GstMatroskaMux *mux = GST_MATROSKA_MUX (user_data);
4052 GstEbmlWrite *ebml = mux->ebml_write;
4053 GstMatroskaPad *best = (GstMatroskaPad *) data;
4054 GstFlowReturn ret = GST_FLOW_OK;
4055 GST_DEBUG_OBJECT (mux, "Collected pads");
4056
4057 /* start with a header */
4058 if (mux->state == GST_MATROSKA_MUX_STATE_START) {
4059 if (mux->collect->data == NULL) {
4060 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
4061 ("No input streams configured"));
4062 return GST_FLOW_ERROR;
4063 }
4064 mux->state = GST_MATROSKA_MUX_STATE_HEADER;
4065 gst_ebml_start_streamheader (ebml);
4066 gst_matroska_mux_start (mux, best, buf);
4067 gst_matroska_mux_stop_streamheader (mux);
4068 mux->state = GST_MATROSKA_MUX_STATE_DATA;
4069 }
4070
4071 /* if there is no best pad, we have reached EOS */
4072 if (best == NULL) {
4073 GST_DEBUG_OBJECT (mux, "No best pad. Finishing...");
4074 if (!mux->ebml_write->streamable) {
4075 gst_matroska_mux_finish (mux);
4076 } else {
4077 GST_DEBUG_OBJECT (mux, "... but streamable, nothing to finish");
4078 }
4079 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
4080 ret = GST_FLOW_EOS;
4081 goto exit;
4082 }
4083
4084 if (best->track->codec_id == NULL) {
4085 GST_ERROR_OBJECT (best->collect.pad, "No codec-id for pad");
4086 ret = GST_FLOW_NOT_NEGOTIATED;
4087 goto exit;
4088 }
4089
4090 /* if we have a best stream, should also have a buffer */
4091 g_assert (buf);
4092
4093 buffer_timestamp = gst_matroska_track_get_buffer_timestamp (best->track, buf);
4094
4095 GST_DEBUG_OBJECT (best->collect.pad, "best pad - buffer ts %"
4096 GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
4097 GST_TIME_ARGS (buffer_timestamp),
4098 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
4099
4100 /* make note of first and last encountered timestamps, so we can calculate
4101 * the actual duration later when we send an updated header on eos */
4102 if (GST_CLOCK_TIME_IS_VALID (buffer_timestamp)) {
4103 GstClockTime start_ts = buffer_timestamp;
4104 GstClockTime end_ts = start_ts;
4105
4106 if (GST_BUFFER_DURATION_IS_VALID (buf))
4107 end_ts += GST_BUFFER_DURATION (buf);
4108 else if (best->track->default_duration)
4109 end_ts += best->track->default_duration;
4110
4111 if (!GST_CLOCK_TIME_IS_VALID (best->end_ts) || end_ts > best->end_ts)
4112 best->end_ts = end_ts;
4113
4114 if (G_UNLIKELY (best->start_ts == GST_CLOCK_TIME_NONE ||
4115 start_ts < best->start_ts))
4116 best->start_ts = start_ts;
4117 }
4118
4119 /* write one buffer */
4120 ret = gst_matroska_mux_write_data (mux, best, buf);
4121
4122 exit:
4123 return ret;
4124 }
4125
4126
4127 /**
4128 * gst_matroska_mux_change_state:
4129 * @element: #GstMatroskaMux
4130 * @transition: State change transition.
4131 *
4132 * Change the muxer state.
4133 *
4134 * Returns: #GstStateChangeReturn
4135 */
4136 static GstStateChangeReturn
gst_matroska_mux_change_state(GstElement * element,GstStateChange transition)4137 gst_matroska_mux_change_state (GstElement * element, GstStateChange transition)
4138 {
4139 GstStateChangeReturn ret;
4140 GstMatroskaMux *mux = GST_MATROSKA_MUX (element);
4141
4142 switch (transition) {
4143 case GST_STATE_CHANGE_NULL_TO_READY:
4144 break;
4145 case GST_STATE_CHANGE_READY_TO_PAUSED:
4146 gst_collect_pads_start (mux->collect);
4147 break;
4148 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4149 break;
4150 case GST_STATE_CHANGE_PAUSED_TO_READY:
4151 gst_collect_pads_stop (mux->collect);
4152 break;
4153 default:
4154 break;
4155 }
4156
4157 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4158
4159 switch (transition) {
4160 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4161 break;
4162 case GST_STATE_CHANGE_PAUSED_TO_READY:
4163 gst_matroska_mux_reset (GST_ELEMENT (mux));
4164 break;
4165 case GST_STATE_CHANGE_READY_TO_NULL:
4166 break;
4167 default:
4168 break;
4169 }
4170
4171 return ret;
4172 }
4173
4174 static void
gst_matroska_mux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4175 gst_matroska_mux_set_property (GObject * object,
4176 guint prop_id, const GValue * value, GParamSpec * pspec)
4177 {
4178 GstMatroskaMux *mux;
4179
4180 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4181 mux = GST_MATROSKA_MUX (object);
4182
4183 switch (prop_id) {
4184 case PROP_WRITING_APP:
4185 if (!g_value_get_string (value)) {
4186 GST_WARNING_OBJECT (mux, "writing-app property can not be NULL");
4187 break;
4188 }
4189 g_free (mux->writing_app);
4190 mux->writing_app = g_value_dup_string (value);
4191 break;
4192 case PROP_DOCTYPE_VERSION:
4193 mux->doctype_version = g_value_get_int (value);
4194 break;
4195 case PROP_MIN_INDEX_INTERVAL:
4196 mux->min_index_interval = g_value_get_int64 (value);
4197 break;
4198 case PROP_STREAMABLE:
4199 mux->ebml_write->streamable = g_value_get_boolean (value);
4200 break;
4201 case PROP_TIMECODESCALE:
4202 mux->time_scale = g_value_get_int64 (value);
4203 break;
4204 case PROP_MIN_CLUSTER_DURATION:
4205 mux->min_cluster_duration = g_value_get_int64 (value);
4206 break;
4207 case PROP_MAX_CLUSTER_DURATION:
4208 mux->max_cluster_duration = g_value_get_int64 (value);
4209 break;
4210 default:
4211 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4212 break;
4213 }
4214 }
4215
4216 static void
gst_matroska_mux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4217 gst_matroska_mux_get_property (GObject * object,
4218 guint prop_id, GValue * value, GParamSpec * pspec)
4219 {
4220 GstMatroskaMux *mux;
4221
4222 g_return_if_fail (GST_IS_MATROSKA_MUX (object));
4223 mux = GST_MATROSKA_MUX (object);
4224
4225 switch (prop_id) {
4226 case PROP_WRITING_APP:
4227 g_value_set_string (value, mux->writing_app);
4228 break;
4229 case PROP_DOCTYPE_VERSION:
4230 g_value_set_int (value, mux->doctype_version);
4231 break;
4232 case PROP_MIN_INDEX_INTERVAL:
4233 g_value_set_int64 (value, mux->min_index_interval);
4234 break;
4235 case PROP_STREAMABLE:
4236 g_value_set_boolean (value, mux->ebml_write->streamable);
4237 break;
4238 case PROP_TIMECODESCALE:
4239 g_value_set_int64 (value, mux->time_scale);
4240 break;
4241 case PROP_MIN_CLUSTER_DURATION:
4242 g_value_set_int64 (value, mux->min_cluster_duration);
4243 break;
4244 case PROP_MAX_CLUSTER_DURATION:
4245 g_value_set_int64 (value, mux->max_cluster_duration);
4246 break;
4247 default:
4248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4249 break;
4250 }
4251 }
4252