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