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