[MOVED FROM BAD 54/57] flv: Always write at least the minimal tags and write the...
[platform/upstream/gstreamer.git] / gst / flv / gstflvmux.c
1 /* GStreamer
2  *
3  * Copyright (c) 2008 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 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("video/x-flv")
50     );
51
52 static GstStaticPadTemplate videosink_templ = GST_STATIC_PAD_TEMPLATE ("video",
53     GST_PAD_SINK,
54     GST_PAD_REQUEST,
55     GST_STATIC_CAPS ("video/x-flash-video; "
56         "video/x-flash-screen; "
57         "video/x-vp6-flash; " "video/x-vp6-alpha; " "video/x-h264;")
58     );
59
60 static GstStaticPadTemplate audiosink_templ = GST_STATIC_PAD_TEMPLATE ("audio",
61     GST_PAD_SINK,
62     GST_PAD_REQUEST,
63     GST_STATIC_CAPS
64     ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
65         "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }; "
66         "audio/mpeg, mpegversion = (int) 4; "
67         "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
68         "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; "
69         "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; "
70         "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
71         "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
72         "audio/x-speex, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 };")
73     );
74
75 #define _do_init(type)                                                          \
76   G_STMT_START{                                                                 \
77     static const GInterfaceInfo tag_setter_info = {                             \
78       NULL,                                                                     \
79       NULL,                                                                     \
80       NULL                                                                      \
81     };                                                                          \
82     g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,                     \
83                                  &tag_setter_info);                             \
84   }G_STMT_END
85
86 GST_BOILERPLATE_FULL (GstFlvMux, gst_flv_mux, GstElement, GST_TYPE_ELEMENT,
87     _do_init);
88
89 static void gst_flv_mux_finalize (GObject * object);
90 static GstFlowReturn
91 gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data);
92
93 static gboolean gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event);
94 static GstPad *gst_flv_mux_request_new_pad (GstElement * element,
95     GstPadTemplate * templ, const gchar * name);
96 static void gst_flv_mux_release_pad (GstElement * element, GstPad * pad);
97
98 static GstStateChangeReturn
99 gst_flv_mux_change_state (GstElement * element, GstStateChange transition);
100
101 static void gst_flv_mux_reset (GstElement * element);
102
103 static void
104 gst_flv_mux_base_init (gpointer g_class)
105 {
106   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
107
108   gst_element_class_add_pad_template (element_class,
109       gst_static_pad_template_get (&videosink_templ));
110   gst_element_class_add_pad_template (element_class,
111       gst_static_pad_template_get (&audiosink_templ));
112   gst_element_class_add_pad_template (element_class,
113       gst_static_pad_template_get (&src_templ));
114   gst_element_class_set_details_simple (element_class, "FLV muxer",
115       "Codec/Muxer",
116       "Muxes video/audio streams into a FLV stream",
117       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
118
119   GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
120 }
121
122 static void
123 gst_flv_mux_class_init (GstFlvMuxClass * klass)
124 {
125   GObjectClass *gobject_class;
126   GstElementClass *gstelement_class;
127
128   GST_DEBUG_CATEGORY_INIT (flvmux_debug, "flvmux", 0, "FLV muxer");
129
130   gobject_class = (GObjectClass *) klass;
131   gstelement_class = (GstElementClass *) klass;
132
133   gobject_class->finalize = gst_flv_mux_finalize;
134
135   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_flv_mux_change_state);
136   gstelement_class->request_new_pad =
137       GST_DEBUG_FUNCPTR (gst_flv_mux_request_new_pad);
138   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_flv_mux_release_pad);
139 }
140
141 static void
142 gst_flv_mux_init (GstFlvMux * mux, GstFlvMuxClass * g_class)
143 {
144   mux->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
145   gst_pad_set_event_function (mux->srcpad, gst_flv_mux_handle_src_event);
146   gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
147
148   mux->collect = gst_collect_pads_new ();
149   gst_collect_pads_set_function (mux->collect,
150       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_flv_mux_collected), mux);
151
152   gst_flv_mux_reset (GST_ELEMENT (mux));
153 }
154
155 static void
156 gst_flv_mux_finalize (GObject * object)
157 {
158   GstFlvMux *mux = GST_FLV_MUX (object);
159
160   gst_object_unref (mux->collect);
161
162   G_OBJECT_CLASS (parent_class)->finalize (object);
163 }
164
165 static void
166 gst_flv_mux_reset (GstElement * element)
167 {
168   GstFlvMux *mux = GST_FLV_MUX (element);
169   GSList *sl;
170
171   while ((sl = mux->collect->data) != NULL) {
172     GstFlvPad *cpad = (GstFlvPad *) sl->data;
173
174     if (cpad->audio_codec_data)
175       gst_buffer_unref (cpad->audio_codec_data);
176     if (cpad->video_codec_data)
177       gst_buffer_unref (cpad->video_codec_data);
178
179     gst_collect_pads_remove_pad (mux->collect, cpad->collect.pad);
180   }
181
182   if (mux->tags)
183     gst_tag_list_free (mux->tags);
184   mux->tags = NULL;
185
186   mux->state = GST_FLV_MUX_STATE_HEADER;
187 }
188
189 static gboolean
190 gst_flv_mux_handle_src_event (GstPad * pad, GstEvent * event)
191 {
192   GstEventType type;
193
194   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
195
196   switch (type) {
197     case GST_EVENT_SEEK:
198       /* disable seeking for now */
199       return FALSE;
200     default:
201       break;
202   }
203
204   return gst_pad_event_default (pad, event);
205 }
206
207 static gboolean
208 gst_flv_mux_handle_sink_event (GstPad * pad, GstEvent * event)
209 {
210   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
211   gboolean ret = TRUE;
212
213   switch (GST_EVENT_TYPE (event)) {
214     case GST_EVENT_TAG:{
215       GstTagList *tags;
216
217       if (!mux->tags)
218         mux->tags = gst_tag_list_new ();
219
220       gst_event_parse_tag (event, &tags);
221       if (tags) {
222         gst_tag_list_insert (mux->tags, tags,
223             gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
224       }
225
226       break;
227     }
228     case GST_EVENT_NEWSEGMENT:
229       /* We don't support NEWSEGMENT events */
230       ret = FALSE;
231       gst_event_unref (event);
232       break;
233     default:
234       break;
235   }
236
237   /* now GstCollectPads can take care of the rest, e.g. EOS */
238   if (ret)
239     ret = mux->collect_event (pad, event);
240   gst_object_unref (mux);
241
242   return ret;
243 }
244
245 static gboolean
246 gst_flv_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
247 {
248   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
249   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
250   gboolean ret = TRUE;
251   GstStructure *s;
252
253   s = gst_caps_get_structure (caps, 0);
254
255   if (strcmp (gst_structure_get_name (s), "video/x-flash-video") == 0) {
256     cpad->video_codec = 2;
257   } else if (strcmp (gst_structure_get_name (s), "video/x-flash-screen") == 0) {
258     cpad->video_codec = 3;
259   } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-flash") == 0) {
260     cpad->video_codec = 4;
261   } else if (strcmp (gst_structure_get_name (s), "video/x-vp6-alpha") == 0) {
262     cpad->video_codec = 5;
263   } else if (strcmp (gst_structure_get_name (s), "video/x-h264") == 0) {
264     cpad->video_codec = 7;
265   } else {
266     ret = FALSE;
267   }
268
269   if (ret && gst_structure_has_field (s, "codec_data")) {
270     const GValue *val = gst_structure_get_value (s, "codec_data");
271
272     if (val) {
273       cpad->video_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
274       cpad->sent_codec_data = FALSE;
275     } else {
276       cpad->sent_codec_data = TRUE;
277     }
278   } else {
279     cpad->sent_codec_data = TRUE;
280   }
281
282   gst_object_unref (mux);
283
284   return ret;
285 }
286
287 static gboolean
288 gst_flv_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
289 {
290   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
291   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
292   gboolean ret = TRUE;
293   GstStructure *s;
294
295   s = gst_caps_get_structure (caps, 0);
296
297   if (strcmp (gst_structure_get_name (s), "audio/x-adpcm") == 0) {
298     const gchar *layout = gst_structure_get_string (s, "layout");
299     if (layout && strcmp (layout, "swf") == 0) {
300       cpad->audio_codec = 1;
301     } else {
302       ret = FALSE;
303     }
304   } else if (strcmp (gst_structure_get_name (s), "audio/mpeg") == 0) {
305     gint mpegversion;
306
307     if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
308       if (mpegversion == 1) {
309         gint layer;
310
311         if (gst_structure_get_int (s, "layer", &layer) && layer == 3) {
312           gint rate;
313
314           if (gst_structure_get_int (s, "rate", &rate) && rate == 8000)
315             cpad->audio_codec = 14;
316           else
317             cpad->audio_codec = 2;
318         } else {
319           ret = FALSE;
320         }
321       } else if (mpegversion == 4) {
322         cpad->audio_codec = 10;
323       } else {
324         ret = FALSE;
325       }
326     } else {
327       ret = FALSE;
328     }
329   } else if (strcmp (gst_structure_get_name (s), "audio/x-nellymoser") == 0) {
330     gint rate, channels;
331
332     if (gst_structure_get_int (s, "rate", &rate)
333         && gst_structure_get_int (s, "channels", &channels)) {
334       if (channels == 1 && rate == 16000)
335         cpad->audio_codec = 4;
336       else if (channels == 1 && rate == 8000)
337         cpad->audio_codec = 5;
338     } else {
339       cpad->audio_codec = 6;
340     }
341   } else if (strcmp (gst_structure_get_name (s), "audio/x-raw-int") == 0) {
342     gint endianness;
343
344     if (gst_structure_get_int (s, "endianness", &endianness)
345         && endianness == G_LITTLE_ENDIAN)
346       cpad->audio_codec = 3;
347     else
348       ret = FALSE;
349   } else if (strcmp (gst_structure_get_name (s), "audio/x-alaw") == 0) {
350     cpad->audio_codec = 7;
351   } else if (strcmp (gst_structure_get_name (s), "audio/x-mulaw") == 0) {
352     cpad->audio_codec = 8;
353   } else if (strcmp (gst_structure_get_name (s), "audio/x-speex") == 0) {
354     cpad->audio_codec = 11;
355   } else {
356     ret = FALSE;
357   }
358
359   if (ret) {
360     gint rate, channels, width;
361
362     if (gst_structure_get_int (s, "rate", &rate)) {
363       if (cpad->audio_codec == 10)
364         cpad->rate = 3;
365       else if (rate == 5512)
366         cpad->rate = 0;
367       else if (rate == 11025)
368         cpad->rate = 1;
369       else if (rate == 22050)
370         cpad->rate = 2;
371       else if (rate == 44100)
372         cpad->rate = 3;
373       else if (rate == 8000 && (cpad->audio_codec == 5
374               || cpad->audio_codec == 14))
375         cpad->rate = 0;
376       else if (rate == 16000 && cpad->audio_codec == 4)
377         cpad->rate = 0;
378       else
379         ret = FALSE;
380     } else if (cpad->audio_codec == 10) {
381       cpad->rate = 3;
382     } else {
383       ret = FALSE;
384     }
385
386     if (gst_structure_get_int (s, "channels", &channels)) {
387       if (cpad->audio_codec == 4 || cpad->audio_codec == 5
388           || cpad->audio_codec == 6)
389         cpad->channels = 0;
390       else if (cpad->audio_codec == 10)
391         cpad->channels = 1;
392       else if (channels == 1)
393         cpad->channels = 0;
394       else if (channels == 2)
395         cpad->channels = 1;
396       else
397         ret = FALSE;
398     } else if (cpad->audio_codec == 4 || cpad->audio_codec == 5
399         || cpad->audio_codec == 6) {
400       cpad->channels = 0;
401     } else if (cpad->audio_codec == 10) {
402       cpad->channels = 1;
403     } else {
404       ret = FALSE;
405     }
406
407     if (gst_structure_get_int (s, "width", &width)) {
408       if (cpad->audio_codec != 3)
409         cpad->width = 1;
410       else if (width == 8)
411         cpad->width = 0;
412       else if (width == 16)
413         cpad->width = 1;
414       else
415         ret = FALSE;
416     } else if (cpad->audio_codec != 3) {
417       cpad->width = 1;
418     } else {
419       ret = FALSE;
420     }
421   }
422
423   if (ret && gst_structure_has_field (s, "codec_data")) {
424     const GValue *val = gst_structure_get_value (s, "codec_data");
425
426     if (val) {
427       cpad->audio_codec_data = gst_buffer_ref (gst_value_get_buffer (val));
428       cpad->sent_codec_data = FALSE;
429     } else {
430       cpad->sent_codec_data = TRUE;
431     }
432   } else {
433     cpad->sent_codec_data = TRUE;
434   }
435
436   gst_object_unref (mux);
437
438   return ret;
439 }
440
441 static GstPad *
442 gst_flv_mux_request_new_pad (GstElement * element,
443     GstPadTemplate * templ, const gchar * pad_name)
444 {
445   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
446   GstFlvMux *mux = GST_FLV_MUX (element);
447   GstFlvPad *cpad;
448   GstPad *pad = NULL;
449   const gchar *name = NULL;
450   GstPadSetCapsFunction setcapsfunc = NULL;
451   gboolean video;
452
453   if (mux->state != GST_FLV_MUX_STATE_HEADER) {
454     GST_WARNING_OBJECT (mux, "Can't request pads after writing header");
455     return NULL;
456   }
457
458   if (templ == gst_element_class_get_pad_template (klass, "audio")) {
459     if (mux->have_audio) {
460       GST_WARNING_OBJECT (mux, "Already have an audio pad");
461       return NULL;
462     }
463     mux->have_audio = TRUE;
464     name = "audio";
465     video = FALSE;
466     setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_audio_pad_setcaps);
467   } else if (templ == gst_element_class_get_pad_template (klass, "video")) {
468     if (mux->have_video) {
469       GST_WARNING_OBJECT (mux, "Already have a video pad");
470       return NULL;
471     }
472     mux->have_video = TRUE;
473     name = "video";
474     video = TRUE;
475     setcapsfunc = GST_DEBUG_FUNCPTR (gst_flv_mux_video_pad_setcaps);
476   } else {
477     GST_WARNING_OBJECT (mux, "Invalid template");
478     return NULL;
479   }
480
481   pad = gst_pad_new_from_template (templ, name);
482   cpad = (GstFlvPad *)
483       gst_collect_pads_add_pad (mux->collect, pad, sizeof (GstFlvPad));
484
485   cpad->video = video;
486
487   cpad->audio_codec = G_MAXUINT;
488   cpad->rate = G_MAXUINT;
489   cpad->width = G_MAXUINT;
490   cpad->channels = G_MAXUINT;
491   cpad->audio_codec_data = NULL;
492
493   cpad->video_codec = G_MAXUINT;
494   cpad->video_codec_data = NULL;
495
496   cpad->sent_codec_data = FALSE;
497
498   cpad->last_timestamp = 0;
499
500   /* FIXME: hacked way to override/extend the event function of
501    * GstCollectPads; because it sets its own event function giving the
502    * element no access to events.
503    */
504   mux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (pad);
505   gst_pad_set_event_function (pad,
506       GST_DEBUG_FUNCPTR (gst_flv_mux_handle_sink_event));
507
508   gst_pad_set_setcaps_function (pad, setcapsfunc);
509   gst_pad_set_active (pad, TRUE);
510   gst_element_add_pad (element, pad);
511
512   return pad;
513 }
514
515 static void
516 gst_flv_mux_release_pad (GstElement * element, GstPad * pad)
517 {
518   GstFlvMux *mux = GST_FLV_MUX (GST_PAD_PARENT (pad));
519   GstFlvPad *cpad = (GstFlvPad *) gst_pad_get_element_private (pad);
520
521   if (cpad && cpad->audio_codec_data)
522     gst_buffer_unref (cpad->audio_codec_data);
523   if (cpad && cpad->video_codec_data)
524     gst_buffer_unref (cpad->video_codec_data);
525
526   gst_collect_pads_remove_pad (mux->collect, pad);
527   gst_element_remove_pad (element, pad);
528 }
529
530 static GstFlowReturn
531 gst_flv_mux_write_metadata (GstFlvMux * mux)
532 {
533   GstTagList *merged_tags;
534   const GstTagList *user_tags;
535   GstFlowReturn ret = GST_FLOW_OK;
536   GstBuffer *script_tag, *tmp;
537   guint8 *data;
538   gint i, n_tags, tags_written = 0;
539
540   user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (mux));
541   GST_DEBUG_OBJECT (mux, "upstream tags = %" GST_PTR_FORMAT, mux->tags);
542   GST_DEBUG_OBJECT (mux, "user-set tags = %" GST_PTR_FORMAT, user_tags);
543
544   merged_tags = gst_tag_list_merge (user_tags, mux->tags,
545       gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (mux)));
546
547   GST_DEBUG_OBJECT (mux, "merged tags = %" GST_PTR_FORMAT, merged_tags);
548
549   script_tag = gst_buffer_new_and_alloc (11);
550   data = GST_BUFFER_DATA (script_tag);
551
552   data[0] = 18;
553
554   /* Data size, unknown for now */
555   data[1] = 0;
556   data[2] = 0;
557   data[3] = 0;
558
559   /* Timestamp */
560   data[4] = data[5] = data[6] = data[7] = 0;
561
562   /* Stream ID */
563   data[8] = data[9] = data[10] = 0;
564
565   tmp = gst_buffer_new_and_alloc (13);
566   data = GST_BUFFER_DATA (tmp);
567   data[0] = 2;                  /* string */
568   data[1] = 0;
569   data[2] = 0x0a;               /* length 10 */
570   memcpy (&data[3], "onMetaData", sizeof ("onMetaData"));
571
572   script_tag = gst_buffer_join (script_tag, tmp);
573
574   n_tags =
575       (merged_tags) ? gst_structure_n_fields ((GstStructure *) merged_tags) : 0;
576   tmp = gst_buffer_new_and_alloc (5);
577   data = GST_BUFFER_DATA (tmp);
578   data[0] = 8;                  /* ECMA array */
579   GST_WRITE_UINT32_BE (data + 1, n_tags);
580   script_tag = gst_buffer_join (script_tag, tmp);
581
582   for (i = 0; merged_tags && i < n_tags; i++) {
583     const gchar *tag_name =
584         gst_structure_nth_field_name ((const GstStructure *) merged_tags, i);
585     if (!strcmp (tag_name, GST_TAG_DURATION)) {
586       gdouble d;
587       guint64 dur;
588
589       if (!gst_tag_list_get_uint64 (merged_tags, GST_TAG_DURATION, &dur))
590         continue;
591
592       d = gst_guint64_to_gdouble (dur);
593       d /= (gdouble) GST_SECOND;
594
595       tmp = gst_buffer_new_and_alloc (2 + 8 + 1 + 8);
596       data = GST_BUFFER_DATA (tmp);
597       data[0] = 0;              /* 8 bytes name */
598       data[1] = 8;
599       memcpy (&data[2], "duration", sizeof ("duration"));
600       data[10] = 0;             /* double */
601       GST_WRITE_DOUBLE_BE (data + 11, d);
602       script_tag = gst_buffer_join (script_tag, tmp);
603       tags_written++;
604     } else if (!strcmp (tag_name, GST_TAG_ARTIST) ||
605         !strcmp (tag_name, GST_TAG_TITLE)) {
606       gchar *s;
607       const gchar *t;
608
609       if (!strcmp (tag_name, GST_TAG_ARTIST))
610         t = "creator";
611       else if (!strcmp (tag_name, GST_TAG_TITLE))
612         t = "title";
613
614       if (!gst_tag_list_get_string (merged_tags, tag_name, &s))
615         continue;
616
617       tmp = gst_buffer_new_and_alloc (2 + strlen (t) + 1 + 2 + strlen (s));
618       data = GST_BUFFER_DATA (tmp);
619       data[0] = 0;              /* tag name length */
620       data[1] = strlen (t);
621       memcpy (&data[2], t, strlen (t));
622       data[2 + strlen (t)] = 2; /* string */
623       data[3 + strlen (t)] = (strlen (s) >> 8) & 0xff;
624       data[4 + strlen (t)] = (strlen (s)) & 0xff;
625       memcpy (&data[5 + strlen (t)], s, strlen (s));
626       script_tag = gst_buffer_join (script_tag, tmp);
627
628       g_free (s);
629       tags_written++;
630     }
631   }
632
633   if (mux->have_video) {
634     GstPad *video_pad;
635     GSList *l = mux->collect->data;
636
637     for (; l; l = l->next) {
638       GstFlvPad *cpad = l->data;
639       if (cpad && cpad->video) {
640         video_pad = cpad->collect.pad;
641         break;
642       }
643     }
644
645     if (video_pad && GST_PAD_CAPS (video_pad)) {
646       GstStructure *s = gst_caps_get_structure (GST_PAD_CAPS (video_pad), 0);
647       gint par_x, par_y;
648
649       if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_x, &par_y)) {
650         gdouble d;
651
652         d = par_x;
653         tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8);
654         data = GST_BUFFER_DATA (tmp);
655         data[0] = 0;            /* 12 bytes name */
656         data[1] = 12;
657         memcpy (&data[2], "AspectRatioX", sizeof ("AspectRatioX"));
658         data[14] = 0;           /* double */
659         GST_WRITE_DOUBLE_BE (data + 15, d);
660         script_tag = gst_buffer_join (script_tag, tmp);
661         tags_written++;
662
663         d = par_y;
664         tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 8);
665         data = GST_BUFFER_DATA (tmp);
666         data[0] = 0;            /* 12 bytes name */
667         data[1] = 12;
668         memcpy (&data[2], "AspectRatioY", sizeof ("AspectRatioY"));
669         data[14] = 0;           /* double */
670         GST_WRITE_DOUBLE_BE (data + 15, d);
671         script_tag = gst_buffer_join (script_tag, tmp);
672         tags_written++;
673       }
674     }
675   }
676
677   {
678     const gchar *s = "GStreamer FLV muxer";
679
680     tmp = gst_buffer_new_and_alloc (2 + 15 + 1 + 2 + strlen (s));
681     data = GST_BUFFER_DATA (tmp);
682     data[0] = 0;                /* 15 bytes name */
683     data[1] = 15;
684     memcpy (&data[2], "metadatacreator", sizeof ("metadatacreator"));
685     data[17] = 2;               /* string */
686     data[18] = (strlen (s) >> 8) & 0xff;
687     data[19] = (strlen (s)) & 0xff;
688     memcpy (&data[20], s, strlen (s));
689     script_tag = gst_buffer_join (script_tag, tmp);
690
691     tags_written++;
692   }
693
694   {
695     GTimeVal tv = { 0, };
696     time_t secs;
697     struct tm *tm;
698     gchar *s;
699     static const gchar *weekdays[] = {
700       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
701     };
702     static const gchar *months[] = {
703       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
704       "Aug", "Sep", "Oct", "Nov", "Dec"
705     };
706
707     g_get_current_time (&tv);
708     secs = tv.tv_sec;
709     tm = gmtime (&secs);
710
711     s = g_strdup_printf ("%s %s %d %d:%d:%d %d", weekdays[tm->tm_wday],
712         months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
713         tm->tm_year + 1900);
714
715     tmp = gst_buffer_new_and_alloc (2 + 12 + 1 + 2 + strlen (s));
716     data = GST_BUFFER_DATA (tmp);
717     data[0] = 0;                /* 12 bytes name */
718     data[1] = 12;
719     memcpy (&data[2], "creationdate", sizeof ("creationdate"));
720     data[14] = 2;               /* string */
721     data[15] = (strlen (s) >> 8) & 0xff;
722     data[16] = (strlen (s)) & 0xff;
723     memcpy (&data[17], s, strlen (s));
724     script_tag = gst_buffer_join (script_tag, tmp);
725
726     g_free (s);
727     tags_written++;
728   }
729
730   tmp = gst_buffer_new_and_alloc (2 + 0 + 1);
731   data = GST_BUFFER_DATA (tmp);
732   data[0] = 0;                  /* 0 byte size */
733   data[1] = 0;
734   data[2] = 9;                  /* end marker */
735   script_tag = gst_buffer_join (script_tag, tmp);
736   tags_written++;
737
738
739   tmp = gst_buffer_new_and_alloc (4);
740   data = GST_BUFFER_DATA (tmp);
741   GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
742   script_tag = gst_buffer_join (script_tag, tmp);
743
744   data = GST_BUFFER_DATA (script_tag);
745   data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff;
746   data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff;
747   data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff;
748
749   GST_WRITE_UINT32_BE (data + 11 + 13 + 1, tags_written);
750
751   gst_buffer_set_caps (script_tag, GST_PAD_CAPS (mux->srcpad));
752   ret = gst_pad_push (mux->srcpad, script_tag);
753
754   if (merged_tags)
755     gst_tag_list_free (merged_tags);
756
757   return ret;
758 }
759
760 static GstFlowReturn
761 gst_flv_mux_write_header (GstFlvMux * mux)
762 {
763   GstBuffer *header = gst_buffer_new_and_alloc (9 + 4);
764   guint8 *data = GST_BUFFER_DATA (header);
765   GstFlowReturn ret;
766
767   if (GST_PAD_CAPS (mux->srcpad) == NULL) {
768     GstCaps *caps = gst_caps_new_simple ("video/x-flv", NULL);
769
770     gst_pad_set_caps (mux->srcpad, caps);
771     gst_caps_unref (caps);
772   }
773   gst_buffer_set_caps (header, GST_PAD_CAPS (mux->srcpad));
774
775   data[0] = 'F';
776   data[1] = 'L';
777   data[2] = 'V';
778   data[3] = 0x01;               /* Version */
779
780   data[4] = (mux->have_audio << 2) | mux->have_video;   /* flags */
781   GST_WRITE_UINT32_BE (data + 5, 9);    /* data offset */
782
783   GST_WRITE_UINT32_BE (data + 9, 0);    /* previous tag size */
784
785   ret = gst_pad_push (mux->srcpad, header);
786   if (ret != GST_FLOW_OK)
787     return ret;
788
789   return gst_flv_mux_write_metadata (mux);
790 }
791
792 static GstFlowReturn
793 gst_flv_mux_write_buffer (GstFlvMux * mux, GstFlvPad * cpad)
794 {
795   GstBuffer *tag;
796   guint8 *data;
797   guint size;
798   GstBuffer *buffer =
799       gst_collect_pads_pop (mux->collect, (GstCollectData *) cpad);
800   guint32 timestamp =
801       (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) ? GST_BUFFER_TIMESTAMP (buffer) /
802       GST_MSECOND : cpad->last_timestamp / GST_MSECOND;
803   gboolean second_run = FALSE;
804   GstFlowReturn ret;
805
806 next:
807   size = 11;
808   if (cpad->video) {
809     size += 1;
810     if (cpad->video_codec == 7 && !cpad->sent_codec_data)
811       size += 4 + GST_BUFFER_SIZE (cpad->video_codec_data);
812     else if (cpad->video_codec == 7)
813       size += 4 + GST_BUFFER_SIZE (buffer);
814     else
815       size += GST_BUFFER_SIZE (buffer);
816   } else {
817     size += 1;
818     if (cpad->audio_codec == 10 && !cpad->sent_codec_data)
819       size += 1 + GST_BUFFER_SIZE (cpad->audio_codec_data);
820     else if (cpad->audio_codec == 10)
821       size += 1 + GST_BUFFER_SIZE (buffer);
822     else
823       size += GST_BUFFER_SIZE (buffer);
824   }
825   size += 4;
826
827   tag = gst_buffer_new_and_alloc (size);
828   data = GST_BUFFER_DATA (tag);
829   memset (data, 0, size);
830
831   data[0] = (cpad->video) ? 9 : 8;
832
833   data[1] = ((size - 11 - 4) >> 16) & 0xff;
834   data[2] = ((size - 11 - 4) >> 8) & 0xff;
835   data[3] = ((size - 11 - 4) >> 0) & 0xff;
836
837   data[4] = (timestamp >> 16) & 0xff;
838   data[5] = (timestamp >> 8) & 0xff;
839   data[6] = (timestamp >> 0) & 0xff;
840   data[7] = (timestamp >> 24) & 0xff;
841
842   data[8] = data[9] = data[10] = 0;
843
844   if (cpad->video) {
845     if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT))
846       data[11] |= 2 << 4;
847     else
848       data[11] |= 1 << 4;
849
850     data[11] |= cpad->video_codec & 0x0f;
851
852     if (cpad->video_codec == 7 && !cpad->sent_codec_data) {
853       data[12] = 0;
854       data[13] = data[14] = data[15] = 0;
855
856       memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (cpad->video_codec_data),
857           GST_BUFFER_SIZE (cpad->video_codec_data));
858       second_run = TRUE;
859     } else if (cpad->video_codec == 7) {
860       data[12] = 1;
861
862       /* FIXME: what to do about composition time */
863       data[13] = data[14] = data[15] = 0;
864
865       memcpy (data + 11 + 1 + 4, GST_BUFFER_DATA (buffer),
866           GST_BUFFER_SIZE (buffer));
867     } else {
868       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
869           GST_BUFFER_SIZE (buffer));
870     }
871   } else {
872     data[11] |= (cpad->audio_codec << 4) & 0xf0;
873     data[11] |= (cpad->rate << 2) & 0x0c;
874     data[11] |= (cpad->width << 1) & 0x02;
875     data[11] |= (cpad->channels << 0) & 0x01;
876
877     if (cpad->audio_codec == 10 && !cpad->sent_codec_data) {
878       data[12] = 0;
879
880       memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (cpad->audio_codec_data),
881           GST_BUFFER_SIZE (cpad->audio_codec_data));
882       second_run = TRUE;
883     } else if (cpad->audio_codec == 10) {
884       data[12] = 1;
885
886       memcpy (data + 11 + 1 + 1, GST_BUFFER_DATA (buffer),
887           GST_BUFFER_SIZE (buffer));
888     } else {
889       memcpy (data + 11 + 1, GST_BUFFER_DATA (buffer),
890           GST_BUFFER_SIZE (buffer));
891     }
892   }
893
894   GST_WRITE_UINT32_BE (data + size - 4, size - 4);
895
896   gst_buffer_set_caps (tag, GST_PAD_CAPS (mux->srcpad));
897
898   if (second_run) {
899     second_run = FALSE;
900     cpad->sent_codec_data = TRUE;
901
902     ret = gst_pad_push (mux->srcpad, tag);
903     if (ret != GST_FLOW_OK) {
904       gst_buffer_unref (buffer);
905       return ret;
906     }
907
908     cpad->last_timestamp = timestamp;
909
910     tag = NULL;
911     goto next;
912   }
913
914   gst_buffer_copy_metadata (tag, buffer, GST_BUFFER_COPY_TIMESTAMPS);
915   GST_BUFFER_OFFSET (tag) = GST_BUFFER_OFFSET_END (tag) =
916       GST_BUFFER_OFFSET_NONE;
917
918   gst_buffer_unref (buffer);
919
920   ret = gst_pad_push (mux->srcpad, tag);
921
922   if (ret == GST_FLOW_OK)
923     cpad->last_timestamp = timestamp;
924
925   return ret;
926 }
927
928 static GstFlowReturn
929 gst_flv_mux_collected (GstCollectPads * pads, gpointer user_data)
930 {
931   GstFlvMux *mux = GST_FLV_MUX (user_data);
932   GstFlvPad *best;
933   GstClockTime best_time;
934   GstFlowReturn ret;
935   GSList *sl;
936   gboolean eos = TRUE;
937
938   if (mux->state == GST_FLV_MUX_STATE_HEADER) {
939     if (mux->collect->data == NULL) {
940       GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
941           ("No input streams configured"));
942       return GST_FLOW_ERROR;
943     }
944
945     if (gst_pad_push_event (mux->srcpad,
946             gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)))
947       ret = gst_flv_mux_write_header (mux);
948     else
949       ret = GST_FLOW_ERROR;
950
951     if (ret != GST_FLOW_OK)
952       return ret;
953     mux->state = GST_FLV_MUX_STATE_DATA;
954   }
955
956   best = NULL;
957   best_time = GST_CLOCK_TIME_NONE;
958   for (sl = mux->collect->data; sl; sl = sl->next) {
959     GstFlvPad *cpad = sl->data;
960     GstBuffer *buffer = gst_collect_pads_peek (pads, (GstCollectData *) cpad);
961     GstClockTime time;
962
963     if (!buffer)
964       continue;
965
966     eos = FALSE;
967
968     time = GST_BUFFER_TIMESTAMP (buffer);
969     gst_buffer_unref (buffer);
970
971     /* Use buffers without valid timestamp first */
972     if (!GST_CLOCK_TIME_IS_VALID (time)) {
973       GST_WARNING_OBJECT (pads, "Buffer without valid timestamp");
974
975       best_time = cpad->last_timestamp;
976       best = cpad;
977       break;
978     }
979
980
981     if (best == NULL || (GST_CLOCK_TIME_IS_VALID (best_time)
982             && time < best_time)) {
983       best = cpad;
984       best_time = time;
985     }
986   }
987
988   if (GST_CLOCK_TIME_IS_VALID (best_time)
989       && best_time / GST_MSECOND > G_MAXUINT32) {
990     GST_WARNING_OBJECT (mux, "Timestamp larger than FLV supports - EOS");
991     eos = TRUE;
992   }
993
994   if (!eos && best) {
995     return gst_flv_mux_write_buffer (mux, best);
996   } else if (eos) {
997     gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
998     return GST_FLOW_UNEXPECTED;
999   } else {
1000     return GST_FLOW_OK;
1001   }
1002 }
1003
1004 static GstStateChangeReturn
1005 gst_flv_mux_change_state (GstElement * element, GstStateChange transition)
1006 {
1007   GstStateChangeReturn ret;
1008   GstFlvMux *mux = GST_FLV_MUX (element);
1009
1010   switch (transition) {
1011     case GST_STATE_CHANGE_NULL_TO_READY:
1012       break;
1013     case GST_STATE_CHANGE_READY_TO_PAUSED:
1014       gst_collect_pads_start (mux->collect);
1015       break;
1016     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1017       break;
1018     case GST_STATE_CHANGE_PAUSED_TO_READY:
1019       gst_collect_pads_stop (mux->collect);
1020       break;
1021     default:
1022       break;
1023   }
1024
1025   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1026
1027   switch (transition) {
1028     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1029       break;
1030     case GST_STATE_CHANGE_PAUSED_TO_READY:
1031       gst_flv_mux_reset (GST_ELEMENT (mux));
1032       break;
1033     case GST_STATE_CHANGE_READY_TO_NULL:
1034       break;
1035     default:
1036       break;
1037   }
1038
1039   return ret;
1040 }