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