e731604711b33e37c1f7ba77ddbbd2e2530369b3
[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   tmp = gst_flv_mux_create_number_script_value ("duration", 0);
689   script_tag = gst_buffer_join (script_tag, tmp);
690   tags_written++;
691
692   /* Sometimes the information about the total file size is useful for the
693      player. It will be filled later, after getting EOS */
694   if (!mux->streamable) {
695     tmp = gst_flv_mux_create_number_script_value ("filesize", 0);
696     script_tag = gst_buffer_join (script_tag, tmp);
697     tags_written++;
698   }
699
700   if (!mux->streamable) {
701     tmp = gst_flv_mux_preallocate_index (mux);
702     script_tag = gst_buffer_join (script_tag, tmp);
703   } else {
704     GST_DEBUG_OBJECT (mux, "not preallocating index, streamable mode");
705   }
706
707   for (i = 0; tags && i < n_tags; i++) {
708     const gchar *tag_name =
709         gst_structure_nth_field_name ((const GstStructure *) tags, i);
710     if (!strcmp (tag_name, GST_TAG_DURATION)) {
711       guint64 dur;
712
713       if (!gst_tag_list_get_uint64 (tags, GST_TAG_DURATION, &dur))
714         continue;
715       mux->duration = dur;
716     } else if (!strcmp (tag_name, GST_TAG_ARTIST) ||
717         !strcmp (tag_name, GST_TAG_TITLE)) {
718       gchar *s;
719       const gchar *t = NULL;
720
721       if (!strcmp (tag_name, GST_TAG_ARTIST))
722         t = "creator";
723       else if (!strcmp (tag_name, GST_TAG_TITLE))
724         t = "title";
725
726       if (!gst_tag_list_get_string (tags, tag_name, &s))
727         continue;
728
729       tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s));
730       data = GST_BUFFER_DATA (tmp);
731       data[0] = 0;              /* tag name length */
732       data[1] = strlen (t);
733       memcpy (&data[2], t, strlen (t));
734       data[2 + strlen (t)] = 2; /* string */
735       data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff;
736       data[4 + strlen (t)] = (strlen (s)) & 0xff;
737       memcpy (&data[5 + strlen (t)], s, strlen (s));
738       script_tag = gst_buffer_join (script_tag, tmp);
739
740       g_free (s);
741       tags_written++;
742     }
743   }
744
745   if (mux->duration == GST_CLOCK_TIME_NONE) {
746     GSList *l;
747
748     GstFormat fmt = GST_FORMAT_TIME;
749     guint64 dur;
750
751     for (l = mux->collect->data; l; l = l->next) {
752       GstCollectData *cdata = l->data;
753
754       fmt = GST_FORMAT_TIME;
755
756       if (gst_pad_query_peer_duration (cdata->pad, &fmt, (gint64 *) & dur) &&
757           fmt == GST_FORMAT_TIME && dur != GST_CLOCK_TIME_NONE) {
758         if (mux->duration == GST_CLOCK_TIME_NONE)
759           mux->duration = dur;
760         else
761           mux->duration = MAX (dur, mux->duration);
762       }
763     }
764   }
765
766   if (mux->duration != GST_CLOCK_TIME_NONE) {
767     gdouble d;
768     d = gst_guint64_to_gdouble (mux->duration);
769     d /= (gdouble) GST_SECOND;
770
771     GST_DEBUG_OBJECT (mux, "determined the duration to be %f", d);
772     data = GST_BUFFER_DATA (script_tag);
773     GST_WRITE_DOUBLE_BE (data + 29 + 2 + 8 + 1, d);
774   }
775
776   if (mux->have_video) {
777     GstPad *video_pad = NULL;
778     GstFlvPad *cpad;
779     GSList *l = mux->collect->data;
780
781     for (; l; l = l->next) {
782       cpad = l->data;
783       if (cpad && cpad->video) {
784         video_pad = cpad->collect.pad;
785         break;
786       }
787     }
788
789     if (video_pad && GST_PAD_CAPS (video_pad)) {
790       GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0);
791       gint size;
792       gint num, den;
793
794       GST_DEBUG_OBJECT (mux, "putting videocodecid %d in the metadata",
795           cpad->video_codec);
796
797       tmp = gst_flv_mux_create_number_script_value ("videocodecid",
798           cpad->video_codec);
799       script_tag = gst_buffer_join (script_tag, tmp);
800       tags_written++;
801
802       if (gst_structure_get_int (s, "width", &size)) {
803         GST_DEBUG_OBJECT (mux, "putting width %d in the metadata", size);
804
805         tmp = gst_flv_mux_create_number_script_value ("width", size);
806         script_tag = gst_buffer_join (script_tag, tmp);
807         tags_written++;
808       }
809
810       if (gst_structure_get_int (s, "height", &size)) {
811         GST_DEBUG_OBJECT (mux, "putting height %d in the metadata", size);
812
813         tmp = gst_flv_mux_create_number_script_value ("height", size);
814         script_tag = gst_buffer_join (script_tag, tmp);
815         tags_written++;
816       }
817
818       if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &num, &den)) {
819         gdouble d;
820
821         d = num;
822         GST_DEBUG_OBJECT (mux, "putting AspectRatioX %f in the metadata", d);
823
824         tmp = gst_flv_mux_create_number_script_value ("AspectRatioX", d);
825         script_tag = gst_buffer_join (script_tag, tmp);
826         tags_written++;
827
828         d = den;
829         GST_DEBUG_OBJECT (mux, "putting AspectRatioY %f in the metadata", d);
830
831         tmp = gst_flv_mux_create_number_script_value ("AspectRatioY", d);
832         script_tag = gst_buffer_join (script_tag, tmp);
833         tags_written++;
834       }
835
836       if (gst_structure_get_fraction (s, "framerate", &num, &den)) {
837         gdouble d;
838
839         gst_util_fraction_to_double (num, den, &d);
840         GST_DEBUG_OBJECT (mux, "putting framerate %f in the metadata", d);
841
842         tmp = gst_flv_mux_create_number_script_value ("framerate", d);
843         script_tag = gst_buffer_join (script_tag, tmp);
844         tags_written++;
845       }
846     }
847   }
848
849   if (mux->have_audio) {
850     GstPad *audio_pad = NULL;
851     GstFlvPad *cpad;
852     GSList *l = mux->collect->data;
853
854     for (; l; l = l->next) {
855       cpad = l->data;
856       if (cpad && !cpad->video) {
857         audio_pad = cpad->collect.pad;
858         break;
859       }
860     }
861
862     if (audio_pad) {
863       GST_DEBUG_OBJECT (mux, "putting audiocodecid %d in the metadata",
864           cpad->audio_codec);
865
866       tmp = gst_flv_mux_create_number_script_value ("audiocodecid",
867           cpad->audio_codec);
868       script_tag = gst_buffer_join (script_tag, tmp);
869       tags_written++;
870     }
871   }
872
873   {
874     const gchar *s = "GStreamer FLV muxer";
875
876     tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s));
877     data = GST_BUFFER_DATA (tmp);
878     data[0] = 0;                /* 15 bytes name */
879     data[1] = 15;
880     memcpy (&data[2], "metadatacreator", 15);
881     data[17] = 2;               /* string */
882     data[18] = (strlen (s) >> 8) & 0xff;
883     data[19] = (strlen (s)) & 0xff;
884     memcpy (&data[20], s, strlen (s));
885     script_tag = gst_buffer_join (script_tag, tmp);
886
887     tags_written++;
888   }
889
890   {
891     GTimeVal tv = { 0, };
892     time_t secs;
893     struct tm *tm;
894     gchar *s;
895     static const gchar *weekdays[] = {
896       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
897     };
898     static const gchar *months[] = {
899       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
900       "Aug", "Sep", "Oct", "Nov", "Dec"
901     };
902
903     g_get_current_time (&tv);
904     secs = tv.tv_sec;
905     tm = gmtime (&secs);
906
907     s = g_strdup_printf ("%s %s %d %d:%d:%d %d", weekdays[tm->tm_wday],
908         months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
909         tm->tm_year + 1900);
910
911     tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s));
912     data = GST_BUFFER_DATA (tmp);
913     data[0] = 0;                /* 12 bytes name */
914     data[1] = 12;
915     memcpy (&data[2], "creationdate", 12);
916     data[14] = 2;               /* string */
917     data[15] = (strlen (s) >> 8) & 0xff;
918     data[16] = (strlen (s)) & 0xff;
919     memcpy (&data[17], s, strlen (s));
920     script_tag = gst_buffer_join (script_tag, tmp);
921
922     g_free (s);
923     tags_written++;
924   }
925
926   tmp = gst_buffer_new_and_alloc (2 + 0 + 1);
927   data = GST_BUFFER_DATA (tmp);
928   data[0] = 0;                  /* 0 byte size */
929   data[1] = 0;
930   data[2] = 9;                  /* end marker */
931   script_tag = gst_buffer_join (script_tag, tmp);
932   tags_written++;
933
934   tmp = gst_buffer_new_and_alloc (4);
935   data = GST_BUFFER_DATA (tmp);
936   GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
937   script_tag = gst_buffer_join (script_tag, tmp);
938
939   data = GST_BUFFER_DATA (script_tag);
940   data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff;
941   data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff;
942   data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff;
943
944   GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written);
945
946   return script_tag;
947 }
948
949 static GstBuffer *
950 gst_flv_mux_buffer_to_tag_internal (GstFlvMux * mux, GstBuffer * buffer,
951     GstFlvPad * cpad, gboolean is_codec_data)
952 {
953   GstBuffer *tag;
954   guint8 *data;
955   guint size;
956   guint32 timestamp =
957       (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
958       GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
959
960   size = 11;
961   if (cpad->video) {
962     size += 1;
963     if (cpad->video_codec == 7)
964       size += 4 + GST_BUFFER_SIZE (buffer);
965     else
966       size += GST_BUFFER_SIZE (buffer);
967   } else {
968     size += 1;
969     if (cpad->audio_codec == 10)
970       size += 1 + GST_BUFFER_SIZE (buffer);
971     else
972       size += GST_BUFFER_SIZE (buffer);
973   }
974   size += 4;
975
976   tag = gst_buffer_new_and_alloc (size);
977   GST_BUFFER_TIMESTAMP (tag) = timestamp * GST_MSECOND;
978   data = GST_BUFFER_DATA (tag);
979   memset (data, 0, size);
980
981   data[0] = (cpad->video) ? 9 : 8;
982
983   data[1] = ((size - 11 - 4) >> 16) & 0xff;
984   data[2] = ((size - 11 - 4) >> 8) & 0xff;
985   data[3] = ((size - 11 - 4) >> 0) & 0xff;
986
987   /* wrap the timestamp every G_MAXINT32 miliseconds */
988   timestamp &= 0x7fffffff;
989   data[4] = (timestamp >> 16) & 0xff;
990   data[5] = (timestamp >> 8) & 0xff;
991   data[6] = (timestamp >> 0) & 0xff;
992   data[7] = (timestamp >> 24) & 0xff;
993
994   data[8] = data[9] = data[10] = 0;
995
996   if (cpad->video) {
997     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
998       data[11] |= 2 << 4;
999     else
1000       data[11] |= 1 << 4;
1001
1002     data[11] |= cpad->video_codec & 0x0f;
1003
1004     if (cpad->video_codec == 7) {
1005       data[12] = is_codec_data ? 0 : 1;
1006
1007       /* FIXME: what to do about composition time */
1008       data[13] = data[14] = data[15] = 0;
1009
1010       memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer),
1011           GST_BUFFER_SIZE (buffer));
1012     } else {
1013       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
1014           GST_BUFFER_SIZE (buffer));
1015     }
1016   } else {
1017     data[11] |= (cpad->audio_codec << 4) & 0xf0;
1018     data[11] |= (cpad->rate << 2) & 0x0c;
1019     data[11] |= (cpad->width << 1) & 0x02;
1020     data[11] |= (cpad->channels << 0) & 0x01;
1021
1022     if (cpad->audio_codec == 10) {
1023       data[12] = is_codec_data ? 0 : 1;
1024
1025       memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer),
1026           GST_BUFFER_SIZE (buffer));
1027     } else {
1028       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
1029           GST_BUFFER_SIZE (buffer));
1030     }
1031   }
1032
1033   GST_WRITE_UINT32_BE (data + size - 4, size - 4);
1034
1035   gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS);
1036   /* mark the buffer if it's an audio buffer and there's also video being muxed
1037    * or it's a video interframe */
1038   if ((mux->have_video && !cpad->video) ||
1039       GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
1040     GST_BUFFER_FLAG_SET (tag, GST_BUFFER_FLAG_DELTA_UNIT);
1041
1042   GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) =
1043       GST_BUFFER_OFFSET_NONE;
1044
1045   return tag;
1046 }
1047
1048 static inline GstBuffer *
1049 gst_flv_mux_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1050     GstFlvPad * cpad)
1051 {
1052   return gst_flv_mux_buffer_to_tag_internal (mux, buffer, cpad, FALSE);
1053 }
1054
1055 static inline GstBuffer *
1056 gst_flv_mux_codec_data_buffer_to_tag (GstFlvMux * mux, GstBuffer * buffer,
1057     GstFlvPad * cpad)
1058 {
1059   return gst_flv_mux_buffer_to_tag_internal (mux, buffer, cpad, TRUE);
1060 }
1061
1062 static void
1063 gst_flv_mux_put_buffer_in_streamheader (GValue * streamheader,
1064     GstBuffer * buffer)
1065 {
1066   GValue value = { 0 };
1067   GstBuffer *buf;
1068
1069   g_value_init (&value, GST_TYPE_BUFFER);
1070   buf = gst_buffer_copy (buffer);
1071   gst_value_set_buffer (&value, buf);
1072   gst_buffer_unref (buf);
1073   gst_value_array_append_value (streamheader, &value);
1074   g_value_unset (&value);
1075 }
1076
1077 static GstFlowReturn
1078 gst_flv_mux_write_header (GstFlvMux * mux)
1079 {
1080   GstBuffer *header, *metadata;
1081   GstBuffer *video_codec_data, *audio_codec_data;
1082   GstCaps *caps;
1083   GstStructure *structure;
1084   GValue streamheader = { 0 };
1085   GSList *l;
1086   GstFlowReturn ret;
1087
1088   header = gst_flv_mux_create_header (mux);
1089   metadata = gst_flv_mux_create_metadata (mux);
1090   video_codec_data = NULL;
1091   audio_codec_data = NULL;
1092
1093   for (l = mux->collect->data; l != NULL; l = l->next) {
1094     GstFlvPad *cpad = l->data;
1095
1096     /* Get H.264 and AAC codec data, if present */
1097     if (cpad && cpad->video && cpad->video_codec == 7) {
1098       if (cpad->video_codec_data == NULL)
1099         GST_WARNING_OBJECT (mux, "Codec data for video stream not found, "
1100             "output might not be playable");
1101       else
1102         video_codec_data =
1103             gst_flv_mux_codec_data_buffer_to_tag (mux, cpad->video_codec_data,
1104             cpad);
1105     } else if (cpad && !cpad->video && cpad->audio_codec == 10) {
1106       if (cpad->audio_codec_data == NULL)
1107         GST_WARNING_OBJECT (mux, "Codec data for audio stream not found, "
1108             "output might not be playable");
1109       else
1110         audio_codec_data =
1111             gst_flv_mux_codec_data_buffer_to_tag (mux, cpad->audio_codec_data,
1112             cpad);
1113     }
1114   }
1115
1116   /* mark buffers that will go in the streamheader */
1117   GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS);
1118   GST_BUFFER_FLAG_SET (metadata, GST_BUFFER_FLAG_IN_CAPS);
1119   if (video_codec_data != NULL) {
1120     GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_IN_CAPS);
1121     /* mark as a delta unit, so downstream will not try to synchronize on that
1122      * buffer - to actually start playback you need a real video keyframe */
1123     GST_BUFFER_FLAG_SET (video_codec_data, GST_BUFFER_FLAG_DELTA_UNIT);
1124   }
1125   if (audio_codec_data != NULL) {
1126     GST_BUFFER_FLAG_SET (audio_codec_data, GST_BUFFER_FLAG_IN_CAPS);
1127   }
1128
1129   /* put buffers in streamheader */
1130   g_value_init (&streamheader, GST_TYPE_ARRAY);
1131   gst_flv_mux_put_buffer_in_streamheader (&streamheader, header);
1132   gst_flv_mux_put_buffer_in_streamheader (&streamheader, metadata);
1133   if (video_codec_data != NULL)
1134     gst_flv_mux_put_buffer_in_streamheader (&streamheader, video_codec_data);
1135   if (audio_codec_data != NULL)
1136     gst_flv_mux_put_buffer_in_streamheader (&streamheader, audio_codec_data);
1137
1138   /* create the caps and put the streamheader in them */
1139   caps = gst_caps_new_simple ("video/x-flv", NULL);
1140   caps = gst_caps_make_writable (caps);
1141   structure = gst_caps_get_structure (caps, 0);
1142   gst_structure_set_value (structure, "streamheader", &streamheader);
1143   g_value_unset (&streamheader);
1144
1145   if (GST_PAD_CAPS (mux->srcpad) == NULL)
1146     gst_pad_set_caps (mux->srcpad, caps);
1147
1148   gst_caps_unref (caps);
1149
1150   /* push the header buffer, the metadata and the codec info, if any */
1151   ret = gst_flv_mux_push (mux, header);
1152   if (ret != GST_FLOW_OK)
1153     return ret;
1154   ret = gst_flv_mux_push (mux, metadata);
1155   if (ret != GST_FLOW_OK)
1156     return ret;
1157   if (video_codec_data != NULL) {
1158     ret = gst_flv_mux_push (mux, video_codec_data);
1159     if (ret != GST_FLOW_OK)
1160       return ret;
1161   }
1162   if (audio_codec_data != NULL) {
1163     ret = gst_flv_mux_push (mux, audio_codec_data);
1164     if (ret != GST_FLOW_OK)
1165       return ret;
1166   }
1167   return GST_FLOW_OK;
1168 }
1169
1170 static void
1171 gst_flv_mux_update_index (GstFlvMux * mux, GstBuffer * buffer, GstFlvPad * cpad)
1172 {
1173   /*
1174    * Add the tag byte offset and to the index if it's a valid seek point, which
1175    * means it's either a video keyframe or if there is no video pad (in that
1176    * case every FLV tag is a valid seek point)
1177    */
1178   if (mux->have_video &&
1179       (!cpad->video ||
1180           GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)))
1181     return;
1182
1183   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
1184     GstFlvMuxIndexEntry *entry = g_slice_new (GstFlvMuxIndexEntry);
1185     entry->position = mux->byte_count;
1186     entry->time =
1187         gst_guint64_to_gdouble (GST_BUFFER_TIMESTAMP (buffer)) / GST_SECOND;
1188     mux->index = g_list_prepend (mux->index, entry);
1189   }
1190 }
1191
1192 static GstFlowReturn
1193 gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
1194 {
1195   GstBuffer *tag;
1196   GstBuffer *buffer =
1197       gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad);
1198   GstFlowReturn ret;
1199
1200   if (!mux->streamable)
1201     gst_flv_mux_update_index (mux, buffer, cpad);
1202
1203   tag = gst_flv_mux_buffer_to_tag (mux, buffer, cpad);
1204
1205   gst_buffer_unref (buffer);
1206
1207   ret = gst_flv_mux_push (mux, tag);
1208
1209   if (ret == GST_FLOW_OK && GST_BUFFER_TIMESTAMP_IS_VALID (tag))
1210     cpad->last_timestamp = GST_BUFFER_TIMESTAMP (tag);
1211
1212   return ret;
1213 }
1214
1215 static guint64
1216 gst_flv_mux_determine_duration (GstFlvMux * mux)
1217 {
1218   GSList *l;
1219   GstClockTime duration = GST_CLOCK_TIME_NONE;
1220
1221   GST_DEBUG_OBJECT (mux, "trying to determine the duration "
1222       "from pad timestamps");
1223
1224   for (l = mux->collect->data; l != NULL; l = l->next) {
1225     GstFlvPad *cpad = l->data;
1226
1227     if (cpad && (cpad->last_timestamp != GST_CLOCK_TIME_NONE)) {
1228       if (duration == GST_CLOCK_TIME_NONE)
1229         duration = cpad->last_timestamp;
1230       else
1231         duration = MAX (duration, cpad->last_timestamp);
1232     }
1233   }
1234
1235   if (duration == GST_CLOCK_TIME_NONE) {
1236     GST_DEBUG_OBJECT (mux, "not able to determine duration "
1237         "from pad timestamps, assuming 0");
1238     return 0;
1239   }
1240
1241   return duration;
1242 }
1243
1244 static GstFlowReturn
1245 gst_flv_mux_rewrite_header (GstFlvMux * mux)
1246 {
1247   GstBuffer *rewrite, *index, *tmp;
1248   GstEvent *event;
1249   guint8 *data;
1250   gdouble d;
1251   GList *l;
1252   guint32 index_len, allocate_size;
1253   guint32 i, index_skip;
1254
1255   if (mux->streamable)
1256     return GST_FLOW_OK;
1257
1258   /* seek back to the preallocated index space */
1259   event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
1260       13 + 29, GST_CLOCK_TIME_NONE, 13 + 29);
1261   if (!gst_pad_push_event (mux->srcpad, event)) {
1262     GST_WARNING_OBJECT (mux, "Seek to rewrite header failed");
1263     return GST_FLOW_OK;
1264   }
1265
1266   /* if we were not able to determine the duration before, set it now */
1267   if (mux->duration == GST_CLOCK_TIME_NONE)
1268     mux->duration = gst_flv_mux_determine_duration (mux);
1269
1270   /* rewrite the duration tag */
1271   d = gst_guint64_to_gdouble (mux->duration);
1272   d /= (gdouble) GST_SECOND;
1273
1274   GST_DEBUG_OBJECT (mux, "determined the final duration to be %f", d);
1275
1276   rewrite = gst_flv_mux_create_number_script_value ("duration", d);
1277
1278   /* rewrite the filesize tag */
1279   d = gst_guint64_to_gdouble (mux->byte_count);
1280
1281   GST_DEBUG_OBJECT (mux, "putting total filesize %f in the metadata", d);
1282
1283   tmp = gst_flv_mux_create_number_script_value ("filesize", d);
1284   rewrite = gst_buffer_join (rewrite, tmp);
1285
1286   if (!mux->index) {
1287     /* no index, so push buffer and return */
1288     return gst_flv_mux_push (mux, rewrite);
1289   }
1290
1291   /* rewrite the index */
1292   mux->index = g_list_reverse (mux->index);
1293   index_len = g_list_length (mux->index);
1294
1295   /* We write at most MAX_INDEX_ENTRIES elements */
1296   if (index_len > MAX_INDEX_ENTRIES) {
1297     index_skip = 1 + index_len / MAX_INDEX_ENTRIES;
1298     index_len = (index_len + index_skip - 1) / index_skip;
1299   } else {
1300     index_skip = 1;
1301   }
1302
1303   GST_DEBUG_OBJECT (mux, "Index length is %d", index_len);
1304   /* see size calculation in gst_flv_mux_preallocate_index */
1305   allocate_size = 11 + 8 + 22 + 10 + index_len * 18;
1306   GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size);
1307   index = gst_buffer_new_and_alloc (allocate_size);
1308   data = GST_BUFFER_DATA (index);
1309
1310   GST_WRITE_UINT16_BE (data, 9);        /* the 'keyframes' key */
1311   memcpy (data + 2, "keyframes", 9);
1312   GST_WRITE_UINT8 (data + 11, 8);       /* nested ECMA array */
1313   GST_WRITE_UINT32_BE (data + 12, 2);   /* two elements */
1314   GST_WRITE_UINT16_BE (data + 16, 5);   /* first string key: 'times' */
1315   memcpy (data + 18, "times", 5);
1316   GST_WRITE_UINT8 (data + 23, 10);      /* strict array */
1317   GST_WRITE_UINT32_BE (data + 24, index_len);
1318   data += 28;
1319
1320   /* the keyframes' times */
1321   for (i = 0, l = mux->index; l; l = l->next, i++) {
1322     GstFlvMuxIndexEntry *entry = l->data;
1323
1324     if (i % index_skip != 0)
1325       continue;
1326     GST_WRITE_UINT8 (data, 0);  /* numeric (aka double) */
1327     GST_WRITE_DOUBLE_BE (data + 1, entry->time);
1328     data += 9;
1329   }
1330
1331   GST_WRITE_UINT16_BE (data, 13);       /* second string key: 'filepositions' */
1332   memcpy (data + 2, "filepositions", 13);
1333   GST_WRITE_UINT8 (data + 15, 10);      /* strict array */
1334   GST_WRITE_UINT32_BE (data + 16, index_len);
1335   data += 20;
1336
1337   /* the keyframes' file positions */
1338   for (i = 0, l = mux->index; l; l = l->next, i++) {
1339     GstFlvMuxIndexEntry *entry = l->data;
1340
1341     if (i % index_skip != 0)
1342       continue;
1343     GST_WRITE_UINT8 (data, 0);
1344     GST_WRITE_DOUBLE_BE (data + 1, entry->position);
1345     data += 9;
1346   }
1347
1348   GST_WRITE_UINT24_BE (data, 9);        /* finish the ECMA array */
1349
1350   /* If there is space left in the prefilled area, reinsert the filler.
1351      There is at least 18  bytes free, so it will always fit. */
1352   if (index_len < MAX_INDEX_ENTRIES) {
1353     GstBuffer *tmp;
1354     guint8 *data;
1355     guint32 remaining_filler_size;
1356
1357     tmp = gst_buffer_new_and_alloc (14);
1358     data = GST_BUFFER_DATA (tmp);
1359     GST_WRITE_UINT16_BE (data, 9);
1360     memcpy (data + 2, "gstfiller", 9);
1361     GST_WRITE_UINT8 (data + 11, 2);     /* string */
1362
1363     /* There is 18 bytes per remaining index entry minus what is used for
1364      * the'gstfiller' key. The rest is already filled with spaces, so just need
1365      * to update length. */
1366     remaining_filler_size = (MAX_INDEX_ENTRIES - index_len) * 18 - 14;
1367     GST_DEBUG_OBJECT (mux, "Remaining filler size is %d bytes",
1368         remaining_filler_size);
1369     GST_WRITE_UINT16_BE (data + 12, remaining_filler_size);
1370     index = gst_buffer_join (index, tmp);
1371   }
1372
1373   rewrite = gst_buffer_join (rewrite, index);
1374
1375   gst_buffer_set_caps (rewrite, GST_PAD_CAPS (mux->srcpad));
1376   return gst_flv_mux_push (mux, rewrite);
1377 }
1378
1379 static GstFlowReturn
1380 gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
1381 {
1382   GstFlvMux *mux = GST_FLV_MUX (user_data);
1383   GstFlvPad *best;
1384   GstClockTime best_time;
1385   GstFlowReturn ret;
1386   GSList *sl;
1387   gboolean eos = TRUE;
1388
1389   if (mux->state == GST_FLV_MUX_STATE_HEADER) {
1390     if (mux->collect->data == NULL) {
1391       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
1392           ("No input streams configured"));
1393       return GST_FLOW_ERROR;
1394     }
1395
1396     if (gst_pad_push_event (mux->srcpad,
1397             gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)))
1398       ret = gst_flv_mux_write_header (mux);
1399     else
1400       ret = GST_FLOW_ERROR;
1401
1402     if (ret != GST_FLOW_OK)
1403       return ret;
1404     mux->state = GST_FLV_MUX_STATE_DATA;
1405   }
1406
1407   if (mux->new_tags) {
1408     GstBuffer *buf = gst_flv_mux_create_metadata (mux);
1409     gst_flv_mux_push (mux, buf);
1410     mux->new_tags = FALSE;
1411   }
1412
1413
1414   best = NULL;
1415   best_time = GST_CLOCK_TIME_NONE;
1416   for (sl = mux->collect->data; sl; sl = sl->next) {
1417     GstFlvPad *cpad = sl->data;
1418     GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad);
1419     GstClockTime time;
1420
1421     if (!buffer)
1422       continue;
1423
1424     eos = FALSE;
1425
1426     time = GST_BUFFER_TIMESTAMP (buffer);
1427     gst_buffer_unref (buffer);
1428
1429     /* Use buffers without valid timestamp first */
1430     if (!GST_CLOCK_TIME_IS_VALID (time)) {
1431       GST_WARNING_OBJECT (pads, "Buffer without valid timestamp");
1432
1433       best_time = cpad->last_timestamp;
1434       best = cpad;
1435       break;
1436     }
1437
1438
1439     if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time)
1440             && time < best_time)) {
1441       best = cpad;
1442       best_time = time;
1443     }
1444   }
1445
1446   /* The FLV timestamp is an int32 field. For non-live streams error out if a
1447      bigger timestamp is seen, for live the timestamp will get wrapped in
1448      gst_flv_mux_buffer_to_tag */
1449   if (!mux->streamable && GST_CLOCK_TIME_IS_VALID (best_time)
1450       && best_time / GST_MSECOND > G_MAXINT32) {
1451     GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS");
1452     eos = TRUE;
1453   }
1454
1455   if (!eos && best) {
1456     return gst_flv_mux_write_buffer (mux, best);
1457   } else if (eos) {
1458     gst_flv_mux_rewrite_header (mux);
1459     gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
1460     return GST_FLOW_UNEXPECTED;
1461   } else {
1462     return GST_FLOW_OK;
1463   }
1464 }
1465
1466 static void
1467 gst_flv_mux_get_property (GObject * object,
1468     guint prop_id, GValue * value, GParamSpec * pspec)
1469 {
1470   GstFlvMux *mux = GST_FLV_MUX (object);
1471
1472   switch (prop_id) {
1473     case PROP_STREAMABLE:
1474       g_value_set_boolean (value, mux->streamable);
1475       break;
1476     default:
1477       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1478       break;
1479   }
1480 }
1481
1482 static void
1483 gst_flv_mux_set_property (GObject * object,
1484     guint prop_id, const GValue * value, GParamSpec * pspec)
1485 {
1486   GstFlvMux *mux = GST_FLV_MUX (object);
1487
1488   switch (prop_id) {
1489     case PROP_STREAMABLE:
1490       mux->streamable = g_value_get_boolean (value);
1491       if (mux->streamable)
1492         gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
1493             GST_TAG_MERGE_REPLACE);
1494       else
1495         gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux),
1496             GST_TAG_MERGE_KEEP);
1497       break;
1498     default:
1499       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1500       break;
1501   }
1502 }
1503
1504 static GstStateChangeReturn
1505 gst_flv_mux_change_state (GstElement * element, GstStateChange transition)
1506 {
1507   GstStateChangeReturn ret;
1508   GstFlvMux *mux = GST_FLV_MUX (element);
1509
1510   switch (transition) {
1511     case GST_STATE_CHANGE_NULL_TO_READY:
1512       break;
1513     case GST_STATE_CHANGE_READY_TO_PAUSED:
1514       gst_collect_pads_start (mux->collect);
1515       break;
1516     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1517       break;
1518     case GST_STATE_CHANGE_PAUSED_TO_READY:
1519       gst_collect_pads_stop (mux->collect);
1520       break;
1521     default:
1522       break;
1523   }
1524
1525   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1526
1527   switch (transition) {
1528     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1529       break;
1530     case GST_STATE_CHANGE_PAUSED_TO_READY:
1531       gst_flv_mux_reset (GST_ELEMENT (mux));
1532       break;
1533     case GST_STATE_CHANGE_READY_TO_NULL:
1534       break;
1535     default:
1536       break;
1537   }
1538
1539   return ret;
1540 }