1 /* GStreamer
2 *
3 * Copyright (c) 2008,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * Copyright (c) 2008-2017 Collabora Ltd
5 * @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * @author: Vincent Penquerc'h <vincent.penquerch@collabora.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 /**
25 * SECTION:element-flvmux
26 * @title: flvmux
27 *
28 * flvmux muxes different streams into an FLV file.
29 *
30 * ## Example launch line
31 * |[
32 * gst-launch-1.0 -v flvmux name=mux ! filesink location=test.flv audiotestsrc samplesperbuffer=44100 num-buffers=10 ! faac ! mux. videotestsrc num-buffers=250 ! video/x-raw,framerate=25/1 ! x264enc ! mux.
33 * ]| This pipeline encodes a test audio and video stream and muxes both into an FLV file.
34 *
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <math.h>
42 #include <string.h>
43
44 #include <gst/audio/audio.h>
45
46 #include "gstflvelements.h"
47 #include "gstflvmux.h"
48 #include "amfdefs.h"
49
50 GST_DEBUG_CATEGORY_STATIC (flvmux_debug);
51 #define GST_CAT_DEFAULT flvmux_debug
52
53 enum
54 {
55 PROP_0,
56 PROP_STREAMABLE,
57 PROP_METADATACREATOR,
58 PROP_ENCODER,
59 PROP_SKIP_BACKWARDS_STREAMS,
60 };
61
62 #define DEFAULT_STREAMABLE FALSE
63 #define MAX_INDEX_ENTRIES 128
64 #define DEFAULT_METADATACREATOR "GStreamer " PACKAGE_VERSION " FLV muxer"
65 #define DEFAULT_SKIP_BACKWARDS_STREAMS FALSE
66
67 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
68 GST_PAD_SRC,
69 GST_PAD_ALWAYS,
70 GST_STATIC_CAPS ("video/x-flv")
71 );
72
73 static GstStaticPadTemplate videosink_templ = GST_STATIC_PAD_TEMPLATE ("video",
74 GST_PAD_SINK,
75 GST_PAD_REQUEST,
76 GST_STATIC_CAPS ("video/x-flash-video; "
77 "video/x-flash-screen; "
78 "video/x-vp6-flash; " "video/x-vp6-alpha; "
79 "video/x-h264, stream-format=avc;")
80 );
81
82 static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio",
83 GST_PAD_SINK,
84 GST_PAD_REQUEST,
85 GST_STATIC_CAPS
86 ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
87 "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; "
88 "audio/mpeg, mpegversion = (int) { 4, 2 }, stream-format = (string) raw; "
89 "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
90 "audio/x-raw, format = (string) { U8, S16LE}, layout = (string) interleaved, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
91 "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
92 "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
93 "audio/x-speex, channels = (int) 1, rate = (int) 16000;")
94 );
95
96 G_DEFINE_TYPE (GstFlvMuxPad, gst_flv_mux_pad, GST_TYPE_AGGREGATOR_PAD);
97
98 #define gst_flv_mux_parent_class parent_class
99 G_DEFINE_TYPE_WITH_CODE (GstFlvMux, gst_flv_mux, GST_TYPE_AGGREGATOR,
100 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
101 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (flvmux, "flvmux",
102 GST_RANK_PRIMARY, GST_TYPE_FLV_MUX, flv_element_init (plugin));
103
104 static GstFlowReturn
105 gst_flv_mux_aggregate (GstAggregator * aggregator, gboolean timeout);
106 static gboolean
107 gst_flv_mux_sink_event (GstAggregator * aggregator, GstAggregatorPad * pad,
108 GstEvent * event);
109
110 static GstAggregatorPad *gst_flv_mux_create_new_pad (GstAggregator * agg,
111 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps);
112 static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad);
113
114 static gboolean gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad,
115 GstCaps * caps);
116 static gboolean gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad,
117 GstCaps * caps);
118
119 static void gst_flv_mux_get_property (GObject * object,
120 guint prop_id, GValue * value, GParamSpec * pspec);
121 static void gst_flv_mux_set_property (GObject * object,
122 guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_flv_mux_finalize (GObject * object);
124
125 static void gst_flv_mux_reset (GstElement * element);
126 static void gst_flv_mux_reset_pad (GstFlvMuxPad * pad);
127
128 static void gst_flv_mux_pad_finalize (GObject * object);
129
130 static gboolean gst_flv_mux_start (GstAggregator * aggregator);
131 static GstFlowReturn gst_flv_mux_flush (GstAggregator * aggregator);
132 static GstClockTime gst_flv_mux_get_next_time (GstAggregator * aggregator);
133 static GstFlowReturn gst_flv_mux_write_eos (GstFlvMux * mux);
134 static GstFlowReturn gst_flv_mux_write_header (GstFlvMux * mux);
135 static GstFlowReturn gst_flv_mux_rewrite_header (GstFlvMux * mux);
136 static gboolean gst_flv_mux_are_all_pads_eos (GstFlvMux * mux);
137 static GstClockTime gst_flv_mux_query_upstream_duration (GstFlvMux * mux);
138 static GstClockTime gst_flv_mux_segment_to_running_time (const GstSegment *
139 segment, GstClockTime t);
140
141 static GstFlowReturn
gst_flv_mux_pad_flush(GstAggregatorPad * pad,GstAggregator * aggregator)142 gst_flv_mux_pad_flush (GstAggregatorPad * pad, GstAggregator * aggregator)
143 {
144 GstFlvMuxPad *flvpad = GST_FLV_MUX_PAD (pad);
145
146 flvpad->last_timestamp = GST_CLOCK_TIME_NONE;
147 flvpad->pts = GST_CLOCK_TIME_NONE;
148 flvpad->dts = GST_CLOCK_TIME_NONE;
149
150 return GST_FLOW_OK;
151 }
152
153 static gboolean
gst_flv_mux_skip_buffer(GstAggregatorPad * apad,GstAggregator * aggregator,GstBuffer * buffer)154 gst_flv_mux_skip_buffer (GstAggregatorPad * apad, GstAggregator * aggregator,
155 GstBuffer * buffer)
156 {
157 GstFlvMuxPad *fpad = GST_FLV_MUX_PAD_CAST (apad);
158 GstFlvMux *mux = GST_FLV_MUX_CAST (aggregator);
159 GstClockTime t;
160
161 if (!mux->skip_backwards_streams)
162 return FALSE;
163
164 if (fpad->drop_deltas) {
165 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
166 GST_INFO_OBJECT (fpad, "Waiting for keyframe, dropping %" GST_PTR_FORMAT,
167 buffer);
168 return TRUE;
169 } else {
170 /* drop-deltas is set and the buffer isn't delta, drop flag */
171 fpad->drop_deltas = FALSE;
172 }
173 }
174
175 if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS_OR_PTS (buffer))) {
176 t = gst_flv_mux_segment_to_running_time (&apad->segment,
177 GST_BUFFER_DTS_OR_PTS (buffer));
178
179 if (t < (GST_MSECOND * mux->last_dts)) {
180 GST_WARNING_OBJECT (fpad,
181 "Timestamp %" GST_TIME_FORMAT " going backwards from last used %"
182 GST_TIME_FORMAT ", dropping %" GST_PTR_FORMAT,
183 GST_TIME_ARGS (t), GST_TIME_ARGS (GST_MSECOND * mux->last_dts),
184 buffer);
185 /* Look for non-delta buffer */
186 fpad->drop_deltas = TRUE;
187 return TRUE;
188 }
189 }
190
191 return FALSE;
192 }
193
194 static void
gst_flv_mux_pad_class_init(GstFlvMuxPadClass * klass)195 gst_flv_mux_pad_class_init (GstFlvMuxPadClass * klass)
196 {
197 GstAggregatorPadClass *aggregatorpad_class = (GstAggregatorPadClass *) klass;
198 GObjectClass *gobject_class = (GObjectClass *) klass;
199
200 gobject_class->finalize = gst_flv_mux_pad_finalize;
201
202 aggregatorpad_class->flush = GST_DEBUG_FUNCPTR (gst_flv_mux_pad_flush);
203 aggregatorpad_class->skip_buffer =
204 GST_DEBUG_FUNCPTR (gst_flv_mux_skip_buffer);
205 }
206
207 static void
gst_flv_mux_pad_init(GstFlvMuxPad * pad)208 gst_flv_mux_pad_init (GstFlvMuxPad * pad)
209 {
210 gst_flv_mux_reset_pad (pad);
211 }
212
213 typedef struct
214 {
215 gdouble position;
216 gdouble time;
217 } GstFlvMuxIndexEntry;
218
219 static void
gst_flv_mux_index_entry_free(GstFlvMuxIndexEntry * entry)220 gst_flv_mux_index_entry_free (GstFlvMuxIndexEntry * entry)
221 {
222 g_slice_free (GstFlvMuxIndexEntry, entry);
223 }
224
225 static GstBuffer *
_gst_buffer_new_wrapped(gpointer mem,gsize size,GFreeFunc free_func)226 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
227 {
228 GstBuffer *buf;
229
230 buf = gst_buffer_new ();
231 gst_buffer_append_memory (buf,
232 gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
233 mem, size, 0, size, mem, free_func));
234
235 return buf;
236 }
237
238 static void
_gst_buffer_new_and_alloc(gsize size,GstBuffer ** buffer,guint8 ** data)239 _gst_buffer_new_and_alloc (gsize size, GstBuffer ** buffer, guint8 ** data)
240 {
241 g_return_if_fail (data != NULL);
242 g_return_if_fail (buffer != NULL);
243
244 *data = g_malloc (size);
245 *buffer = _gst_buffer_new_wrapped (*data, size, g_free);
246 }
247
248 static void
gst_flv_mux_class_init(GstFlvMuxClass * klass)249 gst_flv_mux_class_init (GstFlvMuxClass * klass)
250 {
251 GObjectClass *gobject_class;
252 GstElementClass *gstelement_class;
253 GstAggregatorClass *gstaggregator_class;
254
255 GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
256
257 gobject_class = (GObjectClass *) klass;
258 gstelement_class = (GstElementClass *) klass;
259 gstaggregator_class = (GstAggregatorClass *) klass;
260
261 gobject_class->get_property = gst_flv_mux_get_property;
262 gobject_class->set_property = gst_flv_mux_set_property;
263 gobject_class->finalize = gst_flv_mux_finalize;
264
265 /* FIXME: ideally the right mode of operation should be detected
266 * automatically using queries when parameter not specified. */
267 /**
268 * GstFlvMux:streamable
269 *
270 * If True, the output will be streaming friendly. (ie without indexes and
271 * duration)
272 */
273 g_object_class_install_property (gobject_class, PROP_STREAMABLE,
274 g_param_spec_boolean ("streamable", "streamable",
275 "If set to true, the output should be as if it is to be streamed "
276 "and hence no indexes written or duration written.",
277 DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278 g_object_class_install_property (gobject_class, PROP_METADATACREATOR,
279 g_param_spec_string ("metadatacreator", "metadatacreator",
280 "The value of metadatacreator in the meta packet.",
281 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282
283 g_object_class_install_property (gobject_class, PROP_ENCODER,
284 g_param_spec_string ("encoder", "encoder",
285 "The value of encoder in the meta packet.",
286 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
287 g_object_class_install_property (gobject_class, PROP_SKIP_BACKWARDS_STREAMS,
288 g_param_spec_boolean ("skip-backwards-streams", "Skip backwards streams",
289 "If set to true, streams that go backwards related to the other stream "
290 "will have buffers dropped until they reach the correct timestamp",
291 DEFAULT_SKIP_BACKWARDS_STREAMS,
292 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293
294 gstaggregator_class->create_new_pad =
295 GST_DEBUG_FUNCPTR (gst_flv_mux_create_new_pad);
296 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
297
298 gstaggregator_class->start = GST_DEBUG_FUNCPTR (gst_flv_mux_start);
299 gstaggregator_class->aggregate = GST_DEBUG_FUNCPTR (gst_flv_mux_aggregate);
300 gstaggregator_class->sink_event = GST_DEBUG_FUNCPTR (gst_flv_mux_sink_event);
301 gstaggregator_class->flush = GST_DEBUG_FUNCPTR (gst_flv_mux_flush);
302 gstaggregator_class->get_next_time =
303 GST_DEBUG_FUNCPTR (gst_flv_mux_get_next_time);
304 gstaggregator_class->negotiate = NULL;
305
306 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
307 &videosink_templ, GST_TYPE_FLV_MUX_PAD);
308 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
309 &audiosink_templ, GST_TYPE_FLV_MUX_PAD);
310 gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
311 &src_templ, GST_TYPE_AGGREGATOR_PAD);
312 gst_element_class_set_static_metadata (gstelement_class, "FLV muxer",
313 "Codec/Muxer",
314 "Muxes video/audio streams into a FLV stream",
315 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
316
317 GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
318
319 gst_type_mark_as_plugin_api (GST_TYPE_FLV_MUX_PAD, 0);
320 }
321
322 static void
gst_flv_mux_init(GstFlvMux * mux)323 gst_flv_mux_init (GstFlvMux * mux)
324 {
325 mux->srcpad = GST_AGGREGATOR_CAST (mux)->srcpad;
326
327 /* property */
328 mux->streamable = DEFAULT_STREAMABLE;
329 mux->metadatacreator = g_strdup (DEFAULT_METADATACREATOR);
330 mux->encoder = g_strdup (DEFAULT_METADATACREATOR);
331
332 mux->new_tags = FALSE;
333
334 gst_flv_mux_reset (GST_ELEMENT (mux));
335 }
336
337 static void
gst_flv_mux_finalize(GObject * object)338 gst_flv_mux_finalize (GObject * object)
339 {
340 GstFlvMux *mux = GST_FLV_MUX (object);
341
342 gst_flv_mux_reset (GST_ELEMENT (object));
343 g_free (mux->metadatacreator);
344 g_free (mux->encoder);
345
346 G_OBJECT_CLASS (gst_flv_mux_parent_class)->finalize (object);
347 }
348
349 static void
gst_flv_mux_pad_finalize(GObject * object)350 gst_flv_mux_pad_finalize (GObject * object)
351 {
352 GstFlvMuxPad *pad = GST_FLV_MUX_PAD (object);
353
354 gst_flv_mux_reset_pad (pad);
355
356 G_OBJECT_CLASS (gst_flv_mux_pad_parent_class)->finalize (object);
357 }
358
359 static GstFlowReturn
gst_flv_mux_flush(GstAggregator * aggregator)360 gst_flv_mux_flush (GstAggregator * aggregator)
361 {
362 /* TODO: What is the right behaviour on flush? Should we just ignore it ?
363 * This still needs to be defined. */
364
365 gst_flv_mux_reset (GST_ELEMENT (aggregator));
366 return GST_FLOW_OK;
367 }
368
369 static gboolean
gst_flv_mux_start(GstAggregator * aggregator)370 gst_flv_mux_start (GstAggregator * aggregator)
371 {
372 gst_flv_mux_reset (GST_ELEMENT (aggregator));
373 return TRUE;
374 }
375
376 static void
gst_flv_mux_reset(GstElement * element)377 gst_flv_mux_reset (GstElement * element)
378 {
379 GstFlvMux *mux = GST_FLV_MUX (element);
380
381 g_list_foreach (mux->index, (GFunc) gst_flv_mux_index_entry_free, NULL);
382 g_list_free (mux->index);
383 mux->index = NULL;
384 mux->byte_count = 0;
385
386 mux->duration = GST_CLOCK_TIME_NONE;
387 mux->new_tags = FALSE;
388 mux->first_timestamp = GST_CLOCK_TIME_NONE;
389 mux->last_dts = 0;
390
391 mux->state = GST_FLV_MUX_STATE_HEADER;
392 mux->sent_header = FALSE;
393
394 /* tags */
395 gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
396 }
397
398 /* Extract per-codec relevant tags for
399 * insertion into the metadata later - ie bitrate,
400 * but maybe others in the future */
401 static void
gst_flv_mux_store_codec_tags(GstFlvMux * mux,GstFlvMuxPad * flvpad,GstTagList * list)402 gst_flv_mux_store_codec_tags (GstFlvMux * mux,
403 GstFlvMuxPad * flvpad, GstTagList * list)
404 {
405 /* Look for a bitrate as either nominal or actual bitrate tag */
406 if (gst_tag_list_get_uint (list, GST_TAG_NOMINAL_BITRATE, &flvpad->bitrate)
407 || gst_tag_list_get_uint (list, GST_TAG_BITRATE, &flvpad->bitrate)) {
408 GST_DEBUG_OBJECT (mux, "Stored bitrate for pad %" GST_PTR_FORMAT " = %u",
409 flvpad, flvpad->bitrate);
410 }
411 }
412
413 static gboolean
gst_flv_mux_sink_event(GstAggregator * aggregator,GstAggregatorPad * pad,GstEvent * event)414 gst_flv_mux_sink_event (GstAggregator * aggregator, GstAggregatorPad * pad,
415 GstEvent * event)
416 {
417 GstFlvMux *mux = GST_FLV_MUX (aggregator);
418 GstFlvMuxPad *flvpad = (GstFlvMuxPad *) pad;
419 gboolean ret = TRUE;
420
421 switch (GST_EVENT_TYPE (event)) {
422 case GST_EVENT_CAPS:
423 {
424 GstCaps *caps;
425
426 gst_event_parse_caps (event, &caps);
427
428 if (mux->video_pad == flvpad) {
429 ret = gst_flv_mux_video_pad_setcaps (flvpad, caps);
430 } else if (mux->audio_pad == flvpad) {
431 ret = gst_flv_mux_audio_pad_setcaps (flvpad, caps);
432 } else {
433 g_assert_not_reached ();
434 }
435 break;
436 }
437 case GST_EVENT_TAG:{
438 GstTagList *list;
439 GstTagSetter *setter = GST_TAG_SETTER (mux);
440 const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
441
442 gst_event_parse_tag (event, &list);
443 gst_tag_setter_merge_tags (setter, list, mode);
444 gst_flv_mux_store_codec_tags (mux, flvpad, list);
445 mux->new_tags = TRUE;
446 ret = TRUE;
447 break;
448 }
449 default:
450 break;
451 }
452
453 if (!ret)
454 return FALSE;
455
456 return GST_AGGREGATOR_CLASS (parent_class)->sink_event (aggregator, pad,
457 event);;
458 }
459
460 static gboolean
gst_flv_mux_video_pad_setcaps(GstFlvMuxPad * pad,GstCaps * caps)461 gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
462 {
463 GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
464 gboolean ret = TRUE;
465 GstStructure *s;
466 guint old_codec;
467 GstBuffer *old_codec_data = NULL;
468
469 old_codec = pad->codec;
470 if (pad->codec_data)
471 old_codec_data = gst_buffer_ref (pad->codec_data);
472
473 s = gst_caps_get_structure (caps, 0);
474
475 if (strcmp (gst_structure_get_name (s), "video/x-flash-video") == 0) {
476 pad->codec = 2;
477 } else if (strcmp (gst_structure_get_name (s), "video/x-flash-screen") == 0) {
478 pad->codec = 3;
479 } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-flash") == 0) {
480 pad->codec = 4;
481 } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-alpha") == 0) {
482 pad->codec = 5;
483 } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) {
484 pad->codec = 7;
485 } else {
486 ret = FALSE;
487 }
488
489 if (ret && gst_structure_has_field (s, "codec_data")) {
490 const GValue *val = gst_structure_get_value (s, "codec_data");
491
492 if (val)
493 gst_buffer_replace (&pad->codec_data, gst_value_get_buffer (val));
494 else if (!val && pad->codec_data)
495 gst_buffer_unref (pad->codec_data);
496 }
497
498 if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
499 if (old_codec != pad->codec) {
500 pad->info_changed = TRUE;
501 }
502
503 if (old_codec_data && pad->codec_data) {
504 GstMapInfo map;
505
506 gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
507 if (map.size != gst_buffer_get_size (pad->codec_data) ||
508 gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
509 pad->info_changed = TRUE;
510
511 gst_buffer_unmap (old_codec_data, &map);
512 } else if (!old_codec_data && pad->codec_data) {
513 pad->info_changed = TRUE;
514 }
515
516 if (pad->info_changed)
517 mux->state = GST_FLV_MUX_STATE_HEADER;
518 }
519
520 if (old_codec_data)
521 gst_buffer_unref (old_codec_data);
522
523 gst_object_unref (mux);
524
525 return ret;
526 }
527
528 static gboolean
gst_flv_mux_audio_pad_setcaps(GstFlvMuxPad * pad,GstCaps * caps)529 gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
530 {
531 GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
532 gboolean ret = TRUE;
533 GstStructure *s;
534 guint old_codec, old_rate, old_width, old_channels;
535 GstBuffer *old_codec_data = NULL;
536
537 old_codec = pad->codec;
538 old_rate = pad->rate;
539 old_width = pad->width;
540 old_channels = pad->channels;
541 if (pad->codec_data)
542 old_codec_data = gst_buffer_ref (pad->codec_data);
543
544 s = gst_caps_get_structure (caps, 0);
545
546 if (strcmp (gst_structure_get_name (s), "audio/x-adpcm") == 0) {
547 const gchar *layout = gst_structure_get_string (s, "layout");
548 if (layout && strcmp (layout, "swf") == 0) {
549 pad->codec = 1;
550 } else {
551 ret = FALSE;
552 }
553 } else if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) {
554 gint mpegversion;
555
556 if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
557 if (mpegversion == 1) {
558 gint layer;
559
560 if (gst_structure_get_int (s, "layer", &layer) && layer == 3) {
561 gint rate;
562
563 if (gst_structure_get_int (s, "rate", &rate) && rate == 8000)
564 pad->codec = 14;
565 else
566 pad->codec = 2;
567 } else {
568 ret = FALSE;
569 }
570 } else if (mpegversion == 4 || mpegversion == 2) {
571 pad->codec = 10;
572 } else {
573 ret = FALSE;
574 }
575 } else {
576 ret = FALSE;
577 }
578 } else if (strcmp (gst_structure_get_name (s), "audio/x-nellymoser") == 0) {
579 gint rate, channels;
580
581 if (gst_structure_get_int (s, "rate", &rate)
582 && gst_structure_get_int (s, "channels", &channels)) {
583 if (channels == 1 && rate == 16000)
584 pad->codec = 4;
585 else if (channels == 1 && rate == 8000)
586 pad->codec = 5;
587 else
588 pad->codec = 6;
589 } else {
590 pad->codec = 6;
591 }
592 } else if (strcmp (gst_structure_get_name (s), "audio/x-raw") == 0) {
593 GstAudioInfo info;
594
595 if (gst_audio_info_from_caps (&info, caps)) {
596 pad->codec = 3;
597
598 if (GST_AUDIO_INFO_WIDTH (&info) == 8)
599 pad->width = 0;
600 else if (GST_AUDIO_INFO_WIDTH (&info) == 16)
601 pad->width = 1;
602 else
603 ret = FALSE;
604 } else
605 ret = FALSE;
606 } else if (strcmp (gst_structure_get_name (s), "audio/x-alaw") == 0) {
607 pad->codec = 7;
608 } else if (strcmp (gst_structure_get_name (s), "audio/x-mulaw") == 0) {
609 pad->codec = 8;
610 } else if (strcmp (gst_structure_get_name (s), "audio/x-speex") == 0) {
611 pad->codec = 11;
612 } else {
613 ret = FALSE;
614 }
615
616 if (ret) {
617 gint rate, channels;
618
619 if (gst_structure_get_int (s, "rate", &rate)) {
620 if (pad->codec == 10)
621 pad->rate = 3;
622 else if (rate == 5512)
623 pad->rate = 0;
624 else if (rate == 11025)
625 pad->rate = 1;
626 else if (rate == 22050)
627 pad->rate = 2;
628 else if (rate == 44100)
629 pad->rate = 3;
630 else if (rate == 8000 && (pad->codec == 5 || pad->codec == 14
631 || pad->codec == 7 || pad->codec == 8))
632 pad->rate = 0;
633 else if (rate == 16000 && (pad->codec == 4 || pad->codec == 11))
634 pad->rate = 0;
635 else
636 ret = FALSE;
637 } else if (pad->codec == 10) {
638 pad->rate = 3;
639 } else {
640 ret = FALSE;
641 }
642
643 if (gst_structure_get_int (s, "channels", &channels)) {
644 if (pad->codec == 4 || pad->codec == 5
645 || pad->codec == 6 || pad->codec == 11)
646 pad->channels = 0;
647 else if (pad->codec == 10)
648 pad->channels = 1;
649 else if (channels == 1)
650 pad->channels = 0;
651 else if (channels == 2)
652 pad->channels = 1;
653 else
654 ret = FALSE;
655 } else if (pad->codec == 4 || pad->codec == 5 || pad->codec == 6) {
656 pad->channels = 0;
657 } else if (pad->codec == 10) {
658 pad->channels = 1;
659 } else {
660 ret = FALSE;
661 }
662
663 if (pad->codec != 3)
664 pad->width = 1;
665 }
666
667 if (ret && gst_structure_has_field (s, "codec_data")) {
668 const GValue *val = gst_structure_get_value (s, "codec_data");
669
670 if (val)
671 gst_buffer_replace (&pad->codec_data, gst_value_get_buffer (val));
672 else if (!val && pad->codec_data)
673 gst_buffer_unref (pad->codec_data);
674 }
675
676 if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
677 if (old_codec != pad->codec || old_rate != pad->rate ||
678 old_width != pad->width || old_channels != pad->channels) {
679 pad->info_changed = TRUE;
680 }
681
682 if (old_codec_data && pad->codec_data) {
683 GstMapInfo map;
684
685 gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
686 if (map.size != gst_buffer_get_size (pad->codec_data) ||
687 gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
688 pad->info_changed = TRUE;
689
690 gst_buffer_unmap (old_codec_data, &map);
691 } else if (!old_codec_data && pad->codec_data) {
692 pad->info_changed = TRUE;
693 }
694
695 if (pad->info_changed)
696 mux->state = GST_FLV_MUX_STATE_HEADER;
697 }
698
699 if (old_codec_data)
700 gst_buffer_unref (old_codec_data);
701
702 gst_object_unref (mux);
703
704 return ret;
705 }
706
707 static void
gst_flv_mux_reset_pad(GstFlvMuxPad * pad)708 gst_flv_mux_reset_pad (GstFlvMuxPad * pad)
709 {
710 GST_DEBUG_OBJECT (pad, "resetting pad");
711
712 if (pad->codec_data)
713 gst_buffer_unref (pad->codec_data);
714 pad->codec_data = NULL;
715 pad->codec = G_MAXUINT;
716 pad->rate = G_MAXUINT;
717 pad->width = G_MAXUINT;
718 pad->channels = G_MAXUINT;
719 pad->info_changed = FALSE;
720 pad->drop_deltas = FALSE;
721
722 gst_flv_mux_pad_flush (GST_AGGREGATOR_PAD_CAST (pad), NULL);
723 }
724
725 static GstAggregatorPad *
gst_flv_mux_create_new_pad(GstAggregator * agg,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)726 gst_flv_mux_create_new_pad (GstAggregator * agg,
727 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
728 {
729 GstElementClass *klass = GST_ELEMENT_GET_CLASS (agg);
730 GstAggregatorPad *aggpad;
731 GstFlvMux *mux = GST_FLV_MUX (agg);
732 GstFlvMuxPad *pad = NULL;
733 const gchar *name = NULL;
734 gboolean video;
735
736 if (mux->state != GST_FLV_MUX_STATE_HEADER && !mux->streamable) {
737 GST_ELEMENT_WARNING (mux, STREAM, MUX,
738 ("Requested a late stream in a non-streamable file"),
739 ("Stream added after file started and therefore won't be playable"));
740 return NULL;
741 }
742
743 if (templ == gst_element_class_get_pad_template (klass, "audio")) {
744 if (mux->audio_pad) {
745 GST_WARNING_OBJECT (mux, "Already have an audio pad");
746 return NULL;
747 }
748 name = "audio";
749 video = FALSE;
750 } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
751 if (mux->video_pad) {
752 GST_WARNING_OBJECT (mux, "Already have a video pad");
753 return NULL;
754 }
755 name = "video";
756 video = TRUE;
757 } else {
758 GST_WARNING_OBJECT (mux, "Invalid template");
759 return NULL;
760 }
761
762 aggpad =
763 GST_AGGREGATOR_CLASS (gst_flv_mux_parent_class)->create_new_pad (agg,
764 templ, name, caps);
765 if (aggpad == NULL)
766 return NULL;
767
768 pad = GST_FLV_MUX_PAD (aggpad);
769
770 gst_flv_mux_reset_pad (pad);
771
772 if (video)
773 mux->video_pad = pad;
774 else
775 mux->audio_pad = pad;
776
777 return aggpad;
778 }
779
780 static void
gst_flv_mux_release_pad(GstElement * element,GstPad * pad)781 gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
782 {
783 GstFlvMux *mux = GST_FLV_MUX (element);
784 GstFlvMuxPad *flvpad = GST_FLV_MUX_PAD (gst_object_ref (pad));
785
786 GST_ELEMENT_CLASS (gst_flv_mux_parent_class)->release_pad (element, pad);
787
788 gst_flv_mux_reset_pad (flvpad);
789
790 if (flvpad == mux->video_pad) {
791 mux->video_pad = NULL;
792 } else if (flvpad == mux->audio_pad) {
793 mux->audio_pad = NULL;
794 } else {
795 GST_WARNING_OBJECT (pad, "Pad is not known audio or video pad");
796 }
797
798 gst_object_unref (flvpad);
799 }
800
801 static GstFlowReturn
gst_flv_mux_push(GstFlvMux * mux,GstBuffer * buffer)802 gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer)
803 {
804 GstAggregator *agg = GST_AGGREGATOR (mux);
805 GstAggregatorPad *srcpad = GST_AGGREGATOR_PAD (agg->srcpad);
806
807 if (GST_BUFFER_PTS_IS_VALID (buffer))
808 srcpad->segment.position = GST_BUFFER_PTS (buffer);
809
810 /* pushing the buffer that rewrites the header will make it no longer be the
811 * total output size in bytes, but it doesn't matter at that point */
812 mux->byte_count += gst_buffer_get_size (buffer);
813
814 return gst_aggregator_finish_buffer (GST_AGGREGATOR_CAST (mux), buffer);
815 }
816
817 static GstBuffer *
gst_flv_mux_create_header(GstFlvMux * mux)818 gst_flv_mux_create_header (GstFlvMux * mux)
819 {
820 GstBuffer *header;
821 guint8 *data;
822 gboolean have_audio;
823 gboolean have_video;
824
825 _gst_buffer_new_and_alloc (9 + 4, &header, &data);
826
827 data[0] = 'F';
828 data[1] = 'L';
829 data[2] = 'V';
830 data[3] = 0x01; /* Version */
831
832 have_audio = (mux->audio_pad && mux->audio_pad->codec != G_MAXUINT);
833 have_video = (mux->video_pad && mux->video_pad->codec != G_MAXUINT);
834
835 data[4] = (have_audio << 2) | have_video; /* flags */
836 GST_WRITE_UINT32_BE (data + 5, 9); /* data offset */
837 GST_WRITE_UINT32_BE (data + 9, 0); /* previous tag size */
838
839 return header;
840 }
841
842 static GstBuffer *
gst_flv_mux_preallocate_index(GstFlvMux * mux)843 gst_flv_mux_preallocate_index (GstFlvMux * mux)
844 {
845 GstBuffer *tmp;
846 guint8 *data;
847 gint preallocate_size;
848
849 /* preallocate index of size:
850 * - 'keyframes' ECMA array key: 2 + 9 = 11 bytes
851 * - nested ECMA array header, length and end marker: 8 bytes
852 * - 'times' and 'filepositions' keys: 22 bytes
853 * - two strict arrays headers and lengths: 10 bytes
854 * - each index entry: 18 bytes
855 */
856 preallocate_size = 11 + 8 + 22 + 10 + MAX_INDEX_ENTRIES * 18;
857 GST_DEBUG_OBJECT (mux, "preallocating %d bytes for the index",
858 preallocate_size);
859
860 _gst_buffer_new_and_alloc (preallocate_size, &tmp, &data);
861
862 /* prefill the space with a gstfiller: <spaces> script tag variable */
863 GST_WRITE_UINT16_BE (data, 9); /* 9 characters */
864 memcpy (data + 2, "gstfiller", 9);
865 GST_WRITE_UINT8 (data + 11, AMF0_STRING_MARKER); /* a string value */
866 GST_WRITE_UINT16_BE (data + 12, preallocate_size - 14);
867 memset (data + 14, ' ', preallocate_size - 14); /* the rest is spaces */
868 return tmp;
869 }
870
871 static GstBuffer *
gst_flv_mux_create_number_script_value(const gchar * name,gdouble value)872 gst_flv_mux_create_number_script_value (const gchar * name, gdouble value)
873 {
874 GstBuffer *tmp;
875 guint8 *data;
876 gsize len = strlen (name);
877
878 _gst_buffer_new_and_alloc (2 + len + 1 + 8, &tmp, &data);
879
880 GST_WRITE_UINT16_BE (data, len);
881 data += 2; /* name length */
882 memcpy (data, name, len);
883 data += len;
884 *data++ = AMF0_NUMBER_MARKER; /* double type */
885 GST_WRITE_DOUBLE_BE (data, value);
886
887 return tmp;
888 }
889
890 static GstBuffer *
gst_flv_mux_create_metadata(GstFlvMux * mux)891 gst_flv_mux_create_metadata (GstFlvMux * mux)
892 {
893 const GstTagList *tags;
894 GstBuffer *script_tag, *tmp;
895 GstMapInfo map;
896 guint64 dts;
897 guint8 *data;
898 gint i, n_tags, tags_written = 0;
899
900 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
901
902 dts = mux->last_dts;
903
904 /* Timestamp must start at zero */
905 if (GST_CLOCK_TIME_IS_VALID (mux->first_timestamp)) {
906 dts -= mux->first_timestamp / GST_MSECOND;
907 }
908
909 GST_DEBUG_OBJECT (mux,
910 "Creating metadata, dts %" G_GUINT64_FORMAT ", tags = %" GST_PTR_FORMAT,
911 dts, tags);
912
913 if (dts > G_MAXUINT32) {
914 GST_LOG_OBJECT (mux,
915 "Detected rollover, timestamp will be truncated (previous:%"
916 G_GUINT64_FORMAT ", new:%u)", dts, (guint32) dts);
917 }
918
919 /* FIXME perhaps some bytewriter'ing here ... */
920
921 _gst_buffer_new_and_alloc (11, &script_tag, &data);
922
923 data[0] = 18;
924
925 /* Data size, unknown for now */
926 data[1] = 0;
927 data[2] = 0;
928 data[3] = 0;
929
930 /* Timestamp */
931 GST_WRITE_UINT24_BE (data + 4, dts);
932 data[7] = (((guint) dts) >> 24) & 0xff;
933
934 /* Stream ID */
935 data[8] = data[9] = data[10] = 0;
936
937 _gst_buffer_new_and_alloc (13, &tmp, &data);
938 data[0] = AMF0_STRING_MARKER; /* string */
939 data[1] = 0;
940 data[2] = 10; /* length 10 */
941 memcpy (&data[3], "onMetaData", 10);
942
943 script_tag = gst_buffer_append (script_tag, tmp);
944
945 n_tags = (tags) ? gst_tag_list_n_tags (tags) : 0;
946 _gst_buffer_new_and_alloc (5, &tmp, &data);
947 data[0] = 8; /* ECMA array */
948 GST_WRITE_UINT32_BE (data + 1, n_tags);
949 script_tag = gst_buffer_append (script_tag, tmp);
950
951 /* Some players expect the 'duration' to be always set. Fill it out later,
952 after querying the pads or after getting EOS */
953 if (!mux->streamable) {
954 tmp = gst_flv_mux_create_number_script_value ("duration", 86400);
955 script_tag = gst_buffer_append (script_tag, tmp);
956 tags_written++;
957
958 /* Sometimes the information about the total file size is useful for the
959 player. It will be filled later, after getting EOS */
960 tmp = gst_flv_mux_create_number_script_value ("filesize", 0);
961 script_tag = gst_buffer_append (script_tag, tmp);
962 tags_written++;
963
964 /* Preallocate space for the index to be written at EOS */
965 tmp = gst_flv_mux_preallocate_index (mux);
966 script_tag = gst_buffer_append (script_tag, tmp);
967 } else {
968 GST_DEBUG_OBJECT (mux, "not preallocating index, streamable mode");
969 }
970
971 for (i = 0; tags && i < n_tags; i++) {
972 const gchar *tag_name = gst_tag_list_nth_tag_name (tags, i);
973 if (!strcmp (tag_name, GST_TAG_DURATION)) {
974 GstClockTime dur;
975
976 if (!gst_tag_list_get_uint64 (tags, GST_TAG_DURATION, &dur))
977 continue;
978 mux->duration = dur;
979 } else if (!strcmp (tag_name, GST_TAG_ARTIST) ||
980 !strcmp (tag_name, GST_TAG_TITLE)) {
981 gchar *s;
982 const gchar *t = NULL;
983
984 if (!strcmp (tag_name, GST_TAG_ARTIST))
985 t = "creator";
986 else if (!strcmp (tag_name, GST_TAG_TITLE))
987 t = "title";
988
989 if (!gst_tag_list_get_string (tags, tag_name, &s))
990 continue;
991
992 _gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s),
993 &tmp, &data);
994 data[0] = 0; /* tag name length */
995 data[1] = strlen (t);
996 memcpy (&data[2], t, strlen (t));
997 data[2 + strlen (t)] = 2; /* string */
998 data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff;
999 data[4 + strlen (t)] = (strlen (s)) & 0xff;
1000 memcpy (&data[5 + strlen (t)], s, strlen (s));
1001 script_tag = gst_buffer_append (script_tag, tmp);
1002
1003 g_free (s);
1004 tags_written++;
1005 }
1006 }
1007
1008 if (!mux->streamable && mux->duration == GST_CLOCK_TIME_NONE) {
1009 mux->duration = gst_flv_mux_query_upstream_duration (mux);
1010 }
1011
1012 if (!mux->streamable && mux->duration != GST_CLOCK_TIME_NONE) {
1013 gdouble d;
1014 GstMapInfo map;
1015
1016 d = gst_guint64_to_gdouble (mux->duration);
1017 d /= (gdouble) GST_SECOND;
1018
1019 GST_DEBUG_OBJECT (mux, "determined the duration to be %f", d);
1020 gst_buffer_map (script_tag, &map, GST_MAP_WRITE);
1021 GST_WRITE_DOUBLE_BE (map.data + 29 + 2 + 8 + 1, d);
1022 gst_buffer_unmap (script_tag, &map);
1023 }
1024
1025 if (mux->video_pad && mux->video_pad->codec != G_MAXUINT) {
1026 GstCaps *caps = NULL;
1027
1028 if (mux->video_pad)
1029 caps = gst_pad_get_current_caps (GST_PAD (mux->video_pad));
1030
1031 if (caps != NULL) {
1032 GstStructure *s;
1033 gint size;
1034 gint num, den;
1035
1036 GST_DEBUG_OBJECT (mux, "putting videocodecid %d in the metadata",
1037 mux->video_pad->codec);
1038
1039 tmp = gst_flv_mux_create_number_script_value ("videocodecid",
1040 mux->video_pad->codec);
1041 script_tag = gst_buffer_append (script_tag, tmp);
1042 tags_written++;
1043
1044 s = gst_caps_get_structure (caps, 0);
1045 gst_caps_unref (caps);
1046
1047 if (gst_structure_get_int (s, "width", &size)) {
1048 GST_DEBUG_OBJECT (mux, "putting width %d in the metadata", size);
1049
1050 tmp = gst_flv_mux_create_number_script_value ("width", size);
1051 script_tag = gst_buffer_append (script_tag, tmp);
1052 tags_written++;
1053 }
1054
1055 if (gst_structure_get_int (s, "height", &size)) {
1056 GST_DEBUG_OBJECT (mux, "putting height %d in the metadata", size);
1057
1058 tmp = gst_flv_mux_create_number_script_value ("height", size);
1059 script_tag = gst_buffer_append (script_tag, tmp);
1060 tags_written++;
1061 }
1062
1063 if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &num, &den)) {
1064 gdouble d;
1065
1066 d = num;
1067 GST_DEBUG_OBJECT (mux, "putting AspectRatioX %f in the metadata", d);
1068
1069 tmp = gst_flv_mux_create_number_script_value ("AspectRatioX", d);
1070 script_tag = gst_buffer_append (script_tag, tmp);
1071 tags_written++;
1072
1073 d = den;
1074 GST_DEBUG_OBJECT (mux, "putting AspectRatioY %f in the metadata", d);
1075
1076 tmp = gst_flv_mux_create_number_script_value ("AspectRatioY", d);
1077 script_tag = gst_buffer_append (script_tag, tmp);
1078 tags_written++;
1079 }
1080
1081 if (gst_structure_get_fraction (s, "framerate", &num, &den)) {
1082 gdouble d;
1083
1084 gst_util_fraction_to_double (num, den, &d);
1085 GST_DEBUG_OBJECT (mux, "putting framerate %f in the metadata", d);
1086
1087 tmp = gst_flv_mux_create_number_script_value ("framerate", d);
1088 script_tag = gst_buffer_append (script_tag, tmp);
1089 tags_written++;
1090 }
1091
1092 GST_DEBUG_OBJECT (mux, "putting videodatarate %u KB/s in the metadata",
1093 mux->video_pad->bitrate / 1024);
1094 tmp = gst_flv_mux_create_number_script_value ("videodatarate",
1095 mux->video_pad->bitrate / 1024);
1096 script_tag = gst_buffer_append (script_tag, tmp);
1097 tags_written++;
1098 }
1099 }
1100
1101 if (mux->audio_pad && mux->audio_pad->codec != G_MAXUINT) {
1102 GST_DEBUG_OBJECT (mux, "putting audiocodecid %d in the metadata",
1103 mux->audio_pad->codec);
1104
1105 tmp = gst_flv_mux_create_number_script_value ("audiocodecid",
1106 mux->audio_pad->codec);
1107 script_tag = gst_buffer_append (script_tag, tmp);
1108 tags_written++;
1109
1110 GST_DEBUG_OBJECT (mux, "putting audiodatarate %u KB/s in the metadata",
1111 mux->audio_pad->bitrate / 1024);
1112 tmp = gst_flv_mux_create_number_script_value ("audiodatarate",
1113 mux->audio_pad->bitrate / 1024);
1114 script_tag = gst_buffer_append (script_tag, tmp);
1115 tags_written++;
1116 }
1117
1118 _gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (mux->metadatacreator),
1119 &tmp, &data);
1120 data[0] = 0; /* 15 bytes name */
1121 data[1] = 15;
1122 memcpy (&data[2], "metadatacreator", 15);
1123 data[17] = 2; /* string */
1124 data[18] = (strlen (mux->metadatacreator) >> 8) & 0xff;
1125 data[19] = (strlen (mux->metadatacreator)) & 0xff;
1126 memcpy (&data[20], mux->metadatacreator, strlen (mux->metadatacreator));
1127 script_tag = gst_buffer_append (script_tag, tmp);
1128 tags_written++;
1129
1130 _gst_buffer_new_and_alloc (2 + 7 + 1 + 2 + strlen (mux->encoder),
1131 &tmp, &data);
1132 data[0] = 0; /* 7 bytes name */
1133 data[1] = 7;
1134 memcpy (&data[2], "encoder", 7);
1135 data[9] = 2; /* string */
1136 data[10] = (strlen (mux->encoder) >> 8) & 0xff;
1137 data[11] = (strlen (mux->encoder)) & 0xff;
1138 memcpy (&data[12], mux->encoder, strlen (mux->encoder));
1139 script_tag = gst_buffer_append (script_tag, tmp);
1140 tags_written++;
1141
1142 {
1143 time_t secs;
1144 struct tm tm;
1145 gchar *s;
1146 static const gchar *weekdays[] = {
1147 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1148 };
1149 static const gchar *months[] = {
1150 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
1151 "Aug", "Sep", "Oct", "Nov", "Dec"
1152 };
1153
1154 secs = g_get_real_time () / G_USEC_PER_SEC;
1155 #ifdef HAVE_GMTIME_R
1156 gmtime_r (&secs, &tm);
1157 #else
1158 tm = *gmtime (&secs);
1159 #endif
1160
1161 s = g_strdup_printf ("%s %s %d %02d:%02d:%02d %d", weekdays[tm.tm_wday],
1162 months[tm.tm_mon], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
1163 tm.tm_year + 1900);
1164
1165 _gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s), &tmp, &data);
1166 data[0] = 0; /* 12 bytes name */
1167 data[1] = 12;
1168 memcpy (&data[2], "creationdate", 12);
1169 data[14] = 2; /* string */
1170 data[15] = (strlen (s) >> 8) & 0xff;
1171 data[16] = (strlen (s)) & 0xff;
1172 memcpy (&data[17], s, strlen (s));
1173 script_tag = gst_buffer_append (script_tag, tmp);
1174
1175 g_free (s);
1176 tags_written++;
1177 }
1178
1179 if (!tags_written) {
1180 gst_buffer_unref (script_tag);
1181 script_tag = NULL;
1182 goto exit;
1183 }
1184
1185 _gst_buffer_new_and_alloc (2 + 0 + 1, &tmp, &data);
1186 data[0] = 0; /* 0 byte size */
1187 data[1] = 0;
1188 data[2] = 9; /* end marker */
1189 script_tag = gst_buffer_append (script_tag, tmp);
1190
1191 _gst_buffer_new_and_alloc (4, &tmp, &data);
1192 GST_WRITE_UINT32_BE (data, gst_buffer_get_size (script_tag));
1193 script_tag = gst_buffer_append (script_tag, tmp);
1194
1195 gst_buffer_map (script_tag, &map, GST_MAP_WRITE);
1196 map.data[1] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 16) & 0xff;
1197 map.data[2] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 8) & 0xff;
1198 map.data[3] = ((gst_buffer_get_size (script_tag) - 11 - 4) >> 0) & 0xff;
1199
1200 GST_WRITE_UINT32_BE (map.data + 11 + 13 + 1, tags_written);
1201 gst_buffer_unmap (script_tag, &map);
1202
1203 exit:
1204 return script_tag;
1205 }
1206
1207 static GstBuffer *
gst_flv_mux_buffer_to_tag_internal(GstFlvMux * mux,GstBuffer * buffer,GstFlvMuxPad * pad,gboolean is_codec_data)1208 gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
1209 GstFlvMuxPad * pad, gboolean is_codec_data)
1210 {
1211 GstBuffer *tag;
1212 GstMapInfo map;
1213 guint size;
1214 guint64 pts, dts, cts;
1215 guint8 *data, *bdata = NULL;
1216 gsize bsize = 0;
1217
1218 if (GST_CLOCK_TIME_IS_VALID (pad->dts)) {
1219 pts = pad->pts / GST_MSECOND;
1220 dts = pad->dts / GST_MSECOND;
1221 GST_LOG_OBJECT (mux,
1222 "Pad %s: Created dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
1223 " from rounding %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT,
1224 GST_PAD_NAME (pad), GST_TIME_ARGS (dts * GST_MSECOND),
1225 GST_TIME_ARGS (pts * GST_MSECOND), GST_TIME_ARGS (pad->dts),
1226 GST_TIME_ARGS (pad->pts));
1227 } else if (GST_CLOCK_TIME_IS_VALID (pad->last_timestamp)) {
1228 pts = dts = pad->last_timestamp / GST_MSECOND;
1229 GST_DEBUG_OBJECT (mux,
1230 "Pad %s: Created dts and pts %" GST_TIME_FORMAT
1231 " from rounding last pad timestamp %" GST_TIME_FORMAT,
1232 GST_PAD_NAME (pad), GST_TIME_ARGS (pts * GST_MSECOND),
1233 GST_TIME_ARGS (pad->last_timestamp));
1234 } else {
1235 pts = dts = mux->last_dts;
1236 GST_DEBUG_OBJECT (mux,
1237 "Pad %s: Created dts and pts %" GST_TIME_FORMAT
1238 " from last mux timestamp",
1239 GST_PAD_NAME (pad), GST_TIME_ARGS (pts * GST_MSECOND));
1240 }
1241
1242 /* We prevent backwards timestamps because they confuse librtmp,
1243 * it expects timestamps to go forward not only inside one stream, but
1244 * also between the audio & video streams.
1245 */
1246 if (dts < mux->last_dts) {
1247 GST_WARNING_OBJECT (pad, "Got backwards dts! (%" GST_TIME_FORMAT
1248 " < %" GST_TIME_FORMAT ")", GST_TIME_ARGS (dts * GST_MSECOND),
1249 GST_TIME_ARGS (mux->last_dts * GST_MSECOND));
1250 dts = mux->last_dts;
1251 }
1252 mux->last_dts = dts;
1253
1254 /* Be safe in case TS are buggy */
1255 if (pts > dts)
1256 cts = pts - dts;
1257 else
1258 cts = 0;
1259
1260 /* Timestamp must start at zero */
1261 if (GST_CLOCK_TIME_IS_VALID (mux->first_timestamp)) {
1262 dts -= mux->first_timestamp / GST_MSECOND;
1263 pts = dts + cts;
1264 }
1265
1266 GST_LOG_OBJECT (mux,
1267 "got pts %" G_GUINT64_FORMAT " dts %" G_GUINT64_FORMAT " cts %"
1268 G_GUINT64_FORMAT, pts, dts, cts);
1269
1270 if (dts > G_MAXUINT32) {
1271 GST_LOG_OBJECT (mux,
1272 "Detected rollover, timestamp will be truncated (previous:%"
1273 G_GUINT64_FORMAT ", new:%u)", dts, (guint32) dts);
1274 }
1275
1276 if (buffer != NULL) {
1277 gst_buffer_map (buffer, &map, GST_MAP_READ);
1278 bdata = map.data;
1279 bsize = map.size;
1280 }
1281
1282 size = 11;
1283 if (mux->video_pad == pad) {
1284 size += 1;
1285 if (pad->codec == 7)
1286 size += 4 + bsize;
1287 else
1288 size += bsize;
1289 } else {
1290 size += 1;
1291 if (pad->codec == 10)
1292 size += 1 + bsize;
1293 else
1294 size += bsize;
1295 }
1296 size += 4;
1297
1298 _gst_buffer_new_and_alloc (size, &tag, &data);
1299 memset (data, 0, size);
1300
1301 data[0] = (mux->video_pad == pad) ? 9 : 8;
1302
1303 data[1] = ((size - 11 - 4) >> 16) & 0xff;
1304 data[2] = ((size - 11 - 4) >> 8) & 0xff;
1305 data[3] = ((size - 11 - 4) >> 0) & 0xff;
1306
1307 GST_WRITE_UINT24_BE (data + 4, dts);
1308 data[7] = (((guint) dts) >> 24) & 0xff;
1309
1310 data[8] = data[9] = data[10] = 0;
1311
1312 if (mux->video_pad == pad) {
1313 if (buffer && GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1314 data[11] |= 2 << 4;
1315 else
1316 data[11] |= 1 << 4;
1317
1318 data[11] |= pad->codec & 0x0f;
1319
1320 if (pad->codec == 7) {
1321 if (is_codec_data) {
1322 data[12] = 0;
1323 GST_WRITE_UINT24_BE (data + 13, 0);
1324 } else if (bsize == 0) {
1325 /* AVC end of sequence */
1326 data[12] = 2;
1327 GST_WRITE_UINT24_BE (data + 13, 0);
1328 } else {
1329 /* ACV NALU */
1330 data[12] = 1;
1331 GST_WRITE_UINT24_BE (data + 13, cts);
1332 }
1333 memcpy (data + 11 + 1 + 4, bdata, bsize);
1334 } else {
1335 memcpy (data + 11 + 1, bdata, bsize);
1336 }
1337 } else {
1338 data[11] |= (pad->codec << 4) & 0xf0;
1339 data[11] |= (pad->rate << 2) & 0x0c;
1340 data[11] |= (pad->width << 1) & 0x02;
1341 data[11] |= (pad->channels << 0) & 0x01;
1342
1343 GST_LOG_OBJECT (mux, "Creating byte %02x with "
1344 "codec:%d, rate:%d, width:%d, channels:%d",
1345 data[11], pad->codec, pad->rate, pad->width, pad->channels);
1346
1347 if (pad->codec == 10) {
1348 data[12] = is_codec_data ? 0 : 1;
1349
1350 memcpy (data + 11 + 1 + 1, bdata, bsize);
1351 } else {
1352 memcpy (data + 11 + 1, bdata, bsize);
1353 }
1354 }
1355
1356 if (buffer)
1357 gst_buffer_unmap (buffer, &map);
1358
1359 GST_WRITE_UINT32_BE (data + size - 4, size - 4);
1360
1361 GST_BUFFER_PTS (tag) = GST_CLOCK_TIME_NONE;
1362 GST_BUFFER_DTS (tag) = GST_CLOCK_TIME_NONE;
1363 GST_BUFFER_DURATION (tag) = GST_CLOCK_TIME_NONE;
1364
1365 if (buffer) {
1366 /* if we are streamable we copy over timestamps and offsets,
1367 if not just copy the offsets */
1368 if (mux->streamable) {
1369 GstClockTime timestamp = GST_CLOCK_TIME_NONE;
1370
1371 if (gst_segment_to_running_time_full (&GST_AGGREGATOR_PAD (pad)->segment,
1372 GST_FORMAT_TIME, GST_BUFFER_DTS_OR_PTS (buffer),
1373 ×tamp) == 1) {
1374 GST_BUFFER_PTS (tag) = timestamp;
1375 GST_BUFFER_DURATION (tag) = GST_BUFFER_DURATION (buffer);
1376 }
1377 GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_NONE;
1378 GST_BUFFER_OFFSET_END (tag) = GST_BUFFER_OFFSET_NONE;
1379 } else {
1380 GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET (buffer);
1381 GST_BUFFER_OFFSET_END (tag) = GST_BUFFER_OFFSET_END (buffer);
1382 }
1383
1384 /* mark the buffer if it's an audio buffer and there's also video being muxed
1385 * or it's a video interframe */
1386 if (mux->video_pad == pad &&
1387 GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1388 GST_BUFFER_FLAG_SET (tag, GST_BUFFER_FLAG_DELTA_UNIT);
1389 } else {
1390 GST_BUFFER_FLAG_SET (tag, GST_BUFFER_FLAG_DELTA_UNIT);
1391 GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) =
1392 GST_BUFFER_OFFSET_NONE;
1393 }
1394
1395 return tag;
1396 }
1397
1398 static inline GstBuffer *
gst_flv_mux_buffer_to_tag(GstFlvMux * mux,GstBuffer * buffer,GstFlvMuxPad * pad)1399 gst_flv_mux_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1400 GstFlvMuxPad * pad)
1401 {
1402 return gst_flv_mux_buffer_to_tag_internal (mux, buffer, pad, FALSE);
1403 }
1404
1405 static inline GstBuffer *
gst_flv_mux_codec_data_buffer_to_tag(GstFlvMux * mux,GstBuffer * buffer,GstFlvMuxPad * pad)1406 gst_flv_mux_codec_data_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1407 GstFlvMuxPad * pad)
1408 {
1409 return gst_flv_mux_buffer_to_tag_internal (mux, buffer, pad, TRUE);
1410 }
1411
1412 static inline GstBuffer *
gst_flv_mux_eos_to_tag(GstFlvMux * mux,GstFlvMuxPad * pad)1413 gst_flv_mux_eos_to_tag (GstFlvMux * mux, GstFlvMuxPad * pad)
1414 {
1415 return gst_flv_mux_buffer_to_tag_internal (mux, NULL, pad, FALSE);
1416 }
1417
1418 static void
gst_flv_mux_put_buffer_in_streamheader(GValue * streamheader,GstBuffer * buffer)1419 gst_flv_mux_put_buffer_in_streamheader (GValue * streamheader,
1420 GstBuffer * buffer)
1421 {
1422 GValue value = { 0 };
1423 GstBuffer *buf;
1424
1425 g_value_init (&value, GST_TYPE_BUFFER);
1426 buf = gst_buffer_copy (buffer);
1427 gst_value_set_buffer (&value, buf);
1428 gst_buffer_unref (buf);
1429 gst_value_array_append_value (streamheader, &value);
1430 g_value_unset (&value);
1431 }
1432
1433 static GstCaps *
gst_flv_mux_prepare_src_caps(GstFlvMux * mux,GstBuffer ** header_buf,GstBuffer ** metadata_buf,GstBuffer ** video_codec_data_buf,GstBuffer ** audio_codec_data_buf)1434 gst_flv_mux_prepare_src_caps (GstFlvMux * mux, GstBuffer ** header_buf,
1435 GstBuffer ** metadata_buf, GstBuffer ** video_codec_data_buf,
1436 GstBuffer ** audio_codec_data_buf)
1437 {
1438 GstBuffer *header, *metadata;
1439 GstBuffer *video_codec_data, *audio_codec_data;
1440 GstCaps *caps;
1441 GstStructure *structure;
1442 GValue streamheader = { 0 };
1443 GList *l;
1444
1445 header = gst_flv_mux_create_header (mux);
1446 metadata = gst_flv_mux_create_metadata (mux);
1447 video_codec_data = NULL;
1448 audio_codec_data = NULL;
1449
1450 GST_OBJECT_LOCK (mux);
1451 for (l = GST_ELEMENT_CAST (mux)->sinkpads; l != NULL; l = l->next) {
1452 GstFlvMuxPad *pad = l->data;
1453
1454 /* Get H.264 and AAC codec data, if present */
1455 if (pad && mux->video_pad == pad && pad->codec == 7) {
1456 if (pad->codec_data == NULL)
1457 GST_WARNING_OBJECT (mux, "Codec data for video stream not found, "
1458 "output might not be playable");
1459 else
1460 video_codec_data =
1461 gst_flv_mux_codec_data_buffer_to_tag (mux, pad->codec_data, pad);
1462 } else if (pad && mux->audio_pad == pad && pad->codec == 10) {
1463 if (pad->codec_data == NULL)
1464 GST_WARNING_OBJECT (mux, "Codec data for audio stream not found, "
1465 "output might not be playable");
1466 else
1467 audio_codec_data =
1468 gst_flv_mux_codec_data_buffer_to_tag (mux, pad->codec_data, pad);
1469 }
1470 }
1471 GST_OBJECT_UNLOCK (mux);
1472
1473 /* mark buffers that will go in the streamheader */
1474 GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_HEADER);
1475 GST_BUFFER_FLAG_SET (metadata, GST_BUFFER_FLAG_HEADER);
1476 if (video_codec_data != NULL) {
1477 GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_HEADER);
1478 /* mark as a delta unit, so downstream will not try to synchronize on that
1479 * buffer - to actually start playback you need a real video keyframe */
1480 GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_DELTA_UNIT);
1481 }
1482 if (audio_codec_data != NULL) {
1483 GST_BUFFER_FLAG_SET (audio_codec_data, GST_BUFFER_FLAG_HEADER);
1484 }
1485
1486 /* put buffers in streamheader */
1487 g_value_init (&streamheader, GST_TYPE_ARRAY);
1488 gst_flv_mux_put_buffer_in_streamheader (&streamheader, header);
1489 gst_flv_mux_put_buffer_in_streamheader (&streamheader, metadata);
1490 if (video_codec_data != NULL)
1491 gst_flv_mux_put_buffer_in_streamheader (&streamheader, video_codec_data);
1492 if (audio_codec_data != NULL)
1493 gst_flv_mux_put_buffer_in_streamheader (&streamheader, audio_codec_data);
1494
1495 /* create the caps and put the streamheader in them */
1496 caps = gst_caps_new_empty_simple ("video/x-flv");
1497 structure = gst_caps_get_structure (caps, 0);
1498 gst_structure_set_value (structure, "streamheader", &streamheader);
1499 g_value_unset (&streamheader);
1500
1501 if (header_buf) {
1502 *header_buf = header;
1503 } else {
1504 gst_buffer_unref (header);
1505 }
1506
1507 if (metadata_buf) {
1508 *metadata_buf = metadata;
1509 } else {
1510 gst_buffer_unref (metadata);
1511 }
1512
1513 if (video_codec_data_buf) {
1514 *video_codec_data_buf = video_codec_data;
1515 } else if (video_codec_data) {
1516 gst_buffer_unref (video_codec_data);
1517 }
1518
1519 if (audio_codec_data_buf) {
1520 *audio_codec_data_buf = audio_codec_data;
1521 } else if (audio_codec_data) {
1522 gst_buffer_unref (audio_codec_data);
1523 }
1524
1525 return caps;
1526 }
1527
1528 static GstFlowReturn
gst_flv_mux_write_header(GstFlvMux * mux)1529 gst_flv_mux_write_header (GstFlvMux * mux)
1530 {
1531 GstBuffer *header, *metadata;
1532 GstBuffer *video_codec_data, *audio_codec_data;
1533 GstCaps *caps;
1534 GstFlowReturn ret;
1535
1536 header = metadata = video_codec_data = audio_codec_data = NULL;
1537
1538 /* if not streaming, check if downstream is seekable */
1539 if (!mux->streamable) {
1540 gboolean seekable;
1541 GstQuery *query;
1542
1543 query = gst_query_new_seeking (GST_FORMAT_BYTES);
1544 if (gst_pad_peer_query (mux->srcpad, query)) {
1545 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
1546 GST_INFO_OBJECT (mux, "downstream is %sseekable", seekable ? "" : "not ");
1547 } else {
1548 /* have to assume seeking is supported if query not handled downstream */
1549 GST_WARNING_OBJECT (mux, "downstream did not handle seeking query");
1550 seekable = FALSE;
1551 }
1552 if (!seekable) {
1553 mux->streamable = TRUE;
1554 g_object_notify (G_OBJECT (mux), "streamable");
1555 GST_WARNING_OBJECT (mux, "downstream is not seekable, but "
1556 "streamable=false. Will ignore that and create streamable output "
1557 "instead");
1558 }
1559 gst_query_unref (query);
1560 }
1561
1562 if (mux->streamable && mux->sent_header) {
1563 GstBuffer **video_codec_data_p = NULL, **audio_codec_data_p = NULL;
1564
1565 if (mux->video_pad && mux->video_pad->info_changed)
1566 video_codec_data_p = &video_codec_data;
1567 if (mux->audio_pad && mux->audio_pad->info_changed)
1568 audio_codec_data_p = &audio_codec_data;
1569
1570 caps = gst_flv_mux_prepare_src_caps (mux,
1571 NULL, NULL, video_codec_data_p, audio_codec_data_p);
1572 } else {
1573 caps = gst_flv_mux_prepare_src_caps (mux,
1574 &header, &metadata, &video_codec_data, &audio_codec_data);
1575 }
1576
1577 gst_aggregator_set_src_caps (GST_AGGREGATOR_CAST (mux), caps);
1578
1579 gst_caps_unref (caps);
1580
1581 /* push the header buffer, the metadata and the codec info, if any */
1582 if (header != NULL) {
1583 ret = gst_flv_mux_push (mux, header);
1584 if (ret != GST_FLOW_OK)
1585 goto failure_header;
1586 mux->sent_header = TRUE;
1587 }
1588 if (metadata != NULL) {
1589 ret = gst_flv_mux_push (mux, metadata);
1590 if (ret != GST_FLOW_OK)
1591 goto failure_metadata;
1592 mux->new_tags = FALSE;
1593 }
1594 if (video_codec_data != NULL) {
1595 ret = gst_flv_mux_push (mux, video_codec_data);
1596 if (ret != GST_FLOW_OK)
1597 goto failure_video_codec_data;
1598 mux->video_pad->info_changed = FALSE;
1599 }
1600 if (audio_codec_data != NULL) {
1601 ret = gst_flv_mux_push (mux, audio_codec_data);
1602 if (ret != GST_FLOW_OK)
1603 goto failure_audio_codec_data;
1604 mux->audio_pad->info_changed = FALSE;
1605 }
1606 return GST_FLOW_OK;
1607
1608 failure_header:
1609 gst_buffer_unref (metadata);
1610
1611 failure_metadata:
1612 if (video_codec_data != NULL)
1613 gst_buffer_unref (video_codec_data);
1614
1615 failure_video_codec_data:
1616 if (audio_codec_data != NULL)
1617 gst_buffer_unref (audio_codec_data);
1618
1619 failure_audio_codec_data:
1620 return ret;
1621 }
1622
1623 static GstClockTime
gst_flv_mux_segment_to_running_time(const GstSegment * segment,GstClockTime t)1624 gst_flv_mux_segment_to_running_time (const GstSegment * segment, GstClockTime t)
1625 {
1626 /* we can get a dts before the segment, if dts < pts and pts is inside
1627 * the segment, so we consider early times as 0 */
1628 if (t < segment->start)
1629 return 0;
1630 return gst_segment_to_running_time (segment, GST_FORMAT_TIME, t);
1631 }
1632
1633 static void
gst_flv_mux_update_index(GstFlvMux * mux,GstBuffer * buffer,GstFlvMuxPad * pad)1634 gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer,
1635 GstFlvMuxPad * pad)
1636 {
1637 /*
1638 * Add the tag byte offset and to the index if it's a valid seek point, which
1639 * means it's either a video keyframe or if there is no video pad (in that
1640 * case every FLV tag is a valid seek point)
1641 */
1642 if (mux->video_pad == pad &&
1643 GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1644 return;
1645
1646 if (GST_BUFFER_PTS_IS_VALID (buffer)) {
1647 GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
1648 GstClockTime pts =
1649 gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD
1650 (pad)->segment, GST_BUFFER_PTS (buffer));
1651 entry->position = mux->byte_count;
1652 entry->time = gst_guint64_to_gdouble (pts) / GST_SECOND;
1653 mux->index = g_list_prepend (mux->index, entry);
1654 }
1655 }
1656
1657 static GstFlowReturn
gst_flv_mux_write_buffer(GstFlvMux * mux,GstFlvMuxPad * pad,GstBuffer * buffer)1658 gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvMuxPad * pad,
1659 GstBuffer * buffer)
1660 {
1661 GstBuffer *tag;
1662 GstFlowReturn ret;
1663 GstClockTime dts =
1664 gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD (pad)->segment,
1665 GST_BUFFER_DTS (buffer));
1666
1667 /* clipping function arranged for running_time */
1668
1669 if (!mux->streamable)
1670 gst_flv_mux_update_index (mux, buffer, pad);
1671
1672 tag = gst_flv_mux_buffer_to_tag (mux, buffer, pad);
1673
1674 gst_buffer_unref (buffer);
1675
1676 ret = gst_flv_mux_push (mux, tag);
1677
1678 if (ret == GST_FLOW_OK && GST_CLOCK_TIME_IS_VALID (dts))
1679 pad->last_timestamp = dts;
1680
1681 return ret;
1682 }
1683
1684 static guint64
gst_flv_mux_determine_duration(GstFlvMux * mux)1685 gst_flv_mux_determine_duration (GstFlvMux * mux)
1686 {
1687 GList *l;
1688 GstClockTime duration = GST_CLOCK_TIME_NONE;
1689
1690 GST_DEBUG_OBJECT (mux, "trying to determine the duration "
1691 "from pad timestamps");
1692
1693 GST_OBJECT_LOCK (mux);
1694 for (l = GST_ELEMENT_CAST (mux)->sinkpads; l != NULL; l = l->next) {
1695 GstFlvMuxPad *pad = GST_FLV_MUX_PAD (l->data);
1696
1697 if (pad && (pad->last_timestamp != GST_CLOCK_TIME_NONE)) {
1698 if (duration == GST_CLOCK_TIME_NONE)
1699 duration = pad->last_timestamp;
1700 else
1701 duration = MAX (duration, pad->last_timestamp);
1702 }
1703 }
1704 GST_OBJECT_UNLOCK (mux);
1705
1706 return duration;
1707 }
1708
1709 struct DurationData
1710 {
1711 GstClockTime duration;
1712 };
1713
1714 static gboolean
duration_query_cb(GstElement * element,GstPad * pad,struct DurationData * data)1715 duration_query_cb (GstElement * element, GstPad * pad,
1716 struct DurationData *data)
1717 {
1718 GstClockTime dur;
1719
1720 if (gst_pad_peer_query_duration (GST_PAD (pad), GST_FORMAT_TIME,
1721 (gint64 *) & dur) && dur != GST_CLOCK_TIME_NONE) {
1722 if (data->duration == GST_CLOCK_TIME_NONE)
1723 data->duration = dur;
1724 else
1725 data->duration = MAX (dur, data->duration);
1726 }
1727
1728 return TRUE;
1729 }
1730
1731 static GstClockTime
gst_flv_mux_query_upstream_duration(GstFlvMux * mux)1732 gst_flv_mux_query_upstream_duration (GstFlvMux * mux)
1733 {
1734 struct DurationData cb_data = { GST_CLOCK_TIME_NONE };
1735
1736 gst_element_foreach_sink_pad (GST_ELEMENT (mux),
1737 (GstElementForeachPadFunc) (duration_query_cb), &cb_data);
1738
1739 return cb_data.duration;
1740 }
1741
1742 static gboolean
gst_flv_mux_are_all_pads_eos(GstFlvMux * mux)1743 gst_flv_mux_are_all_pads_eos (GstFlvMux * mux)
1744 {
1745 GList *l;
1746
1747 GST_OBJECT_LOCK (mux);
1748 for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
1749 GstFlvMuxPad *pad = GST_FLV_MUX_PAD (l->data);
1750
1751 if (!gst_aggregator_pad_is_eos (GST_AGGREGATOR_PAD (pad))) {
1752 GST_OBJECT_UNLOCK (mux);
1753 return FALSE;
1754 }
1755 }
1756 GST_OBJECT_UNLOCK (mux);
1757 return TRUE;
1758 }
1759
1760 static GstFlowReturn
gst_flv_mux_write_eos(GstFlvMux * mux)1761 gst_flv_mux_write_eos (GstFlvMux * mux)
1762 {
1763 GstBuffer *tag;
1764
1765 if (mux->video_pad == NULL)
1766 return GST_FLOW_OK;
1767
1768 tag = gst_flv_mux_eos_to_tag (mux, mux->video_pad);
1769
1770 return gst_flv_mux_push (mux, tag);
1771 }
1772
1773 static GstFlowReturn
gst_flv_mux_rewrite_header(GstFlvMux * mux)1774 gst_flv_mux_rewrite_header (GstFlvMux * mux)
1775 {
1776 GstBuffer *rewrite, *index, *tmp;
1777 GstEvent *event;
1778 guint8 *data;
1779 gdouble d;
1780 GList *l;
1781 guint32 index_len, allocate_size;
1782 guint32 i, index_skip;
1783 GstSegment segment;
1784 GstClockTime dur;
1785
1786 if (mux->streamable)
1787 return GST_FLOW_OK;
1788
1789 /* seek back to the preallocated index space */
1790 gst_segment_init (&segment, GST_FORMAT_BYTES);
1791 segment.start = segment.time = 13 + 29;
1792 event = gst_event_new_segment (&segment);
1793 if (!gst_pad_push_event (mux->srcpad, event)) {
1794 GST_WARNING_OBJECT (mux, "Seek to rewrite header failed");
1795 return GST_FLOW_OK;
1796 }
1797
1798 /* determine duration now based on our own timestamping,
1799 * so that it is likely many times better and consistent
1800 * than whatever obtained by some query */
1801 dur = gst_flv_mux_determine_duration (mux);
1802 if (dur != GST_CLOCK_TIME_NONE)
1803 mux->duration = dur;
1804
1805 /* rewrite the duration tag */
1806 d = gst_guint64_to_gdouble (mux->duration);
1807 d /= (gdouble) GST_SECOND;
1808
1809 GST_DEBUG_OBJECT (mux, "determined the final duration to be %f", d);
1810
1811 rewrite = gst_flv_mux_create_number_script_value ("duration", d);
1812
1813 /* rewrite the filesize tag */
1814 d = gst_guint64_to_gdouble (mux->byte_count);
1815
1816 GST_DEBUG_OBJECT (mux, "putting total filesize %f in the metadata", d);
1817
1818 tmp = gst_flv_mux_create_number_script_value ("filesize", d);
1819 rewrite = gst_buffer_append (rewrite, tmp);
1820
1821 if (!mux->index) {
1822 /* no index, so push buffer and return */
1823 return gst_flv_mux_push (mux, rewrite);
1824 }
1825
1826 /* rewrite the index */
1827 mux->index = g_list_reverse (mux->index);
1828 index_len = g_list_length (mux->index);
1829
1830 /* We write at most MAX_INDEX_ENTRIES elements */
1831 if (index_len > MAX_INDEX_ENTRIES) {
1832 index_skip = 1 + index_len / MAX_INDEX_ENTRIES;
1833 index_len = (index_len + index_skip - 1) / index_skip;
1834 } else {
1835 index_skip = 1;
1836 }
1837
1838 GST_DEBUG_OBJECT (mux, "Index length is %d", index_len);
1839 /* see size calculation in gst_flv_mux_preallocate_index */
1840 allocate_size = 11 + 8 + 22 + 10 + index_len * 18;
1841 GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size);
1842 _gst_buffer_new_and_alloc (allocate_size, &index, &data);
1843
1844 GST_WRITE_UINT16_BE (data, 9); /* the 'keyframes' key */
1845 memcpy (data + 2, "keyframes", 9);
1846 GST_WRITE_UINT8 (data + 11, 8); /* nested ECMA array */
1847 GST_WRITE_UINT32_BE (data + 12, 2); /* two elements */
1848 GST_WRITE_UINT16_BE (data + 16, 5); /* first string key: 'times' */
1849 memcpy (data + 18, "times", 5);
1850 GST_WRITE_UINT8 (data + 23, 10); /* strict array */
1851 GST_WRITE_UINT32_BE (data + 24, index_len);
1852 data += 28;
1853
1854 /* the keyframes' times */
1855 for (i = 0, l = mux->index; l; l = l->next, i++) {
1856 GstFlvMuxIndexEntry *entry = l->data;
1857
1858 if (i % index_skip != 0)
1859 continue;
1860 GST_WRITE_UINT8 (data, 0); /* numeric (aka double) */
1861 GST_WRITE_DOUBLE_BE (data + 1, entry->time);
1862 data += 9;
1863 }
1864
1865 GST_WRITE_UINT16_BE (data, 13); /* second string key: 'filepositions' */
1866 memcpy (data + 2, "filepositions", 13);
1867 GST_WRITE_UINT8 (data + 15, 10); /* strict array */
1868 GST_WRITE_UINT32_BE (data + 16, index_len);
1869 data += 20;
1870
1871 /* the keyframes' file positions */
1872 for (i = 0, l = mux->index; l; l = l->next, i++) {
1873 GstFlvMuxIndexEntry *entry = l->data;
1874
1875 if (i % index_skip != 0)
1876 continue;
1877 GST_WRITE_UINT8 (data, 0);
1878 GST_WRITE_DOUBLE_BE (data + 1, entry->position);
1879 data += 9;
1880 }
1881
1882 GST_WRITE_UINT24_BE (data, 9); /* finish the ECMA array */
1883
1884 /* If there is space left in the prefilled area, reinsert the filler.
1885 There is at least 18 bytes free, so it will always fit. */
1886 if (index_len < MAX_INDEX_ENTRIES) {
1887 GstBuffer *tmp;
1888 guint8 *data;
1889 guint32 remaining_filler_size;
1890
1891 _gst_buffer_new_and_alloc (14, &tmp, &data);
1892 GST_WRITE_UINT16_BE (data, 9);
1893 memcpy (data + 2, "gstfiller", 9);
1894 GST_WRITE_UINT8 (data + 11, 2); /* string */
1895
1896 /* There is 18 bytes per remaining index entry minus what is used for
1897 * the'gstfiller' key. The rest is already filled with spaces, so just need
1898 * to update length. */
1899 remaining_filler_size = (MAX_INDEX_ENTRIES - index_len) * 18 - 14;
1900 GST_DEBUG_OBJECT (mux, "Remaining filler size is %d bytes",
1901 remaining_filler_size);
1902 GST_WRITE_UINT16_BE (data + 12, remaining_filler_size);
1903 index = gst_buffer_append (index, tmp);
1904 }
1905
1906 rewrite = gst_buffer_append (rewrite, index);
1907
1908 return gst_flv_mux_push (mux, rewrite);
1909 }
1910
1911 /* Returns NULL, or a reference to the pad with the
1912 * buffer with lowest running time */
1913 static GstFlvMuxPad *
gst_flv_mux_find_best_pad(GstAggregator * aggregator,GstClockTime * ts,gboolean timeout)1914 gst_flv_mux_find_best_pad (GstAggregator * aggregator, GstClockTime * ts,
1915 gboolean timeout)
1916 {
1917 GstFlvMuxPad *best = NULL;
1918 GstClockTime best_ts = GST_CLOCK_TIME_NONE;
1919 GstIterator *pads;
1920 GValue padptr = { 0, };
1921 gboolean done = FALSE;
1922
1923 pads = gst_element_iterate_sink_pads (GST_ELEMENT (aggregator));
1924
1925 while (!done) {
1926 switch (gst_iterator_next (pads, &padptr)) {
1927 case GST_ITERATOR_OK:{
1928 GstAggregatorPad *apad = g_value_get_object (&padptr);
1929 GstClockTime t = GST_CLOCK_TIME_NONE;
1930 GstBuffer *buffer;
1931
1932 buffer = gst_aggregator_pad_peek_buffer (apad);
1933 if (!buffer) {
1934 if (!timeout && !GST_PAD_IS_EOS (apad)) {
1935 gst_object_replace ((GstObject **) & best, NULL);
1936 best_ts = GST_CLOCK_TIME_NONE;
1937 done = TRUE;
1938 }
1939 break;
1940 }
1941
1942 if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS_OR_PTS (buffer))) {
1943 t = gst_flv_mux_segment_to_running_time (&apad->segment,
1944 GST_BUFFER_DTS_OR_PTS (buffer));
1945 }
1946
1947 if (!GST_CLOCK_TIME_IS_VALID (best_ts) ||
1948 (GST_CLOCK_TIME_IS_VALID (t) && t < best_ts)) {
1949 gst_object_replace ((GstObject **) & best, GST_OBJECT (apad));
1950 best_ts = t;
1951 }
1952 gst_buffer_unref (buffer);
1953 break;
1954 }
1955 case GST_ITERATOR_DONE:
1956 done = TRUE;
1957 break;
1958 case GST_ITERATOR_RESYNC:
1959 gst_iterator_resync (pads);
1960 /* Clear the best pad and start again. It might have disappeared */
1961 gst_object_replace ((GstObject **) & best, NULL);
1962 best_ts = GST_CLOCK_TIME_NONE;
1963 break;
1964 case GST_ITERATOR_ERROR:
1965 /* This can't happen if the parameters to gst_iterator_next() are valid */
1966 g_assert_not_reached ();
1967 break;
1968 }
1969 g_value_reset (&padptr);
1970 }
1971 g_value_unset (&padptr);
1972 gst_iterator_free (pads);
1973
1974 if (best) {
1975 GST_DEBUG_OBJECT (aggregator,
1976 "Best pad found with TS %" GST_TIME_FORMAT ": %" GST_PTR_FORMAT,
1977 GST_TIME_ARGS (best_ts), best);
1978 } else {
1979 GST_DEBUG_OBJECT (aggregator, "Best pad not found");
1980 }
1981
1982 if (ts)
1983 *ts = best_ts;
1984 return best;
1985 }
1986
1987 static GstFlowReturn
gst_flv_mux_aggregate(GstAggregator * aggregator,gboolean timeout)1988 gst_flv_mux_aggregate (GstAggregator * aggregator, gboolean timeout)
1989 {
1990 GstFlvMux *mux = GST_FLV_MUX (aggregator);
1991 GstFlvMuxPad *best;
1992 GstClockTime best_time = GST_CLOCK_TIME_NONE;
1993 GstFlowReturn ret;
1994 GstClockTime ts;
1995 GstBuffer *buffer = NULL;
1996
1997 if (mux->state == GST_FLV_MUX_STATE_HEADER) {
1998 if (GST_ELEMENT_CAST (mux)->sinkpads == NULL) {
1999 GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
2000 ("No input streams configured"));
2001 return GST_FLOW_ERROR;
2002 }
2003
2004 best = gst_flv_mux_find_best_pad (aggregator, &ts, timeout);
2005 if (!best) {
2006 if (!gst_flv_mux_are_all_pads_eos (mux))
2007 return GST_AGGREGATOR_FLOW_NEED_DATA;
2008 else
2009 return GST_FLOW_OK;
2010 }
2011
2012 ret = gst_flv_mux_write_header (mux);
2013 if (ret != GST_FLOW_OK) {
2014 gst_object_unref (best);
2015 return ret;
2016 }
2017
2018 mux->state = GST_FLV_MUX_STATE_DATA;
2019
2020 if (!mux->streamable || mux->first_timestamp == GST_CLOCK_TIME_NONE) {
2021 if (best && GST_CLOCK_TIME_IS_VALID (ts))
2022 mux->first_timestamp = ts;
2023 else
2024 mux->first_timestamp = 0;
2025 }
2026 } else {
2027 best = gst_flv_mux_find_best_pad (aggregator, &ts, timeout);
2028 }
2029
2030 if (best) {
2031 buffer = gst_aggregator_pad_pop_buffer (GST_AGGREGATOR_PAD (best));
2032 if (!buffer) {
2033 /* We might have gotten a flush event after we picked the pad */
2034 gst_object_unref (best);
2035 return GST_AGGREGATOR_FLOW_NEED_DATA;
2036 }
2037 }
2038
2039 if (mux->new_tags && mux->streamable) {
2040 GstBuffer *buf = gst_flv_mux_create_metadata (mux);
2041 if (buf)
2042 gst_flv_mux_push (mux, buf);
2043 mux->new_tags = FALSE;
2044 }
2045
2046 if (best) {
2047 best->dts =
2048 gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD
2049 (best)->segment, GST_BUFFER_DTS_OR_PTS (buffer));
2050
2051 if (GST_CLOCK_TIME_IS_VALID (best->dts))
2052 best_time = best->dts - mux->first_timestamp;
2053
2054 if (GST_BUFFER_PTS_IS_VALID (buffer))
2055 best->pts =
2056 gst_flv_mux_segment_to_running_time (&GST_AGGREGATOR_PAD
2057 (best)->segment, GST_BUFFER_PTS (buffer));
2058 else
2059 best->pts = best->dts;
2060
2061 GST_LOG_OBJECT (best,
2062 "got buffer PTS %" GST_TIME_FORMAT " DTS %" GST_TIME_FORMAT,
2063 GST_TIME_ARGS (best->pts), GST_TIME_ARGS (best->dts));
2064 } else {
2065 if (!gst_flv_mux_are_all_pads_eos (mux))
2066 return GST_AGGREGATOR_FLOW_NEED_DATA;
2067 best_time = GST_CLOCK_STIME_NONE;
2068 }
2069
2070 /* The FLV timestamp is an int32 field. For non-live streams error out if a
2071 bigger timestamp is seen, for live the timestamp will get wrapped in
2072 gst_flv_mux_buffer_to_tag */
2073 if (!mux->streamable && (GST_CLOCK_TIME_IS_VALID (best_time))
2074 && best_time / GST_MSECOND > G_MAXINT32) {
2075 GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS");
2076 if (buffer) {
2077 gst_buffer_unref (buffer);
2078 buffer = NULL;
2079 }
2080 gst_object_unref (best);
2081 best = NULL;
2082 }
2083
2084 if (best) {
2085 GstFlowReturn ret = gst_flv_mux_write_buffer (mux, best, buffer);
2086 gst_object_unref (best);
2087 return ret;
2088 } else {
2089 if (gst_flv_mux_are_all_pads_eos (mux)) {
2090 gst_flv_mux_write_eos (mux);
2091 gst_flv_mux_rewrite_header (mux);
2092 return GST_FLOW_EOS;
2093 }
2094 return GST_FLOW_OK;
2095 }
2096 }
2097
2098 static void
gst_flv_mux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2099 gst_flv_mux_get_property (GObject * object,
2100 guint prop_id, GValue * value, GParamSpec * pspec)
2101 {
2102 GstFlvMux *mux = GST_FLV_MUX (object);
2103
2104 switch (prop_id) {
2105 case PROP_STREAMABLE:
2106 g_value_set_boolean (value, mux->streamable);
2107 break;
2108 case PROP_METADATACREATOR:
2109 g_value_set_string (value, mux->metadatacreator);
2110 break;
2111 case PROP_ENCODER:
2112 g_value_set_string (value, mux->encoder);
2113 break;
2114 case PROP_SKIP_BACKWARDS_STREAMS:
2115 g_value_set_boolean (value, mux->skip_backwards_streams);
2116 break;
2117 default:
2118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2119 break;
2120 }
2121 }
2122
2123 static void
gst_flv_mux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2124 gst_flv_mux_set_property (GObject * object,
2125 guint prop_id, const GValue * value, GParamSpec * pspec)
2126 {
2127 GstFlvMux *mux = GST_FLV_MUX (object);
2128
2129 switch (prop_id) {
2130 case PROP_STREAMABLE:
2131 mux->streamable = g_value_get_boolean (value);
2132 if (mux->streamable)
2133 gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
2134 GST_TAG_MERGE_REPLACE);
2135 else
2136 gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
2137 GST_TAG_MERGE_KEEP);
2138 break;
2139 case PROP_METADATACREATOR:
2140 g_free (mux->metadatacreator);
2141 if (!g_value_get_string (value)) {
2142 GST_WARNING_OBJECT (mux, "metadatacreator property can not be NULL");
2143 mux->metadatacreator = g_strdup (DEFAULT_METADATACREATOR);
2144 } else {
2145 mux->metadatacreator = g_value_dup_string (value);
2146 }
2147 break;
2148 case PROP_ENCODER:
2149 g_free (mux->encoder);
2150 if (!g_value_get_string (value)) {
2151 GST_WARNING_OBJECT (mux, "encoder property can not be NULL");
2152 mux->encoder = g_strdup (DEFAULT_METADATACREATOR);
2153 } else {
2154 mux->encoder = g_value_dup_string (value);
2155 }
2156 break;
2157 case PROP_SKIP_BACKWARDS_STREAMS:
2158 mux->skip_backwards_streams = g_value_get_boolean (value);
2159 break;
2160 default:
2161 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2162 break;
2163 }
2164 }
2165
2166 static GstClockTime
gst_flv_mux_get_next_time(GstAggregator * aggregator)2167 gst_flv_mux_get_next_time (GstAggregator * aggregator)
2168 {
2169 GstFlvMux *mux = GST_FLV_MUX (aggregator);
2170 GstAggregatorPad *agg_audio_pad = GST_AGGREGATOR_PAD_CAST (mux->audio_pad);
2171 GstAggregatorPad *agg_video_pad = GST_AGGREGATOR_PAD_CAST (mux->video_pad);
2172
2173 GST_OBJECT_LOCK (aggregator);
2174 if (mux->state == GST_FLV_MUX_STATE_HEADER &&
2175 ((mux->audio_pad && mux->audio_pad->codec == G_MAXUINT) ||
2176 (mux->video_pad && mux->video_pad->codec == G_MAXUINT)))
2177 goto wait_for_data;
2178
2179 if (!((agg_audio_pad && gst_aggregator_pad_has_buffer (agg_audio_pad)) ||
2180 (agg_video_pad && gst_aggregator_pad_has_buffer (agg_video_pad))))
2181 goto wait_for_data;
2182 GST_OBJECT_UNLOCK (aggregator);
2183
2184 return gst_aggregator_simple_get_next_time (aggregator);
2185
2186 wait_for_data:
2187 GST_OBJECT_UNLOCK (aggregator);
2188 return GST_CLOCK_TIME_NONE;
2189 }
2190