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