flvmux: add some comments and defines to clarify code.
[platform/upstream/gst-plugins-good.git] / gst / flv / gstflvmux.c
1 /* GStreamer
2  *
3  * Copyright (c) 2008,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-flvmux
23  *
24  * flvmux muxes different streams into an FLV file.
25  *
26  * <refsect2>
27  * <title>Example launch line</title>
28  * |[
29  * gst-launch -v filesrc location=/path/to/audio ! decodebin2 ! queue ! flvmux name=m ! filesink location=file.flv   filesrc location=/path/to/video ! decodebin2 ! queue ! m.
30  * ]| This pipeline muxes an audio and video file into a single FLV file.
31  * </refsect2>
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include <math.h>
39 #include <string.h>
40
41 #include "gstflvmux.h"
42 #include "amfdefs.h"
43
44 GST_DEBUG_CATEGORY_STATIC (flvmux_debug);
45 #define GST_CAT_DEFAULT flvmux_debug
46
47 enum
48 {
49   PROP_0,
50   PROP_STREAMABLE
51 };
52
53 #define DEFAULT_STREAMABLE FALSE
54 #define MAX_INDEX_ENTRIES 128
55
56 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("video/x-flv")
60     );
61
62 static GstStaticPadTemplate videosink_templ = GST_STATIC_PAD_TEMPLATE ("video",
63     GST_PAD_SINK,
64     GST_PAD_REQUEST,
65     GST_STATIC_CAPS ("video/x-flash-video; "
66         "video/x-flash-screen; "
67         "video/x-vp6-flash; " "video/x-vp6-alpha; "
68         "video/x-h264, stream-format=avc;")
69     );
70
71 static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio",
72     GST_PAD_SINK,
73     GST_PAD_REQUEST,
74     GST_STATIC_CAPS
75     ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
76         "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; "
77         "audio/mpeg, mpegversion = (int) { 2, 4 }, framed = (boolean) TRUE; "
78         "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
79         "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 8, depth = (int) 8, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) FALSE; "
80         "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 16, depth = (int) 16, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) TRUE; "
81         "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
82         "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
83         "audio/x-speex, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 };")
84     );
85
86 #define _do_init(type)                                                          \
87   G_STMT_START{                                                                 \
88     static const GInterfaceInfo tag_setter_info = {                             \
89       NULL,                                                                     \
90       NULL,                                                                     \
91       NULL                                                                      \
92     };                                                                          \
93     g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,                     \
94                                  &tag_setter_info);                             \
95   }G_STMT_END
96
97 GST_BOILERPLATE_FULL (GstFlvMux, gst_flv_mux, GstElement, GST_TYPE_ELEMENT,
98     _do_init);
99
100 static void gst_flv_mux_finalize (GObject * object);
101 static GstFlowReturn
102 gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data);
103
104 static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event);
105 static GstPad *gst_flv_mux_request_new_pad (GstElement * element,
106     GstPadTemplate * templ, const gchar * name);
107 static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad);
108
109 static void gst_flv_mux_get_property (GObject * object,
110     guint prop_id, GValue * value, GParamSpec * pspec);
111 static void gst_flv_mux_set_property (GObject * object,
112     guint prop_id, const GValue * value, GParamSpec * pspec);
113
114 static GstStateChangeReturn
115 gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
116
117 static void gst_flv_mux_reset (GstElement * element);
118 static void gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * pad,
119     gboolean video);
120
121 typedef struct
122 {
123   gdouble position;
124   gdouble time;
125 } GstFlvMuxIndexEntry;
126
127 static void
128 gst_flv_mux_index_entry_free (GstFlvMuxIndexEntry * entry)
129 {
130   g_slice_free (GstFlvMuxIndexEntry, entry);
131 }
132
133 static void
134 gst_flv_mux_base_init (gpointer g_class)
135 {
136   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
137
138   gst_element_class_add_pad_template (element_class,
139       gst_static_pad_template_get (&videosink_templ));
140   gst_element_class_add_pad_template (element_class,
141       gst_static_pad_template_get (&audiosink_templ));
142   gst_element_class_add_pad_template (element_class,
143       gst_static_pad_template_get (&src_templ));
144   gst_element_class_set_details_simple (element_class, "FLV muxer",
145       "Codec/Muxer",
146       "Muxes video/audio streams into a FLV stream",
147       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
148
149   GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
150 }
151
152 static void
153 gst_flv_mux_class_init (GstFlvMuxClass * klass)
154 {
155   GObjectClass *gobject_class;
156   GstElementClass *gstelement_class;
157
158   GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
159
160   gobject_class = (GObjectClass *) klass;
161   gstelement_class = (GstElementClass *) klass;
162
163   gobject_class->get_property = gst_flv_mux_get_property;
164   gobject_class->set_property = gst_flv_mux_set_property;
165   gobject_class->finalize = gst_flv_mux_finalize;
166
167   /* FIXME: ideally the right mode of operation should be detected
168    * automatically using queries when parameter not specified. */
169   /**
170    * GstFlvMux:streamable
171    *
172    * If True, the output will be streaming friendly. (ie without indexes and
173    * duration)
174    *
175    * Since: 0.10.24
176    **/
177   g_object_class_install_property (gobject_class, PROP_STREAMABLE,
178       g_param_spec_boolean ("streamable", "streamable",
179           "If set to true, the output should be as if it is to be streamed "
180           "and hence no indexes written or duration written.",
181           DEFAULT_STREAMABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182
183   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_flv_mux_change_state);
184   gstelement_class->request_new_pad =
185       GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad);
186   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
187 }
188
189 static void
190 gst_flv_mux_init (GstFlvMux * mux, GstFlvMuxClass * g_class)
191 {
192   mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
193   gst_pad_set_event_function (mux->srcpad, gst_flv_mux_handle_src_event);
194   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
195
196   /* property */
197   mux->streamable = DEFAULT_STREAMABLE;
198
199   mux->new_tags = FALSE;
200
201   mux->collect = gst_collect_pads_new ();
202   gst_collect_pads_set_function (mux->collect,
203       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_flv_mux_collected), mux);
204
205   gst_flv_mux_reset (GST_ELEMENT (mux));
206 }
207
208 static void
209 gst_flv_mux_finalize (GObject * object)
210 {
211   GstFlvMux *mux = GST_FLV_MUX (object);
212
213   gst_object_unref (mux->collect);
214
215   G_OBJECT_CLASS (parent_class)->finalize (object);
216 }
217
218 static void
219 gst_flv_mux_reset (GstElement * element)
220 {
221   GstFlvMux *mux = GST_FLV_MUX (element);
222   GSList *sl;
223
224   for (sl = mux->collect->data; sl != NULL; sl = g_slist_next (sl)) {
225     GstFlvPad *cpad = (GstFlvPad *) sl->data;
226
227     gst_flv_mux_reset_pad (mux, cpad, cpad->video);
228   }
229
230   g_list_foreach (mux->index, (GFunc) gst_flv_mux_index_entry_free, NULL);
231   g_list_free (mux->index);
232   mux->index = NULL;
233   mux->byte_count = 0;
234
235   mux->have_audio = mux->have_video = FALSE;
236   mux->duration = GST_CLOCK_TIME_NONE;
237   mux->new_tags = FALSE;
238
239   mux->state = GST_FLV_MUX_STATE_HEADER;
240
241   /* tags */
242   gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
243 }
244
245 static gboolean
246 gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event)
247 {
248   GstEventType type;
249
250   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
251
252   switch (type) {
253     case GST_EVENT_SEEK:
254       /* disable seeking for now */
255       return FALSE;
256     default:
257       break;
258   }
259
260   return gst_pad_event_default (pad, event);
261 }
262
263 static gboolean
264 gst_flv_mux_handle_sink_event (GstPad * pad, GstEvent * event)
265 {
266   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
267   gboolean ret = TRUE;
268
269   switch (GST_EVENT_TYPE (event)) {
270     case GST_EVENT_TAG:{
271       GstTagList *list;
272       GstTagSetter *setter = GST_TAG_SETTER (mux);
273       const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
274
275       gst_event_parse_tag (event, &list);
276       gst_tag_setter_merge_tags (setter, list, mode);
277       mux->new_tags = TRUE;
278       break;
279     }
280     default:
281       break;
282   }
283
284   /* now GstCollectPads can take care of the rest, e.g. EOS */
285   if (ret)
286     ret = mux->collect_event (pad, event);
287   gst_object_unref (mux);
288
289   return ret;
290 }
291
292 static gboolean
293 gst_flv_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
294 {
295   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
296   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
297   gboolean ret = TRUE;
298   GstStructure *s;
299
300   s = gst_caps_get_structure (caps, 0);
301
302   if (strcmp (gst_structure_get_name (s), "video/x-flash-video") == 0) {
303     cpad->video_codec = 2;
304   } else if (strcmp (gst_structure_get_name (s), "video/x-flash-screen") == 0) {
305     cpad->video_codec = 3;
306   } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-flash") == 0) {
307     cpad->video_codec = 4;
308   } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-alpha") == 0) {
309     cpad->video_codec = 5;
310   } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) {
311     cpad->video_codec = 7;
312   } else {
313     ret = FALSE;
314   }
315
316   if (ret && gst_structure_has_field (s, "codec_data")) {
317     const GValue *val = gst_structure_get_value (s, "codec_data");
318
319     if (val)
320       cpad->video_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
321   }
322
323   gst_object_unref (mux);
324
325   return ret;
326 }
327
328 static gboolean
329 gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
330 {
331   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
332   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
333   gboolean ret = TRUE;
334   GstStructure *s;
335
336   s = gst_caps_get_structure (caps, 0);
337
338   if (strcmp (gst_structure_get_name (s), "audio/x-adpcm") == 0) {
339     const gchar *layout = gst_structure_get_string (s, "layout");
340     if (layout && strcmp (layout, "swf") == 0) {
341       cpad->audio_codec = 1;
342     } else {
343       ret = FALSE;
344     }
345   } else if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) {
346     gint mpegversion;
347
348     if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
349       if (mpegversion == 1) {
350         gint layer;
351
352         if (gst_structure_get_int (s, "layer", &layer) && layer == 3) {
353           gint rate;
354
355           if (gst_structure_get_int (s, "rate", &rate) && rate == 8000)
356             cpad->audio_codec = 14;
357           else
358             cpad->audio_codec = 2;
359         } else {
360           ret = FALSE;
361         }
362       } else if (mpegversion == 4 || mpegversion == 2) {
363         cpad->audio_codec = 10;
364       } else {
365         ret = FALSE;
366       }
367     } else {
368       ret = FALSE;
369     }
370   } else if (strcmp (gst_structure_get_name (s), "audio/x-nellymoser") == 0) {
371     gint rate, channels;
372
373     if (gst_structure_get_int (s, "rate", &rate)
374         && gst_structure_get_int (s, "channels", &channels)) {
375       if (channels == 1 && rate == 16000)
376         cpad->audio_codec = 4;
377       else if (channels == 1 && rate == 8000)
378         cpad->audio_codec = 5;
379       else
380         cpad->audio_codec = 6;
381     } else {
382       cpad->audio_codec = 6;
383     }
384   } else if (strcmp (gst_structure_get_name (s), "audio/x-raw-int") == 0) {
385     gint endianness;
386
387     if (gst_structure_get_int (s, "endianness", &endianness)
388         && endianness == G_LITTLE_ENDIAN)
389       cpad->audio_codec = 3;
390     else
391       ret = FALSE;
392   } else if (strcmp (gst_structure_get_name (s), "audio/x-alaw") == 0) {
393     cpad->audio_codec = 7;
394   } else if (strcmp (gst_structure_get_name (s), "audio/x-mulaw") == 0) {
395     cpad->audio_codec = 8;
396   } else if (strcmp (gst_structure_get_name (s), "audio/x-speex") == 0) {
397     cpad->audio_codec = 11;
398   } else {
399     ret = FALSE;
400   }
401
402   if (ret) {
403     gint rate, channels, width;
404
405     if (gst_structure_get_int (s, "rate", &rate)) {
406       if (cpad->audio_codec == 10)
407         cpad->rate = 3;
408       else if (rate == 5512)
409         cpad->rate = 0;
410       else if (rate == 11025)
411         cpad->rate = 1;
412       else if (rate == 22050)
413         cpad->rate = 2;
414       else if (rate == 44100)
415         cpad->rate = 3;
416       else if (rate == 8000 && (cpad->audio_codec == 5
417               || cpad->audio_codec == 14))
418         cpad->rate = 0;
419       else if (rate == 16000 && cpad->audio_codec == 4)
420         cpad->rate = 0;
421       else
422         ret = FALSE;
423     } else if (cpad->audio_codec == 10) {
424       cpad->rate = 3;
425     } else {
426       ret = FALSE;
427     }
428
429     if (gst_structure_get_int (s, "channels", &channels)) {
430       if (cpad->audio_codec == 4 || cpad->audio_codec == 5
431           || cpad->audio_codec == 6)
432         cpad->channels = 0;
433       else if (cpad->audio_codec == 10)
434         cpad->channels = 1;
435       else if (channels == 1)
436         cpad->channels = 0;
437       else if (channels == 2)
438         cpad->channels = 1;
439       else
440         ret = FALSE;
441     } else if (cpad->audio_codec == 4 || cpad->audio_codec == 5
442         || cpad->audio_codec == 6) {
443       cpad->channels = 0;
444     } else if (cpad->audio_codec == 10) {
445       cpad->channels = 1;
446     } else {
447       ret = FALSE;
448     }
449
450     if (gst_structure_get_int (s, "width", &width)) {
451       if (cpad->audio_codec != 3)
452         cpad->width = 1;
453       else if (width == 8)
454         cpad->width = 0;
455       else if (width == 16)
456         cpad->width = 1;
457       else
458         ret = FALSE;
459     } else if (cpad->audio_codec != 3) {
460       cpad->width = 1;
461     } else {
462       ret = FALSE;
463     }
464   }
465
466   if (ret && gst_structure_has_field (s, "codec_data")) {
467     const GValue *val = gst_structure_get_value (s, "codec_data");
468
469     if (val)
470       cpad->audio_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
471   }
472
473   gst_object_unref (mux);
474
475   return ret;
476 }
477
478 static void
479 gst_flv_mux_reset_pad (GstFlvMux * mux, GstFlvPad * cpad, gboolean video)
480 {
481   cpad->video = video;
482
483   if (cpad->audio_codec_data)
484     gst_buffer_unref (cpad->audio_codec_data);
485   cpad->audio_codec_data = NULL;
486   cpad->audio_codec = G_MAXUINT;
487   cpad->rate = G_MAXUINT;
488   cpad->width = G_MAXUINT;
489   cpad->channels = G_MAXUINT;
490
491   if (cpad->video_codec_data)
492     gst_buffer_unref (cpad->video_codec_data);
493   cpad->video_codec_data = NULL;
494   cpad->video_codec = G_MAXUINT;
495   cpad->last_timestamp = 0;
496 }
497
498 static GstPad *
499 gst_flv_mux_request_new_pad (GstElement * element,
500     GstPadTemplate * templ, const gchar * pad_name)
501 {
502   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
503   GstFlvMux *mux = GST_FLV_MUX (element);
504   GstFlvPad *cpad;
505   GstPad *pad = NULL;
506   const gchar *name = NULL;
507   GstPadSetCapsFunction setcapsfunc = NULL;
508   gboolean video;
509
510   if (mux->state != GST_FLV_MUX_STATE_HEADER) {
511     GST_WARNING_OBJECT (mux, "Can't request pads after writing header");
512     return NULL;
513   }
514
515   if (templ == gst_element_class_get_pad_template (klass, "audio")) {
516     if (mux->have_audio) {
517       GST_WARNING_OBJECT (mux, "Already have an audio pad");
518       return NULL;
519     }
520     mux->have_audio = TRUE;
521     name = "audio";
522     video = FALSE;
523     setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_audio_pad_setcaps);
524   } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
525     if (mux->have_video) {
526       GST_WARNING_OBJECT (mux, "Already have a video pad");
527       return NULL;
528     }
529     mux->have_video = TRUE;
530     name = "video";
531     video = TRUE;
532     setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_video_pad_setcaps);
533   } else {
534     GST_WARNING_OBJECT (mux, "Invalid template");
535     return NULL;
536   }
537
538   pad = gst_pad_new_from_template (templ, name);
539   cpad = (GstFlvPad *)
540       gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad));
541
542   cpad->audio_codec_data = NULL;
543   cpad->video_codec_data = NULL;
544   gst_flv_mux_reset_pad (mux, cpad, video);
545
546   /* FIXME: hacked way to override/extend the event function of
547    * GstCollectPads; because it sets its own event function giving the
548    * element no access to events.
549    */
550   mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad);
551   gst_pad_set_event_function (pad,
552       GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event));
553
554   gst_pad_set_setcaps_function (pad, setcapsfunc);
555   gst_pad_set_active (pad, TRUE);
556   gst_element_add_pad (element, pad);
557
558   return pad;
559 }
560
561 static void
562 gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
563 {
564   GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
565   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
566
567   gst_flv_mux_reset_pad (mux, cpad, cpad->video);
568   gst_collect_pads_remove_pad (mux->collect, pad);
569   gst_element_remove_pad (element, pad);
570 }
571
572 static GstFlowReturn
573 gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer)
574 {
575   gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->srcpad));
576   /* pushing the buffer that rewrites the header will make it no longer be the
577    * total output size in bytes, but it doesn't matter at that point */
578   mux->byte_count += GST_BUFFER_SIZE (buffer);
579
580   return gst_pad_push (mux->srcpad, buffer);
581 }
582
583 static GstBuffer *
584 gst_flv_mux_create_header (GstFlvMux * mux)
585 {
586   GstBuffer *header;
587   guint8 *data;
588
589   header = gst_buffer_new_and_alloc (9 + 4);
590   data = GST_BUFFER_DATA (header);
591
592   data[0] = 'F';
593   data[1] = 'L';
594   data[2] = 'V';
595   data[3] = 0x01;               /* Version */
596
597   data[4] = (mux->have_audio << 2) | mux->have_video;   /* flags */
598   GST_WRITE_UINT32_BE (data + 5, 9);    /* data offset */
599   GST_WRITE_UINT32_BE (data + 9, 0);    /* previous tag size */
600
601   return header;
602 }
603
604 static GstBuffer *
605 gst_flv_mux_preallocate_index (GstFlvMux * mux)
606 {
607   GstBuffer *tmp;
608   guint8 *data;
609   gint preallocate_size;
610
611   /* preallocate index of size:
612    *  - 'keyframes' ECMA array key: 2 + 9 = 11 bytes
613    *  - nested ECMA array header, length and end marker: 8 bytes
614    *  - 'times' and 'filepositions' keys: 22 bytes
615    *  - two strict arrays headers and lengths: 10 bytes
616    *  - each index entry: 18 bytes
617    */
618   preallocate_size = 11 + 8 + 22 + 10 + MAX_INDEX_ENTRIES * 18;
619   GST_DEBUG_OBJECT (mux, "preallocating %d bytes for the index",
620       preallocate_size);
621
622   tmp = gst_buffer_new_and_alloc (preallocate_size);
623   data = GST_BUFFER_DATA (tmp);
624
625   /* prefill the space with a gstfiller: <spaces> script tag variable */
626   GST_WRITE_UINT16_BE (data, 9);        /* 9 characters */
627   memcpy (data + 2, "gstfiller", 9);
628   GST_WRITE_UINT8 (data + 11, AMF0_STRING_MARKER);      /* a string value */
629   GST_WRITE_UINT16_BE (data + 12, preallocate_size - 14);
630   memset (data + 14, ' ', preallocate_size - 14);       /* the rest is spaces */
631   return tmp;
632 }
633
634 static GstBuffer *
635 gst_flv_mux_create_number_script_value (const gchar * name, gdouble value)
636 {
637   GstBuffer *tmp = gst_buffer_new_and_alloc (2 + strlen (name) + 1 + 8);
638   guint8 *data = GST_BUFFER_DATA (tmp);
639   gsize len = strlen (name);
640
641   GST_WRITE_UINT16_BE (data, len);
642   data += 2;                    /* name length */
643   memcpy (data, name, len);
644   data += len;
645   *data++ = AMF0_NUMBER_MARKER; /* double type */
646   GST_WRITE_DOUBLE_BE (data, value);
647
648   return tmp;
649 }
650
651 static GstBuffer *
652 gst_flv_mux_create_metadata (GstFlvMux * mux)
653 {
654   const GstTagList *tags;
655   GstBuffer *script_tag, *tmp;
656   guint8 *data;
657   gint i, n_tags, tags_written = 0;
658
659   tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
660
661   GST_DEBUG_OBJECT (mux, "tags = %" GST_PTR_FORMAT, tags);
662
663   script_tag = gst_buffer_new_and_alloc (11);
664   data = GST_BUFFER_DATA (script_tag);
665
666   data[0] = 18;
667
668   /* Data size, unknown for now */
669   data[1] = 0;
670   data[2] = 0;
671   data[3] = 0;
672
673   /* Timestamp */
674   data[4] = data[5] = data[6] = data[7] = 0;
675
676   /* Stream ID */
677   data[8] = data[9] = data[10] = 0;
678
679   tmp = gst_buffer_new_and_alloc (13);
680   data = GST_BUFFER_DATA (tmp);
681   data[0] = AMF0_STRING_MARKER; /* string */
682   data[1] = 0;
683   data[2] = 10;                 /* length 10 */
684   memcpy (&data[3], "onMetaData", 10);
685
686   script_tag = gst_buffer_join (script_tag, tmp);
687
688   n_tags = (tags) ? gst_structure_n_fields ((GstStructure *) tags) : 0;
689   tmp = gst_buffer_new_and_alloc (5);
690   data = GST_BUFFER_DATA (tmp);
691   data[0] = 8;                  /* ECMA array */
692   GST_WRITE_UINT32_BE (data + 1, n_tags);
693   script_tag = gst_buffer_join (script_tag, tmp);
694
695   /* Some players expect the 'duration' to be always set. Fill it out later,
696      after querying the pads or after getting EOS */
697   if (!mux->streamable) {
698     tmp = gst_flv_mux_create_number_script_value ("duration", 86400);
699     script_tag = gst_buffer_join (script_tag, tmp);
700     tags_written++;
701
702     /* Sometimes the information about the total file size is useful for the
703        player. It will be filled later, after getting EOS */
704     tmp = gst_flv_mux_create_number_script_value ("filesize", 0);
705     script_tag = gst_buffer_join (script_tag, tmp);
706     tags_written++;
707
708     /* Preallocate space for the index to be written at EOS */
709     tmp = gst_flv_mux_preallocate_index (mux);
710     script_tag = gst_buffer_join (script_tag, tmp);
711   } else {
712     GST_DEBUG_OBJECT (mux, "not preallocating index, streamable mode");
713   }
714
715   for (i = 0; tags && i < n_tags; i++) {
716     const gchar *tag_name =
717         gst_structure_nth_field_name ((const GstStructure *) tags, i);
718     if (!strcmp (tag_name, GST_TAG_DURATION)) {
719       guint64 dur;
720
721       if (!gst_tag_list_get_uint64 (tags, GST_TAG_DURATION, &dur))
722         continue;
723       mux->duration = dur;
724     } else if (!strcmp (tag_name, GST_TAG_ARTIST) ||
725         !strcmp (tag_name, GST_TAG_TITLE)) {
726       gchar *s;
727       const gchar *t = NULL;
728
729       if (!strcmp (tag_name, GST_TAG_ARTIST))
730         t = "creator";
731       else if (!strcmp (tag_name, GST_TAG_TITLE))
732         t = "title";
733
734       if (!gst_tag_list_get_string (tags, tag_name, &s))
735         continue;
736
737       tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s));
738       data = GST_BUFFER_DATA (tmp);
739       data[0] = 0;              /* tag name length */
740       data[1] = strlen (t);
741       memcpy (&data[2], t, strlen (t));
742       data[2 + strlen (t)] = 2; /* string */
743       data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff;
744       data[4 + strlen (t)] = (strlen (s)) & 0xff;
745       memcpy (&data[5 + strlen (t)], s, strlen (s));
746       script_tag = gst_buffer_join (script_tag, tmp);
747
748       g_free (s);
749       tags_written++;
750     }
751   }
752
753   if (mux->duration == GST_CLOCK_TIME_NONE) {
754     GSList *l;
755
756     GstFormat fmt = GST_FORMAT_TIME;
757     guint64 dur;
758
759     for (l = mux->collect->data; l; l = l->next) {
760       GstCollectData *cdata = l->data;
761
762       fmt = GST_FORMAT_TIME;
763
764       if (gst_pad_query_peer_duration (cdata->pad, &fmt, (gint64 *) & dur) &&
765           fmt == GST_FORMAT_TIME && dur != GST_CLOCK_TIME_NONE) {
766         if (mux->duration == GST_CLOCK_TIME_NONE)
767           mux->duration = dur;
768         else
769           mux->duration = MAX (dur, mux->duration);
770       }
771     }
772   }
773
774   if (!mux->streamable && mux->duration != GST_CLOCK_TIME_NONE) {
775     gdouble d;
776     d = gst_guint64_to_gdouble (mux->duration);
777     d /= (gdouble) GST_SECOND;
778
779     GST_DEBUG_OBJECT (mux, "determined the duration to be %f", d);
780     data = GST_BUFFER_DATA (script_tag);
781     GST_WRITE_DOUBLE_BE (data + 29 + 2 + 8 + 1, d);
782   }
783
784   if (mux->have_video) {
785     GstPad *video_pad = NULL;
786     GstFlvPad *cpad;
787     GSList *l = mux->collect->data;
788
789     for (; l; l = l->next) {
790       cpad = l->data;
791       if (cpad && cpad->video) {
792         video_pad = cpad->collect.pad;
793         break;
794       }
795     }
796
797     if (video_pad && GST_PAD_CAPS (video_pad)) {
798       GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0);
799       gint size;
800       gint num, den;
801
802       GST_DEBUG_OBJECT (mux, "putting videocodecid %d in the metadata",
803           cpad->video_codec);
804
805       tmp = gst_flv_mux_create_number_script_value ("videocodecid",
806           cpad->video_codec);
807       script_tag = gst_buffer_join (script_tag, tmp);
808       tags_written++;
809
810       if (gst_structure_get_int (s, "width", &size)) {
811         GST_DEBUG_OBJECT (mux, "putting width %d in the metadata", size);
812
813         tmp = gst_flv_mux_create_number_script_value ("width", size);
814         script_tag = gst_buffer_join (script_tag, tmp);
815         tags_written++;
816       }
817
818       if (gst_structure_get_int (s, "height", &size)) {
819         GST_DEBUG_OBJECT (mux, "putting height %d in the metadata", size);
820
821         tmp = gst_flv_mux_create_number_script_value ("height", size);
822         script_tag = gst_buffer_join (script_tag, tmp);
823         tags_written++;
824       }
825
826       if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &num, &den)) {
827         gdouble d;
828
829         d = num;
830         GST_DEBUG_OBJECT (mux, "putting AspectRatioX %f in the metadata", d);
831
832         tmp = gst_flv_mux_create_number_script_value ("AspectRatioX", d);
833         script_tag = gst_buffer_join (script_tag, tmp);
834         tags_written++;
835
836         d = den;
837         GST_DEBUG_OBJECT (mux, "putting AspectRatioY %f in the metadata", d);
838
839         tmp = gst_flv_mux_create_number_script_value ("AspectRatioY", d);
840         script_tag = gst_buffer_join (script_tag, tmp);
841         tags_written++;
842       }
843
844       if (gst_structure_get_fraction (s, "framerate", &num, &den)) {
845         gdouble d;
846
847         gst_util_fraction_to_double (num, den, &d);
848         GST_DEBUG_OBJECT (mux, "putting framerate %f in the metadata", d);
849
850         tmp = gst_flv_mux_create_number_script_value ("framerate", d);
851         script_tag = gst_buffer_join (script_tag, tmp);
852         tags_written++;
853       }
854     }
855   }
856
857   if (mux->have_audio) {
858     GstPad *audio_pad = NULL;
859     GstFlvPad *cpad;
860     GSList *l = mux->collect->data;
861
862     for (; l; l = l->next) {
863       cpad = l->data;
864       if (cpad && !cpad->video) {
865         audio_pad = cpad->collect.pad;
866         break;
867       }
868     }
869
870     if (audio_pad) {
871       GST_DEBUG_OBJECT (mux, "putting audiocodecid %d in the metadata",
872           cpad->audio_codec);
873
874       tmp = gst_flv_mux_create_number_script_value ("audiocodecid",
875           cpad->audio_codec);
876       script_tag = gst_buffer_join (script_tag, tmp);
877       tags_written++;
878     }
879   }
880
881   {
882     const gchar *s = "GStreamer FLV muxer";
883
884     tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s));
885     data = GST_BUFFER_DATA (tmp);
886     data[0] = 0;                /* 15 bytes name */
887     data[1] = 15;
888     memcpy (&data[2], "metadatacreator", 15);
889     data[17] = 2;               /* string */
890     data[18] = (strlen (s) >> 8) & 0xff;
891     data[19] = (strlen (s)) & 0xff;
892     memcpy (&data[20], s, strlen (s));
893     script_tag = gst_buffer_join (script_tag, tmp);
894
895     tags_written++;
896   }
897
898   {
899     GTimeVal tv = { 0, };
900     time_t secs;
901     struct tm *tm;
902     gchar *s;
903     static const gchar *weekdays[] = {
904       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
905     };
906     static const gchar *months[] = {
907       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
908       "Aug", "Sep", "Oct", "Nov", "Dec"
909     };
910
911     g_get_current_time (&tv);
912     secs = tv.tv_sec;
913     tm = gmtime (&secs);
914
915     s = g_strdup_printf ("%s %s %d %d:%d:%d %d", weekdays[tm->tm_wday],
916         months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
917         tm->tm_year + 1900);
918
919     tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s));
920     data = GST_BUFFER_DATA (tmp);
921     data[0] = 0;                /* 12 bytes name */
922     data[1] = 12;
923     memcpy (&data[2], "creationdate", 12);
924     data[14] = 2;               /* string */
925     data[15] = (strlen (s) >> 8) & 0xff;
926     data[16] = (strlen (s)) & 0xff;
927     memcpy (&data[17], s, strlen (s));
928     script_tag = gst_buffer_join (script_tag, tmp);
929
930     g_free (s);
931     tags_written++;
932   }
933
934   tmp = gst_buffer_new_and_alloc (2 + 0 + 1);
935   data = GST_BUFFER_DATA (tmp);
936   data[0] = 0;                  /* 0 byte size */
937   data[1] = 0;
938   data[2] = 9;                  /* end marker */
939   script_tag = gst_buffer_join (script_tag, tmp);
940   tags_written++;
941
942   tmp = gst_buffer_new_and_alloc (4);
943   data = GST_BUFFER_DATA (tmp);
944   GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
945   script_tag = gst_buffer_join (script_tag, tmp);
946
947   data = GST_BUFFER_DATA (script_tag);
948   data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff;
949   data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff;
950   data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff;
951
952   GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written);
953
954   return script_tag;
955 }
956
957 static GstBuffer *
958 gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
959     GstFlvPad * cpad, gboolean is_codec_data)
960 {
961   GstBuffer *tag;
962   guint8 *data;
963   guint size;
964   guint32 timestamp =
965       (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
966       GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
967
968   size = 11;
969   if (cpad->video) {
970     size += 1;
971     if (cpad->video_codec == 7)
972       size += 4 + GST_BUFFER_SIZE (buffer);
973     else
974       size += GST_BUFFER_SIZE (buffer);
975   } else {
976     size += 1;
977     if (cpad->audio_codec == 10)
978       size += 1 + GST_BUFFER_SIZE (buffer);
979     else
980       size += GST_BUFFER_SIZE (buffer);
981   }
982   size += 4;
983
984   tag = gst_buffer_new_and_alloc (size);
985   GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND;
986   data = GST_BUFFER_DATA (tag);
987   memset (data, 0, size);
988
989   data[0] = (cpad->video) ? 9 : 8;
990
991   data[1] = ((size - 11 - 4) >> 16) & 0xff;
992   data[2] = ((size - 11 - 4) >> 8) & 0xff;
993   data[3] = ((size - 11 - 4) >> 0) & 0xff;
994
995   /* wrap the timestamp every G_MAXINT32 miliseconds */
996   timestamp &= 0x7fffffff;
997   data[4] = (timestamp >> 16) & 0xff;
998   data[5] = (timestamp >> 8) & 0xff;
999   data[6] = (timestamp >> 0) & 0xff;
1000   data[7] = (timestamp >> 24) & 0xff;
1001
1002   data[8] = data[9] = data[10] = 0;
1003
1004   if (cpad->video) {
1005     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1006       data[11] |= 2 << 4;
1007     else
1008       data[11] |= 1 << 4;
1009
1010     data[11] |= cpad->video_codec & 0x0f;
1011
1012     if (cpad->video_codec == 7) {
1013       data[12] = is_codec_data ? 0 : 1;
1014
1015       /* FIXME: what to do about composition time */
1016       data[13] = data[14] = data[15] = 0;
1017
1018       memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer),
1019           GST_BUFFER_SIZE (buffer));
1020     } else {
1021       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
1022           GST_BUFFER_SIZE (buffer));
1023     }
1024   } else {
1025     data[11] |= (cpad->audio_codec << 4) & 0xf0;
1026     data[11] |= (cpad->rate << 2) & 0x0c;
1027     data[11] |= (cpad->width << 1) & 0x02;
1028     data[11] |= (cpad->channels << 0) & 0x01;
1029
1030     if (cpad->audio_codec == 10) {
1031       data[12] = is_codec_data ? 0 : 1;
1032
1033       memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer),
1034           GST_BUFFER_SIZE (buffer));
1035     } else {
1036       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
1037           GST_BUFFER_SIZE (buffer));
1038     }
1039   }
1040
1041   GST_WRITE_UINT32_BE (data + size - 4, size - 4);
1042
1043   gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS);
1044   /* mark the buffer if it's an audio buffer and there's also video being muxed
1045    * or it's a video interframe */
1046   if ((mux->have_video && !cpad->video) ||
1047       GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1048     GST_BUFFER_FLAG_SET (tag, GST_BUFFER_FLAG_DELTA_UNIT);
1049
1050   GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) =
1051       GST_BUFFER_OFFSET_NONE;
1052
1053   return tag;
1054 }
1055
1056 static inline GstBuffer *
1057 gst_flv_mux_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1058     GstFlvPad * cpad)
1059 {
1060   return gst_flv_mux_buffer_to_tag_internal (mux, buffer, cpad, FALSE);
1061 }
1062
1063 static inline GstBuffer *
1064 gst_flv_mux_codec_data_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1065     GstFlvPad * cpad)
1066 {
1067   return gst_flv_mux_buffer_to_tag_internal (mux, buffer, cpad, TRUE);
1068 }
1069
1070 static void
1071 gst_flv_mux_put_buffer_in_streamheader (GValue * streamheader,
1072     GstBuffer * buffer)
1073 {
1074   GValue value = { 0 };
1075   GstBuffer *buf;
1076
1077   g_value_init (&value, GST_TYPE_BUFFER);
1078   buf = gst_buffer_copy (buffer);
1079   gst_value_set_buffer (&value, buf);
1080   gst_buffer_unref (buf);
1081   gst_value_array_append_value (streamheader, &value);
1082   g_value_unset (&value);
1083 }
1084
1085 static GstFlowReturn
1086 gst_flv_mux_write_header (GstFlvMux * mux)
1087 {
1088   GstBuffer *header, *metadata;
1089   GstBuffer *video_codec_data, *audio_codec_data;
1090   GstCaps *caps;
1091   GstStructure *structure;
1092   GValue streamheader = { 0 };
1093   GSList *l;
1094   GstFlowReturn ret;
1095
1096   header = gst_flv_mux_create_header (mux);
1097   metadata = gst_flv_mux_create_metadata (mux);
1098   video_codec_data = NULL;
1099   audio_codec_data = NULL;
1100
1101   for (l = mux->collect->data; l != NULL; l = l->next) {
1102     GstFlvPad *cpad = l->data;
1103
1104     /* Get H.264 and AAC codec data, if present */
1105     if (cpad && cpad->video && cpad->video_codec == 7) {
1106       if (cpad->video_codec_data == NULL)
1107         GST_WARNING_OBJECT (mux, "Codec data for video stream not found, "
1108             "output might not be playable");
1109       else
1110         video_codec_data =
1111             gst_flv_mux_codec_data_buffer_to_tag (mux, cpad->video_codec_data,
1112             cpad);
1113     } else if (cpad && !cpad->video && cpad->audio_codec == 10) {
1114       if (cpad->audio_codec_data == NULL)
1115         GST_WARNING_OBJECT (mux, "Codec data for audio stream not found, "
1116             "output might not be playable");
1117       else
1118         audio_codec_data =
1119             gst_flv_mux_codec_data_buffer_to_tag (mux, cpad->audio_codec_data,
1120             cpad);
1121     }
1122   }
1123
1124   /* mark buffers that will go in the streamheader */
1125   GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS);
1126   GST_BUFFER_FLAG_SET (metadata, GST_BUFFER_FLAG_IN_CAPS);
1127   if (video_codec_data != NULL) {
1128     GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_IN_CAPS);
1129     /* mark as a delta unit, so downstream will not try to synchronize on that
1130      * buffer - to actually start playback you need a real video keyframe */
1131     GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_DELTA_UNIT);
1132   }
1133   if (audio_codec_data != NULL) {
1134     GST_BUFFER_FLAG_SET (audio_codec_data, GST_BUFFER_FLAG_IN_CAPS);
1135   }
1136
1137   /* put buffers in streamheader */
1138   g_value_init (&streamheader, GST_TYPE_ARRAY);
1139   gst_flv_mux_put_buffer_in_streamheader (&streamheader, header);
1140   gst_flv_mux_put_buffer_in_streamheader (&streamheader, metadata);
1141   if (video_codec_data != NULL)
1142     gst_flv_mux_put_buffer_in_streamheader (&streamheader, video_codec_data);
1143   if (audio_codec_data != NULL)
1144     gst_flv_mux_put_buffer_in_streamheader (&streamheader, audio_codec_data);
1145
1146   /* create the caps and put the streamheader in them */
1147   caps = gst_caps_new_simple ("video/x-flv", NULL);
1148   caps = gst_caps_make_writable (caps);
1149   structure = gst_caps_get_structure (caps, 0);
1150   gst_structure_set_value (structure, "streamheader", &streamheader);
1151   g_value_unset (&streamheader);
1152
1153   if (GST_PAD_CAPS (mux->srcpad) == NULL)
1154     gst_pad_set_caps (mux->srcpad, caps);
1155
1156   gst_caps_unref (caps);
1157
1158   /* push the header buffer, the metadata and the codec info, if any */
1159   ret = gst_flv_mux_push (mux, header);
1160   if (ret != GST_FLOW_OK)
1161     return ret;
1162   ret = gst_flv_mux_push (mux, metadata);
1163   if (ret != GST_FLOW_OK)
1164     return ret;
1165   if (video_codec_data != NULL) {
1166     ret = gst_flv_mux_push (mux, video_codec_data);
1167     if (ret != GST_FLOW_OK)
1168       return ret;
1169   }
1170   if (audio_codec_data != NULL) {
1171     ret = gst_flv_mux_push (mux, audio_codec_data);
1172     if (ret != GST_FLOW_OK)
1173       return ret;
1174   }
1175   return GST_FLOW_OK;
1176 }
1177
1178 static void
1179 gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer, GstFlvPad * cpad)
1180 {
1181   /*
1182    * Add the tag byte offset and to the index if it's a valid seek point, which
1183    * means it's either a video keyframe or if there is no video pad (in that
1184    * case every FLV tag is a valid seek point)
1185    */
1186   if (mux->have_video &&
1187       (!cpad->video ||
1188           GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)))
1189     return;
1190
1191   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
1192     GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
1193     entry->position = mux->byte_count;
1194     entry->time =
1195         gst_guint64_to_gdouble (GST_BUFFER_TIMESTAMP (buffer)) / GST_SECOND;
1196     mux->index = g_list_prepend (mux->index, entry);
1197   }
1198 }
1199
1200 static GstFlowReturn
1201 gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
1202 {
1203   GstBuffer *tag;
1204   GstBuffer *buffer =
1205       gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad);
1206   GstFlowReturn ret;
1207
1208   /* arrange downstream running time */
1209   buffer = gst_buffer_make_metadata_writable (buffer);
1210   GST_BUFFER_TIMESTAMP (buffer) =
1211       gst_segment_to_running_time (&cpad->collect.segment,
1212       GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer));
1213
1214   if (!mux->streamable)
1215     gst_flv_mux_update_index (mux, buffer, cpad);
1216
1217   tag = gst_flv_mux_buffer_to_tag (mux, buffer, cpad);
1218
1219   gst_buffer_unref (buffer);
1220
1221   ret = gst_flv_mux_push (mux, tag);
1222
1223   if (ret == GST_FLOW_OK && GST_BUFFER_TIMESTAMP_IS_VALID (tag))
1224     cpad->last_timestamp = GST_BUFFER_TIMESTAMP (tag);
1225
1226   return ret;
1227 }
1228
1229 static guint64
1230 gst_flv_mux_determine_duration (GstFlvMux * mux)
1231 {
1232   GSList *l;
1233   GstClockTime duration = GST_CLOCK_TIME_NONE;
1234
1235   GST_DEBUG_OBJECT (mux, "trying to determine the duration "
1236       "from pad timestamps");
1237
1238   for (l = mux->collect->data; l != NULL; l = l->next) {
1239     GstFlvPad *cpad = l->data;
1240
1241     if (cpad && (cpad->last_timestamp != GST_CLOCK_TIME_NONE)) {
1242       if (duration == GST_CLOCK_TIME_NONE)
1243         duration = cpad->last_timestamp;
1244       else
1245         duration = MAX (duration, cpad->last_timestamp);
1246     }
1247   }
1248
1249   if (duration == GST_CLOCK_TIME_NONE) {
1250     GST_DEBUG_OBJECT (mux, "not able to determine duration "
1251         "from pad timestamps, assuming 0");
1252     return 0;
1253   }
1254
1255   return duration;
1256 }
1257
1258 static GstFlowReturn
1259 gst_flv_mux_rewrite_header (GstFlvMux * mux)
1260 {
1261   GstBuffer *rewrite, *index, *tmp;
1262   GstEvent *event;
1263   guint8 *data;
1264   gdouble d;
1265   GList *l;
1266   guint32 index_len, allocate_size;
1267   guint32 i, index_skip;
1268
1269   if (mux->streamable)
1270     return GST_FLOW_OK;
1271
1272   /* seek back to the preallocated index space */
1273   event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
1274       13 + 29, GST_CLOCK_TIME_NONE, 13 + 29);
1275   if (!gst_pad_push_event (mux->srcpad, event)) {
1276     GST_WARNING_OBJECT (mux, "Seek to rewrite header failed");
1277     return GST_FLOW_OK;
1278   }
1279
1280   /* if we were not able to determine the duration before, set it now */
1281   if (mux->duration == GST_CLOCK_TIME_NONE)
1282     mux->duration = gst_flv_mux_determine_duration (mux);
1283
1284   /* rewrite the duration tag */
1285   d = gst_guint64_to_gdouble (mux->duration);
1286   d /= (gdouble) GST_SECOND;
1287
1288   GST_DEBUG_OBJECT (mux, "determined the final duration to be %f", d);
1289
1290   rewrite = gst_flv_mux_create_number_script_value ("duration", d);
1291
1292   /* rewrite the filesize tag */
1293   d = gst_guint64_to_gdouble (mux->byte_count);
1294
1295   GST_DEBUG_OBJECT (mux, "putting total filesize %f in the metadata", d);
1296
1297   tmp = gst_flv_mux_create_number_script_value ("filesize", d);
1298   rewrite = gst_buffer_join (rewrite, tmp);
1299
1300   if (!mux->index) {
1301     /* no index, so push buffer and return */
1302     return gst_flv_mux_push (mux, rewrite);
1303   }
1304
1305   /* rewrite the index */
1306   mux->index = g_list_reverse (mux->index);
1307   index_len = g_list_length (mux->index);
1308
1309   /* We write at most MAX_INDEX_ENTRIES elements */
1310   if (index_len > MAX_INDEX_ENTRIES) {
1311     index_skip = 1 + index_len / MAX_INDEX_ENTRIES;
1312     index_len = (index_len + index_skip - 1) / index_skip;
1313   } else {
1314     index_skip = 1;
1315   }
1316
1317   GST_DEBUG_OBJECT (mux, "Index length is %d", index_len);
1318   /* see size calculation in gst_flv_mux_preallocate_index */
1319   allocate_size = 11 + 8 + 22 + 10 + index_len * 18;
1320   GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size);
1321   index = gst_buffer_new_and_alloc (allocate_size);
1322   data = GST_BUFFER_DATA (index);
1323
1324   GST_WRITE_UINT16_BE (data, 9);        /* the 'keyframes' key */
1325   memcpy (data + 2, "keyframes", 9);
1326   GST_WRITE_UINT8 (data + 11, 8);       /* nested ECMA array */
1327   GST_WRITE_UINT32_BE (data + 12, 2);   /* two elements */
1328   GST_WRITE_UINT16_BE (data + 16, 5);   /* first string key: 'times' */
1329   memcpy (data + 18, "times", 5);
1330   GST_WRITE_UINT8 (data + 23, 10);      /* strict array */
1331   GST_WRITE_UINT32_BE (data + 24, index_len);
1332   data += 28;
1333
1334   /* the keyframes' times */
1335   for (i = 0, l = mux->index; l; l = l->next, i++) {
1336     GstFlvMuxIndexEntry *entry = l->data;
1337
1338     if (i % index_skip != 0)
1339       continue;
1340     GST_WRITE_UINT8 (data, 0);  /* numeric (aka double) */
1341     GST_WRITE_DOUBLE_BE (data + 1, entry->time);
1342     data += 9;
1343   }
1344
1345   GST_WRITE_UINT16_BE (data, 13);       /* second string key: 'filepositions' */
1346   memcpy (data + 2, "filepositions", 13);
1347   GST_WRITE_UINT8 (data + 15, 10);      /* strict array */
1348   GST_WRITE_UINT32_BE (data + 16, index_len);
1349   data += 20;
1350
1351   /* the keyframes' file positions */
1352   for (i = 0, l = mux->index; l; l = l->next, i++) {
1353     GstFlvMuxIndexEntry *entry = l->data;
1354
1355     if (i % index_skip != 0)
1356       continue;
1357     GST_WRITE_UINT8 (data, 0);
1358     GST_WRITE_DOUBLE_BE (data + 1, entry->position);
1359     data += 9;
1360   }
1361
1362   GST_WRITE_UINT24_BE (data, 9);        /* finish the ECMA array */
1363
1364   /* If there is space left in the prefilled area, reinsert the filler.
1365      There is at least 18  bytes free, so it will always fit. */
1366   if (index_len < MAX_INDEX_ENTRIES) {
1367     GstBuffer *tmp;
1368     guint8 *data;
1369     guint32 remaining_filler_size;
1370
1371     tmp = gst_buffer_new_and_alloc (14);
1372     data = GST_BUFFER_DATA (tmp);
1373     GST_WRITE_UINT16_BE (data, 9);
1374     memcpy (data + 2, "gstfiller", 9);
1375     GST_WRITE_UINT8 (data + 11, 2);     /* string */
1376
1377     /* There is 18 bytes per remaining index entry minus what is used for
1378      * the'gstfiller' key. The rest is already filled with spaces, so just need
1379      * to update length. */
1380     remaining_filler_size = (MAX_INDEX_ENTRIES - index_len) * 18 - 14;
1381     GST_DEBUG_OBJECT (mux, "Remaining filler size is %d bytes",
1382         remaining_filler_size);
1383     GST_WRITE_UINT16_BE (data + 12, remaining_filler_size);
1384     index = gst_buffer_join (index, tmp);
1385   }
1386
1387   rewrite = gst_buffer_join (rewrite, index);
1388
1389   gst_buffer_set_caps (rewrite, GST_PAD_CAPS (mux->srcpad));
1390   return gst_flv_mux_push (mux, rewrite);
1391 }
1392
1393 static GstFlowReturn
1394 gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
1395 {
1396   GstFlvMux *mux = GST_FLV_MUX (user_data);
1397   GstFlvPad *best;
1398   GstClockTime best_time;
1399   GstFlowReturn ret;
1400   GSList *sl;
1401   gboolean eos = TRUE;
1402
1403   if (mux->state == GST_FLV_MUX_STATE_HEADER) {
1404     if (mux->collect->data == NULL) {
1405       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1406           ("No input streams configured"));
1407       return GST_FLOW_ERROR;
1408     }
1409
1410     if (gst_pad_push_event (mux->srcpad,
1411             gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)))
1412       ret = gst_flv_mux_write_header (mux);
1413     else
1414       ret = GST_FLOW_ERROR;
1415
1416     if (ret != GST_FLOW_OK)
1417       return ret;
1418     mux->state = GST_FLV_MUX_STATE_DATA;
1419   }
1420
1421   if (mux->new_tags) {
1422     GstBuffer *buf = gst_flv_mux_create_metadata (mux);
1423     gst_flv_mux_push (mux, buf);
1424     mux->new_tags = FALSE;
1425   }
1426
1427
1428   best = NULL;
1429   best_time = GST_CLOCK_TIME_NONE;
1430   for (sl = mux->collect->data; sl; sl = sl->next) {
1431     GstFlvPad *cpad = sl->data;
1432     GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad);
1433     GstClockTime time;
1434
1435     if (!buffer)
1436       continue;
1437
1438     eos = FALSE;
1439
1440     time = GST_BUFFER_TIMESTAMP (buffer);
1441     gst_buffer_unref (buffer);
1442
1443     /* Use buffers without valid timestamp first */
1444     if (!GST_CLOCK_TIME_IS_VALID (time)) {
1445       GST_WARNING_OBJECT (pads, "Buffer without valid timestamp");
1446
1447       best_time = cpad->last_timestamp;
1448       best = cpad;
1449       break;
1450     }
1451
1452     time = gst_segment_to_running_time (&cpad->collect.segment,
1453         GST_FORMAT_TIME, time);
1454     if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
1455       GST_DEBUG_OBJECT (mux, "clipping buffer on pad %s outside segment",
1456           GST_PAD_NAME (cpad->collect.pad));
1457       buffer = gst_collect_pads_pop (pads, (GstCollectData *) cpad);
1458       gst_buffer_unref (buffer);
1459       return GST_FLOW_OK;
1460     }
1461
1462     if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time)
1463             && time < best_time)) {
1464       best = cpad;
1465       best_time = time;
1466     }
1467   }
1468
1469   /* The FLV timestamp is an int32 field. For non-live streams error out if a
1470      bigger timestamp is seen, for live the timestamp will get wrapped in
1471      gst_flv_mux_buffer_to_tag */
1472   if (!mux->streamable && GST_CLOCK_TIME_IS_VALID (best_time)
1473       && best_time / GST_MSECOND > G_MAXINT32) {
1474     GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS");
1475     eos = TRUE;
1476   }
1477
1478   if (!eos && best) {
1479     return gst_flv_mux_write_buffer (mux, best);
1480   } else if (eos) {
1481     gst_flv_mux_rewrite_header (mux);
1482     gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
1483     return GST_FLOW_UNEXPECTED;
1484   } else {
1485     return GST_FLOW_OK;
1486   }
1487 }
1488
1489 static void
1490 gst_flv_mux_get_property (GObject * object,
1491     guint prop_id, GValue * value, GParamSpec * pspec)
1492 {
1493   GstFlvMux *mux = GST_FLV_MUX (object);
1494
1495   switch (prop_id) {
1496     case PROP_STREAMABLE:
1497       g_value_set_boolean (value, mux->streamable);
1498       break;
1499     default:
1500       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1501       break;
1502   }
1503 }
1504
1505 static void
1506 gst_flv_mux_set_property (GObject * object,
1507     guint prop_id, const GValue * value, GParamSpec * pspec)
1508 {
1509   GstFlvMux *mux = GST_FLV_MUX (object);
1510
1511   switch (prop_id) {
1512     case PROP_STREAMABLE:
1513       mux->streamable = g_value_get_boolean (value);
1514       if (mux->streamable)
1515         gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
1516             GST_TAG_MERGE_REPLACE);
1517       else
1518         gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
1519             GST_TAG_MERGE_KEEP);
1520       break;
1521     default:
1522       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1523       break;
1524   }
1525 }
1526
1527 static GstStateChangeReturn
1528 gst_flv_mux_change_state (GstElement * element, GstStateChange transition)
1529 {
1530   GstStateChangeReturn ret;
1531   GstFlvMux *mux = GST_FLV_MUX (element);
1532
1533   switch (transition) {
1534     case GST_STATE_CHANGE_NULL_TO_READY:
1535       break;
1536     case GST_STATE_CHANGE_READY_TO_PAUSED:
1537       gst_collect_pads_start (mux->collect);
1538       break;
1539     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1540       break;
1541     case GST_STATE_CHANGE_PAUSED_TO_READY:
1542       gst_collect_pads_stop (mux->collect);
1543       break;
1544     default:
1545       break;
1546   }
1547
1548   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1549
1550   switch (transition) {
1551     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1552       break;
1553     case GST_STATE_CHANGE_PAUSED_TO_READY:
1554       gst_flv_mux_reset (GST_ELEMENT (mux));
1555       break;
1556     case GST_STATE_CHANGE_READY_TO_NULL:
1557       break;
1558     default:
1559       break;
1560   }
1561
1562   return ret;
1563 }