Commit | Line | Data |
---|---|---|
d4f5d74a | 1 | /* |
7fbde343 | 2 | * FLV muxer |
d4f5d74a GM |
3 | * Copyright (c) 2003 The FFmpeg Project. |
4 | * | |
b78e7197 DB |
5 | * This file is part of FFmpeg. |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
d4f5d74a GM |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | |
b78e7197 | 10 | * version 2.1 of the License, or (at your option) any later version. |
d4f5d74a | 11 | * |
b78e7197 | 12 | * FFmpeg is distributed in the hope that it will be useful, |
d4f5d74a GM |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
b78e7197 | 18 | * License along with FFmpeg; if not, write to the Free Software |
5509bffa | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
d4f5d74a GM |
20 | */ |
21 | #include "avformat.h" | |
6cac3a3b | 22 | #include "flv.h" |
148c9bdb | 23 | #include "riff.h" |
d4f5d74a | 24 | |
068f2a22 MN |
25 | #undef NDEBUG |
26 | #include <assert.h> | |
27 | ||
7caf0cc6 | 28 | static const AVCodecTag flv_video_codec_ids[] = { |
148c9bdb AH |
29 | {CODEC_ID_FLV1, FLV_CODECID_H263 }, |
30 | {CODEC_ID_FLASHSV, FLV_CODECID_SCREEN}, | |
31 | {CODEC_ID_VP6F, FLV_CODECID_VP6 }, | |
09d8c0ae | 32 | {CODEC_ID_VP6, FLV_CODECID_VP6 }, |
148c9bdb AH |
33 | {CODEC_ID_NONE, 0} |
34 | }; | |
35 | ||
7caf0cc6 | 36 | static const AVCodecTag flv_audio_codec_ids[] = { |
148c9bdb | 37 | {CODEC_ID_MP3, FLV_CODECID_MP3 >> FLV_AUDIO_CODECID_OFFSET}, |
44de39f9 MN |
38 | {CODEC_ID_PCM_S8, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET}, |
39 | {CODEC_ID_PCM_S16BE, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET}, | |
148c9bdb AH |
40 | {CODEC_ID_PCM_S16LE, FLV_CODECID_PCM_LE >> FLV_AUDIO_CODECID_OFFSET}, |
41 | {CODEC_ID_ADPCM_SWF, FLV_CODECID_ADPCM >> FLV_AUDIO_CODECID_OFFSET}, | |
b7d1cd02 | 42 | {CODEC_ID_NELLYMOSER, FLV_CODECID_NELLYMOSER >> FLV_AUDIO_CODECID_OFFSET}, |
148c9bdb AH |
43 | {CODEC_ID_NONE, 0} |
44 | }; | |
45 | ||
d4f5d74a GM |
46 | typedef struct FLVContext { |
47 | int hasAudio; | |
48 | int hasVideo; | |
11a8e425 | 49 | int reserved; |
634b8cfa BC |
50 | offset_t duration_offset; |
51 | offset_t filesize_offset; | |
52 | int64_t duration; | |
d4f5d74a GM |
53 | } FLVContext; |
54 | ||
37cdf93d | 55 | static int get_audio_flags(AVCodecContext *enc){ |
6cac3a3b | 56 | int flags = (enc->bits_per_sample == 16) ? FLV_SAMPLESSIZE_16BIT : FLV_SAMPLESSIZE_8BIT; |
37cdf93d MN |
57 | |
58 | switch (enc->sample_rate) { | |
59 | case 44100: | |
6cac3a3b | 60 | flags |= FLV_SAMPLERATE_44100HZ; |
37cdf93d MN |
61 | break; |
62 | case 22050: | |
6cac3a3b | 63 | flags |= FLV_SAMPLERATE_22050HZ; |
37cdf93d MN |
64 | break; |
65 | case 11025: | |
6cac3a3b | 66 | flags |= FLV_SAMPLERATE_11025HZ; |
37cdf93d MN |
67 | break; |
68 | case 8000: //nellymoser only | |
69 | case 5512: //not mp3 | |
4838727e | 70 | if(enc->codec_id != CODEC_ID_MP3){ |
ec627278 MN |
71 | flags |= FLV_SAMPLERATE_SPECIAL; |
72 | break; | |
4838727e | 73 | } |
37cdf93d | 74 | default: |
755bfeab | 75 | av_log(enc, AV_LOG_ERROR, "flv does not support that sample rate, choose from (44100, 22050, 11025).\n"); |
37cdf93d MN |
76 | return -1; |
77 | } | |
78 | ||
79 | if (enc->channels > 1) { | |
6cac3a3b | 80 | flags |= FLV_STEREO; |
37cdf93d | 81 | } |
115329f1 | 82 | |
37cdf93d MN |
83 | switch(enc->codec_id){ |
84 | case CODEC_ID_MP3: | |
6cac3a3b | 85 | flags |= FLV_CODECID_MP3 | FLV_SAMPLESSIZE_16BIT; |
37cdf93d | 86 | break; |
923bd441 | 87 | case CODEC_ID_PCM_S8: |
44de39f9 | 88 | flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_8BIT; |
bb270c08 | 89 | break; |
923bd441 | 90 | case CODEC_ID_PCM_S16BE: |
44de39f9 | 91 | flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 92 | break; |
923bd441 | 93 | case CODEC_ID_PCM_S16LE: |
6cac3a3b | 94 | flags |= FLV_CODECID_PCM_LE | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 95 | break; |
2fde8aae | 96 | case CODEC_ID_ADPCM_SWF: |
e9509536 | 97 | flags |= FLV_CODECID_ADPCM | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 98 | break; |
b7d1cd02 BW |
99 | case CODEC_ID_NELLYMOSER: |
100 | flags |= FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT; | |
101 | break; | |
37cdf93d MN |
102 | case 0: |
103 | flags |= enc->codec_tag<<4; | |
104 | break; | |
105 | default: | |
a254c574 | 106 | av_log(enc, AV_LOG_ERROR, "codec not compatible with flv\n"); |
37cdf93d MN |
107 | return -1; |
108 | } | |
115329f1 | 109 | |
37cdf93d MN |
110 | return flags; |
111 | } | |
112 | ||
fd0fb306 MN |
113 | static void put_amf_string(ByteIOContext *pb, const char *str) |
114 | { | |
115 | size_t len = strlen(str); | |
116 | put_be16(pb, len); | |
117 | put_buffer(pb, str, len); | |
118 | } | |
119 | ||
120 | static void put_amf_double(ByteIOContext *pb, double d) | |
121 | { | |
6cac3a3b | 122 | put_byte(pb, AMF_DATA_TYPE_NUMBER); |
fd0fb306 MN |
123 | put_be64(pb, av_dbl2int(d)); |
124 | } | |
125 | ||
148c9bdb AH |
126 | static void put_amf_bool(ByteIOContext *pb, int b) { |
127 | put_byte(pb, AMF_DATA_TYPE_BOOL); | |
128 | put_byte(pb, !!b); | |
129 | } | |
130 | ||
d4f5d74a GM |
131 | static int flv_write_header(AVFormatContext *s) |
132 | { | |
899681cd | 133 | ByteIOContext *pb = s->pb; |
d4f5d74a | 134 | FLVContext *flv = s->priv_data; |
148c9bdb | 135 | int i, width, height, samplerate, samplesize, channels, audiocodecid, videocodecid; |
fd0fb306 MN |
136 | double framerate = 0.0; |
137 | int metadata_size_pos, data_size; | |
d4f5d74a | 138 | |
d4f5d74a GM |
139 | flv->hasAudio = 0; |
140 | flv->hasVideo = 0; | |
141 | ||
11a8e425 | 142 | for(i=0; i<s->nb_streams; i++){ |
01f4895c | 143 | AVCodecContext *enc = s->streams[i]->codec; |
fd0fb306 MN |
144 | if (enc->codec_type == CODEC_TYPE_VIDEO) { |
145 | width = enc->width; | |
146 | height = enc->height; | |
147 | if (s->streams[i]->r_frame_rate.den && s->streams[i]->r_frame_rate.num) { | |
148 | framerate = av_q2d(s->streams[i]->r_frame_rate); | |
149 | } else { | |
150 | framerate = 1/av_q2d(s->streams[i]->codec->time_base); | |
151 | } | |
152 | flv->hasVideo=1; | |
148c9bdb | 153 | |
bb85077f | 154 | videocodecid = enc->codec_tag; |
148c9bdb AH |
155 | if(videocodecid == 0) { |
156 | av_log(enc, AV_LOG_ERROR, "video codec not compatible with flv\n"); | |
157 | return -1; | |
158 | } | |
fd0fb306 MN |
159 | } else { |
160 | flv->hasAudio=1; | |
161 | samplerate = enc->sample_rate; | |
148c9bdb AH |
162 | channels = enc->channels; |
163 | ||
bb85077f | 164 | audiocodecid = enc->codec_tag; |
148c9bdb AH |
165 | samplesize = (enc->codec_id == CODEC_ID_PCM_S8) ? 8 : 16; |
166 | ||
c45388b1 MN |
167 | if(get_audio_flags(enc)<0) |
168 | return -1; | |
fd0fb306 | 169 | } |
254629b1 | 170 | av_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ |
c45388b1 MN |
171 | } |
172 | put_tag(pb,"FLV"); | |
173 | put_byte(pb,1); | |
174 | put_byte(pb, FLV_HEADER_FLAG_HASAUDIO * flv->hasAudio | |
175 | + FLV_HEADER_FLAG_HASVIDEO * flv->hasVideo); | |
176 | put_be32(pb,9); | |
177 | put_be32(pb,0); | |
178 | ||
179 | for(i=0; i<s->nb_streams; i++){ | |
180 | if(s->streams[i]->codec->codec_tag == 5){ | |
11a8e425 MN |
181 | put_byte(pb,8); // message type |
182 | put_be24(pb,0); // include flags | |
183 | put_be24(pb,0); // time stamp | |
184 | put_be32(pb,0); // reserved | |
185 | put_be32(pb,11); // size | |
186 | flv->reserved=5; | |
187 | } | |
188 | } | |
d4f5d74a | 189 | |
fd0fb306 MN |
190 | /* write meta_tag */ |
191 | put_byte(pb, 18); // tag type META | |
192 | metadata_size_pos= url_ftell(pb); | |
193 | put_be24(pb, 0); // size of data part (sum of all parts below) | |
194 | put_be24(pb, 0); // time stamp | |
195 | put_be32(pb, 0); // reserved | |
196 | ||
197 | /* now data of data_size size */ | |
198 | ||
199 | /* first event name as a string */ | |
6cac3a3b | 200 | put_byte(pb, AMF_DATA_TYPE_STRING); |
fd0fb306 MN |
201 | put_amf_string(pb, "onMetaData"); // 12 bytes |
202 | ||
203 | /* mixed array (hash) with size and string/type/data tuples */ | |
6cac3a3b | 204 | put_byte(pb, AMF_DATA_TYPE_MIXEDARRAY); |
148c9bdb | 205 | put_be32(pb, 5*flv->hasVideo + 4*flv->hasAudio + 2); // +2 for duration and file size |
fd0fb306 | 206 | |
634b8cfa BC |
207 | put_amf_string(pb, "duration"); |
208 | flv->duration_offset= url_ftell(pb); | |
209 | put_amf_double(pb, 0); // delayed write | |
fd0fb306 MN |
210 | |
211 | if(flv->hasVideo){ | |
212 | put_amf_string(pb, "width"); | |
213 | put_amf_double(pb, width); | |
214 | ||
215 | put_amf_string(pb, "height"); | |
216 | put_amf_double(pb, height); | |
217 | ||
218 | put_amf_string(pb, "videodatarate"); | |
219 | put_amf_double(pb, s->bit_rate / 1024.0); | |
220 | ||
221 | put_amf_string(pb, "framerate"); | |
222 | put_amf_double(pb, framerate); | |
148c9bdb AH |
223 | |
224 | put_amf_string(pb, "videocodecid"); | |
225 | put_amf_double(pb, videocodecid); | |
fd0fb306 MN |
226 | } |
227 | ||
228 | if(flv->hasAudio){ | |
229 | put_amf_string(pb, "audiosamplerate"); | |
230 | put_amf_double(pb, samplerate); | |
148c9bdb AH |
231 | |
232 | put_amf_string(pb, "audiosamplesize"); | |
233 | put_amf_double(pb, samplesize); | |
234 | ||
235 | put_amf_string(pb, "stereo"); | |
236 | put_amf_bool(pb, (channels == 2)); | |
237 | ||
238 | put_amf_string(pb, "audiocodecid"); | |
239 | put_amf_double(pb, audiocodecid); | |
fd0fb306 MN |
240 | } |
241 | ||
634b8cfa BC |
242 | put_amf_string(pb, "filesize"); |
243 | flv->filesize_offset= url_ftell(pb); | |
244 | put_amf_double(pb, 0); // delayed write | |
fd0fb306 MN |
245 | |
246 | put_amf_string(pb, ""); | |
6cac3a3b | 247 | put_byte(pb, AMF_END_OF_OBJECT); |
fd0fb306 MN |
248 | |
249 | /* write total size of tag */ | |
250 | data_size= url_ftell(pb) - metadata_size_pos - 10; | |
251 | url_fseek(pb, metadata_size_pos, SEEK_SET); | |
252 | put_be24(pb, data_size); | |
253 | url_fseek(pb, data_size + 10 - 3, SEEK_CUR); | |
254 | put_be32(pb, data_size + 11); | |
255 | ||
d4f5d74a GM |
256 | return 0; |
257 | } | |
258 | ||
d4f5d74a GM |
259 | static int flv_write_trailer(AVFormatContext *s) |
260 | { | |
14b32253 | 261 | int64_t file_size; |
14b32253 | 262 | |
899681cd | 263 | ByteIOContext *pb = s->pb; |
d4f5d74a GM |
264 | FLVContext *flv = s->priv_data; |
265 | ||
14b32253 | 266 | file_size = url_ftell(pb); |
634b8cfa BC |
267 | |
268 | /* update informations */ | |
269 | url_fseek(pb, flv->duration_offset, SEEK_SET); | |
270 | put_amf_double(pb, flv->duration / (double)1000); | |
271 | url_fseek(pb, flv->filesize_offset, SEEK_SET); | |
272 | put_amf_double(pb, file_size); | |
273 | ||
d4f5d74a GM |
274 | url_fseek(pb, file_size, SEEK_SET); |
275 | return 0; | |
276 | } | |
277 | ||
e928649b | 278 | static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) |
d4f5d74a | 279 | { |
899681cd | 280 | ByteIOContext *pb = s->pb; |
01f4895c | 281 | AVCodecContext *enc = s->streams[pkt->stream_index]->codec; |
d4f5d74a | 282 | FLVContext *flv = s->priv_data; |
e928649b | 283 | int size= pkt->size; |
f683dbdc | 284 | int flags, flags_size; |
d4f5d74a | 285 | |
949b1a13 | 286 | // av_log(s, AV_LOG_DEBUG, "type:%d pts: %"PRId64" size:%d\n", enc->codec_type, timestamp, size); |
115329f1 | 287 | |
f683dbdc MN |
288 | if(enc->codec_id == CODEC_ID_VP6 || enc->codec_id == CODEC_ID_VP6F) |
289 | flags_size= 2; | |
290 | else | |
291 | flags_size= 1; | |
292 | ||
d4f5d74a | 293 | if (enc->codec_type == CODEC_TYPE_VIDEO) { |
6cac3a3b | 294 | put_byte(pb, FLV_TAG_TYPE_VIDEO); |
09d8c0ae | 295 | |
bb85077f | 296 | flags = enc->codec_tag; |
09d8c0ae BL |
297 | if(flags == 0) { |
298 | av_log(enc, AV_LOG_ERROR, "video codec %X not compatible with flv\n",enc->codec_id); | |
299 | return -1; | |
300 | } | |
301 | ||
6cac3a3b | 302 | flags |= pkt->flags & PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; |
75293f05 MN |
303 | } else { |
304 | assert(enc->codec_type == CODEC_TYPE_AUDIO); | |
37cdf93d | 305 | flags = get_audio_flags(enc); |
115329f1 | 306 | |
068f2a22 | 307 | assert(size); |
068f2a22 | 308 | |
6cac3a3b | 309 | put_byte(pb, FLV_TAG_TYPE_AUDIO); |
75293f05 MN |
310 | } |
311 | ||
f683dbdc | 312 | put_be24(pb,size + flags_size); |
75293f05 | 313 | put_be24(pb,pkt->pts); |
018b6fb4 AB |
314 | put_byte(pb,pkt->pts >> 24); |
315 | put_be24(pb,flv->reserved); | |
75293f05 | 316 | put_byte(pb,flags); |
09d8c0ae BL |
317 | if (enc->codec_id == CODEC_ID_VP6) |
318 | put_byte(pb,0); | |
319 | if (enc->codec_id == CODEC_ID_VP6F) | |
320 | put_byte(pb, enc->extradata_size ? enc->extradata[0] : 0); | |
75293f05 | 321 | put_buffer(pb, pkt->data, size); |
f683dbdc | 322 | put_be32(pb,size+flags_size+11); // previous tag size |
634b8cfa | 323 | flv->duration = pkt->pts + pkt->duration; |
115329f1 | 324 | |
d4f5d74a GM |
325 | put_flush_packet(pb); |
326 | return 0; | |
327 | } | |
328 | ||
ff70e601 | 329 | AVOutputFormat flv_muxer = { |
d4f5d74a GM |
330 | "flv", |
331 | "flv format", | |
e817a73d | 332 | "video/x-flv", |
d4f5d74a GM |
333 | "flv", |
334 | sizeof(FLVContext), | |
6ebe07fb | 335 | #ifdef CONFIG_LIBMP3LAME |
80783dc2 | 336 | CODEC_ID_MP3, |
6ebe07fb | 337 | #else // CONFIG_LIBMP3LAME |
964ff354 | 338 | CODEC_ID_ADPCM_SWF, |
6ebe07fb | 339 | #endif // CONFIG_LIBMP3LAME |
d4f5d74a GM |
340 | CODEC_ID_FLV1, |
341 | flv_write_header, | |
342 | flv_write_packet, | |
343 | flv_write_trailer, | |
6c77805f | 344 | .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0}, |
d4f5d74a | 345 | }; |