Commit | Line | Data |
---|---|---|
d4f5d74a | 1 | /* |
7fbde343 | 2 | * FLV muxer |
2912e87a | 3 | * Copyright (c) 2003 The Libav Project |
d4f5d74a | 4 | * |
2912e87a | 5 | * This file is part of Libav. |
b78e7197 | 6 | * |
2912e87a | 7 | * Libav 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 | * |
2912e87a | 12 | * Libav 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 | |
2912e87a | 18 | * License along with Libav; if not, write to the Free Software |
5509bffa | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
d4f5d74a | 20 | */ |
ee8aecd2 | 21 | |
3383a53e | 22 | #include "libavutil/intfloat.h" |
d4f5d74a | 23 | #include "avformat.h" |
6cac3a3b | 24 | #include "flv.h" |
80b39e1c | 25 | #include "internal.h" |
f23496b5 | 26 | #include "avc.h" |
16f82508 | 27 | #include "metadata.h" |
d2d67e42 | 28 | #include "libavutil/dict.h" |
d4f5d74a | 29 | |
068f2a22 MN |
30 | #undef NDEBUG |
31 | #include <assert.h> | |
32 | ||
7caf0cc6 | 33 | static const AVCodecTag flv_video_codec_ids[] = { |
148c9bdb AH |
34 | {CODEC_ID_FLV1, FLV_CODECID_H263 }, |
35 | {CODEC_ID_FLASHSV, FLV_CODECID_SCREEN}, | |
1b88277b | 36 | {CODEC_ID_FLASHSV2, FLV_CODECID_SCREEN2}, |
148c9bdb | 37 | {CODEC_ID_VP6F, FLV_CODECID_VP6 }, |
09d8c0ae | 38 | {CODEC_ID_VP6, FLV_CODECID_VP6 }, |
f23496b5 | 39 | {CODEC_ID_H264, FLV_CODECID_H264 }, |
148c9bdb AH |
40 | {CODEC_ID_NONE, 0} |
41 | }; | |
42 | ||
7caf0cc6 | 43 | static const AVCodecTag flv_audio_codec_ids[] = { |
148c9bdb | 44 | {CODEC_ID_MP3, FLV_CODECID_MP3 >> FLV_AUDIO_CODECID_OFFSET}, |
8e9efe43 | 45 | {CODEC_ID_PCM_U8, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET}, |
44de39f9 | 46 | {CODEC_ID_PCM_S16BE, FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET}, |
148c9bdb AH |
47 | {CODEC_ID_PCM_S16LE, FLV_CODECID_PCM_LE >> FLV_AUDIO_CODECID_OFFSET}, |
48 | {CODEC_ID_ADPCM_SWF, FLV_CODECID_ADPCM >> FLV_AUDIO_CODECID_OFFSET}, | |
f23496b5 | 49 | {CODEC_ID_AAC, FLV_CODECID_AAC >> FLV_AUDIO_CODECID_OFFSET}, |
b7d1cd02 | 50 | {CODEC_ID_NELLYMOSER, FLV_CODECID_NELLYMOSER >> FLV_AUDIO_CODECID_OFFSET}, |
046c4001 | 51 | {CODEC_ID_SPEEX, FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET}, |
148c9bdb AH |
52 | {CODEC_ID_NONE, 0} |
53 | }; | |
54 | ||
d4f5d74a | 55 | typedef struct FLVContext { |
11a8e425 | 56 | int reserved; |
bc5c918e DB |
57 | int64_t duration_offset; |
58 | int64_t filesize_offset; | |
634b8cfa | 59 | int64_t duration; |
905de119 | 60 | int64_t delay; ///< first dts delay (needed for AVC & Speex) |
d4f5d74a GM |
61 | } FLVContext; |
62 | ||
ef74e397 | 63 | typedef struct FLVStreamContext { |
ef74e397 JR |
64 | int64_t last_ts; ///< last timestamp for each stream |
65 | } FLVStreamContext; | |
66 | ||
0a3ad7ff AK |
67 | static int get_audio_flags(AVFormatContext *s, AVCodecContext *enc) |
68 | { | |
dd1c8f3e | 69 | int flags = (enc->bits_per_coded_sample == 16) ? FLV_SAMPLESSIZE_16BIT : FLV_SAMPLESSIZE_8BIT; |
37cdf93d | 70 | |
f23496b5 BC |
71 | if (enc->codec_id == CODEC_ID_AAC) // specs force these parameters |
72 | return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ | FLV_SAMPLESSIZE_16BIT | FLV_STEREO; | |
046c4001 JR |
73 | else if (enc->codec_id == CODEC_ID_SPEEX) { |
74 | if (enc->sample_rate != 16000) { | |
0a3ad7ff | 75 | av_log(s, AV_LOG_ERROR, "flv only supports wideband (16kHz) Speex audio\n"); |
046c4001 JR |
76 | return -1; |
77 | } | |
78 | if (enc->channels != 1) { | |
0a3ad7ff | 79 | av_log(s, AV_LOG_ERROR, "flv only supports mono Speex audio\n"); |
046c4001 JR |
80 | return -1; |
81 | } | |
046c4001 JR |
82 | return FLV_CODECID_SPEEX | FLV_SAMPLERATE_11025HZ | FLV_SAMPLESSIZE_16BIT; |
83 | } else { | |
37cdf93d MN |
84 | switch (enc->sample_rate) { |
85 | case 44100: | |
6cac3a3b | 86 | flags |= FLV_SAMPLERATE_44100HZ; |
37cdf93d MN |
87 | break; |
88 | case 22050: | |
6cac3a3b | 89 | flags |= FLV_SAMPLERATE_22050HZ; |
37cdf93d MN |
90 | break; |
91 | case 11025: | |
6cac3a3b | 92 | flags |= FLV_SAMPLERATE_11025HZ; |
37cdf93d | 93 | break; |
bd2ff1a8 | 94 | case 16000: //nellymoser only |
37cdf93d MN |
95 | case 8000: //nellymoser only |
96 | case 5512: //not mp3 | |
4838727e | 97 | if(enc->codec_id != CODEC_ID_MP3){ |
ec627278 MN |
98 | flags |= FLV_SAMPLERATE_SPECIAL; |
99 | break; | |
4838727e | 100 | } |
37cdf93d | 101 | default: |
0a3ad7ff | 102 | av_log(s, AV_LOG_ERROR, "flv does not support that sample rate, choose from (44100, 22050, 11025).\n"); |
37cdf93d MN |
103 | return -1; |
104 | } | |
f23496b5 | 105 | } |
37cdf93d MN |
106 | |
107 | if (enc->channels > 1) { | |
6cac3a3b | 108 | flags |= FLV_STEREO; |
37cdf93d | 109 | } |
115329f1 | 110 | |
37cdf93d MN |
111 | switch(enc->codec_id){ |
112 | case CODEC_ID_MP3: | |
6cac3a3b | 113 | flags |= FLV_CODECID_MP3 | FLV_SAMPLESSIZE_16BIT; |
37cdf93d | 114 | break; |
8e9efe43 | 115 | case CODEC_ID_PCM_U8: |
44de39f9 | 116 | flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_8BIT; |
bb270c08 | 117 | break; |
923bd441 | 118 | case CODEC_ID_PCM_S16BE: |
44de39f9 | 119 | flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 120 | break; |
923bd441 | 121 | case CODEC_ID_PCM_S16LE: |
6cac3a3b | 122 | flags |= FLV_CODECID_PCM_LE | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 123 | break; |
2fde8aae | 124 | case CODEC_ID_ADPCM_SWF: |
e9509536 | 125 | flags |= FLV_CODECID_ADPCM | FLV_SAMPLESSIZE_16BIT; |
bb270c08 | 126 | break; |
b7d1cd02 | 127 | case CODEC_ID_NELLYMOSER: |
8ddd280d AW |
128 | if (enc->sample_rate == 8000) { |
129 | flags |= FLV_CODECID_NELLYMOSER_8KHZ_MONO | FLV_SAMPLESSIZE_16BIT; | |
bd2ff1a8 MS |
130 | } else if (enc->sample_rate == 16000) { |
131 | flags |= FLV_CODECID_NELLYMOSER_16KHZ_MONO | FLV_SAMPLESSIZE_16BIT; | |
8ddd280d AW |
132 | } else { |
133 | flags |= FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT; | |
134 | } | |
b7d1cd02 | 135 | break; |
37cdf93d MN |
136 | case 0: |
137 | flags |= enc->codec_tag<<4; | |
138 | break; | |
139 | default: | |
0a3ad7ff | 140 | av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); |
37cdf93d MN |
141 | return -1; |
142 | } | |
115329f1 | 143 | |
37cdf93d MN |
144 | return flags; |
145 | } | |
146 | ||
ae628ec1 | 147 | static void put_amf_string(AVIOContext *pb, const char *str) |
fd0fb306 MN |
148 | { |
149 | size_t len = strlen(str); | |
77eb5504 AK |
150 | avio_wb16(pb, len); |
151 | avio_write(pb, str, len); | |
fd0fb306 MN |
152 | } |
153 | ||
ae628ec1 | 154 | static void put_avc_eos_tag(AVIOContext *pb, unsigned ts) { |
77eb5504 AK |
155 | avio_w8(pb, FLV_TAG_TYPE_VIDEO); |
156 | avio_wb24(pb, 5); /* Tag Data Size */ | |
157 | avio_wb24(pb, ts); /* lower 24 bits of timestamp in ms*/ | |
158 | avio_w8(pb, (ts >> 24) & 0x7F); /* MSB of ts in ms*/ | |
159 | avio_wb24(pb, 0); /* StreamId = 0 */ | |
160 | avio_w8(pb, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */ | |
161 | avio_w8(pb, 2); /* AVC end of sequence */ | |
162 | avio_wb24(pb, 0); /* Always 0 for AVC EOS. */ | |
163 | avio_wb32(pb, 16); /* Size of FLV tag */ | |
df4f1d51 TF |
164 | } |
165 | ||
ae628ec1 | 166 | static void put_amf_double(AVIOContext *pb, double d) |
fd0fb306 | 167 | { |
77eb5504 | 168 | avio_w8(pb, AMF_DATA_TYPE_NUMBER); |
3383a53e | 169 | avio_wb64(pb, av_double2int(d)); |
fd0fb306 MN |
170 | } |
171 | ||
ae628ec1 | 172 | static void put_amf_bool(AVIOContext *pb, int b) { |
77eb5504 AK |
173 | avio_w8(pb, AMF_DATA_TYPE_BOOL); |
174 | avio_w8(pb, !!b); | |
148c9bdb AH |
175 | } |
176 | ||
d4f5d74a GM |
177 | static int flv_write_header(AVFormatContext *s) |
178 | { | |
ae628ec1 | 179 | AVIOContext *pb = s->pb; |
d4f5d74a | 180 | FLVContext *flv = s->priv_data; |
21e2dc9f | 181 | AVCodecContext *audio_enc = NULL, *video_enc = NULL, *data_enc = NULL; |
cad0c375 | 182 | int i, metadata_count = 0; |
fd0fb306 | 183 | double framerate = 0.0; |
cad0c375 | 184 | int64_t metadata_size_pos, data_size, metadata_count_pos; |
d2d67e42 | 185 | AVDictionaryEntry *tag = NULL; |
d4f5d74a | 186 | |
11a8e425 | 187 | for(i=0; i<s->nb_streams; i++){ |
01f4895c | 188 | AVCodecContext *enc = s->streams[i]->codec; |
ef74e397 | 189 | FLVStreamContext *sc; |
21e2dc9f LB |
190 | switch (enc->codec_type) { |
191 | case AVMEDIA_TYPE_VIDEO: | |
fd0fb306 MN |
192 | if (s->streams[i]->r_frame_rate.den && s->streams[i]->r_frame_rate.num) { |
193 | framerate = av_q2d(s->streams[i]->r_frame_rate); | |
194 | } else { | |
195 | framerate = 1/av_q2d(s->streams[i]->codec->time_base); | |
196 | } | |
5366f15d BC |
197 | video_enc = enc; |
198 | if(enc->codec_tag == 0) { | |
0a3ad7ff | 199 | av_log(s, AV_LOG_ERROR, "video codec not compatible with flv\n"); |
148c9bdb AH |
200 | return -1; |
201 | } | |
21e2dc9f LB |
202 | break; |
203 | case AVMEDIA_TYPE_AUDIO: | |
5366f15d | 204 | audio_enc = enc; |
0a3ad7ff | 205 | if (get_audio_flags(s, enc) < 0) |
21e2dc9f LB |
206 | return AVERROR_INVALIDDATA; |
207 | break; | |
208 | case AVMEDIA_TYPE_DATA: | |
209 | if (enc->codec_id != CODEC_ID_TEXT) { | |
210 | av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); | |
211 | return AVERROR_INVALIDDATA; | |
212 | } | |
213 | data_enc = enc; | |
214 | break; | |
215 | default: | |
216 | av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); | |
217 | return -1; | |
fd0fb306 | 218 | } |
c3f9ebf7 | 219 | avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ |
ef74e397 JR |
220 | |
221 | sc = av_mallocz(sizeof(FLVStreamContext)); | |
222 | if (!sc) | |
223 | return AVERROR(ENOMEM); | |
224 | s->streams[i]->priv_data = sc; | |
225 | sc->last_ts = -1; | |
c45388b1 | 226 | } |
905de119 JR |
227 | flv->delay = AV_NOPTS_VALUE; |
228 | ||
bbc413f9 | 229 | avio_write(pb, "FLV", 3); |
77eb5504 AK |
230 | avio_w8(pb,1); |
231 | avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!audio_enc | |
5366f15d | 232 | + FLV_HEADER_FLAG_HASVIDEO * !!video_enc); |
77eb5504 AK |
233 | avio_wb32(pb,9); |
234 | avio_wb32(pb,0); | |
c45388b1 MN |
235 | |
236 | for(i=0; i<s->nb_streams; i++){ | |
237 | if(s->streams[i]->codec->codec_tag == 5){ | |
77eb5504 AK |
238 | avio_w8(pb,8); // message type |
239 | avio_wb24(pb,0); // include flags | |
240 | avio_wb24(pb,0); // time stamp | |
241 | avio_wb32(pb,0); // reserved | |
242 | avio_wb32(pb,11); // size | |
11a8e425 MN |
243 | flv->reserved=5; |
244 | } | |
245 | } | |
d4f5d74a | 246 | |
fd0fb306 | 247 | /* write meta_tag */ |
77eb5504 | 248 | avio_w8(pb, 18); // tag type META |
a2704c97 | 249 | metadata_size_pos= avio_tell(pb); |
77eb5504 AK |
250 | avio_wb24(pb, 0); // size of data part (sum of all parts below) |
251 | avio_wb24(pb, 0); // time stamp | |
252 | avio_wb32(pb, 0); // reserved | |
fd0fb306 MN |
253 | |
254 | /* now data of data_size size */ | |
255 | ||
256 | /* first event name as a string */ | |
77eb5504 | 257 | avio_w8(pb, AMF_DATA_TYPE_STRING); |
fd0fb306 MN |
258 | put_amf_string(pb, "onMetaData"); // 12 bytes |
259 | ||
260 | /* mixed array (hash) with size and string/type/data tuples */ | |
77eb5504 | 261 | avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); |
cad0c375 | 262 | metadata_count_pos = avio_tell(pb); |
21e2dc9f LB |
263 | metadata_count = 5 * !!video_enc + |
264 | 5 * !!audio_enc + | |
265 | 1 * !!data_enc + | |
266 | 2; // +2 for duration and file size | |
267 | ||
cad0c375 | 268 | avio_wb32(pb, metadata_count); |
fd0fb306 | 269 | |
634b8cfa | 270 | put_amf_string(pb, "duration"); |
a2704c97 | 271 | flv->duration_offset= avio_tell(pb); |
c5e1e982 | 272 | put_amf_double(pb, s->duration / AV_TIME_BASE); // fill in the guessed duration, it'll be corrected later if incorrect |
fd0fb306 | 273 | |
5366f15d | 274 | if(video_enc){ |
fd0fb306 | 275 | put_amf_string(pb, "width"); |
5366f15d | 276 | put_amf_double(pb, video_enc->width); |
fd0fb306 MN |
277 | |
278 | put_amf_string(pb, "height"); | |
5366f15d | 279 | put_amf_double(pb, video_enc->height); |
fd0fb306 MN |
280 | |
281 | put_amf_string(pb, "videodatarate"); | |
426a6f34 | 282 | put_amf_double(pb, video_enc->bit_rate / 1024.0); |
fd0fb306 MN |
283 | |
284 | put_amf_string(pb, "framerate"); | |
285 | put_amf_double(pb, framerate); | |
148c9bdb AH |
286 | |
287 | put_amf_string(pb, "videocodecid"); | |
5366f15d | 288 | put_amf_double(pb, video_enc->codec_tag); |
fd0fb306 MN |
289 | } |
290 | ||
5366f15d | 291 | if(audio_enc){ |
426a6f34 SK |
292 | put_amf_string(pb, "audiodatarate"); |
293 | put_amf_double(pb, audio_enc->bit_rate / 1024.0); | |
294 | ||
fd0fb306 | 295 | put_amf_string(pb, "audiosamplerate"); |
5366f15d | 296 | put_amf_double(pb, audio_enc->sample_rate); |
148c9bdb AH |
297 | |
298 | put_amf_string(pb, "audiosamplesize"); | |
8e9efe43 | 299 | put_amf_double(pb, audio_enc->codec_id == CODEC_ID_PCM_U8 ? 8 : 16); |
148c9bdb AH |
300 | |
301 | put_amf_string(pb, "stereo"); | |
5366f15d | 302 | put_amf_bool(pb, audio_enc->channels == 2); |
148c9bdb AH |
303 | |
304 | put_amf_string(pb, "audiocodecid"); | |
5366f15d | 305 | put_amf_double(pb, audio_enc->codec_tag); |
fd0fb306 MN |
306 | } |
307 | ||
21e2dc9f LB |
308 | if (data_enc) { |
309 | put_amf_string(pb, "datastream"); | |
310 | put_amf_double(pb, 0.0); | |
311 | } | |
312 | ||
d2d67e42 | 313 | while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { |
16f82508 | 314 | put_amf_string(pb, tag->key); |
77eb5504 | 315 | avio_w8(pb, AMF_DATA_TYPE_STRING); |
16f82508 | 316 | put_amf_string(pb, tag->value); |
cad0c375 | 317 | metadata_count++; |
16f82508 TT |
318 | } |
319 | ||
634b8cfa | 320 | put_amf_string(pb, "filesize"); |
a2704c97 | 321 | flv->filesize_offset= avio_tell(pb); |
634b8cfa | 322 | put_amf_double(pb, 0); // delayed write |
fd0fb306 MN |
323 | |
324 | put_amf_string(pb, ""); | |
77eb5504 | 325 | avio_w8(pb, AMF_END_OF_OBJECT); |
fd0fb306 MN |
326 | |
327 | /* write total size of tag */ | |
a2704c97 | 328 | data_size= avio_tell(pb) - metadata_size_pos - 10; |
cad0c375 MS |
329 | |
330 | avio_seek(pb, metadata_count_pos, SEEK_SET); | |
331 | avio_wb32(pb, metadata_count); | |
332 | ||
6b4aa5da | 333 | avio_seek(pb, metadata_size_pos, SEEK_SET); |
77eb5504 | 334 | avio_wb24(pb, data_size); |
45a8a02a | 335 | avio_skip(pb, data_size + 10 - 3); |
77eb5504 | 336 | avio_wb32(pb, data_size + 11); |
fd0fb306 | 337 | |
f23496b5 BC |
338 | for (i = 0; i < s->nb_streams; i++) { |
339 | AVCodecContext *enc = s->streams[i]->codec; | |
340 | if (enc->codec_id == CODEC_ID_AAC || enc->codec_id == CODEC_ID_H264) { | |
bc5c918e | 341 | int64_t pos; |
77eb5504 | 342 | avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? |
f23496b5 | 343 | FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); |
77eb5504 AK |
344 | avio_wb24(pb, 0); // size patched later |
345 | avio_wb24(pb, 0); // ts | |
346 | avio_w8(pb, 0); // ts ext | |
347 | avio_wb24(pb, 0); // streamid | |
a2704c97 | 348 | pos = avio_tell(pb); |
f23496b5 | 349 | if (enc->codec_id == CODEC_ID_AAC) { |
0a3ad7ff | 350 | avio_w8(pb, get_audio_flags(s, enc)); |
77eb5504 AK |
351 | avio_w8(pb, 0); // AAC sequence header |
352 | avio_write(pb, enc->extradata, enc->extradata_size); | |
f23496b5 | 353 | } else { |
77eb5504 AK |
354 | avio_w8(pb, enc->codec_tag | FLV_FRAME_KEY); // flags |
355 | avio_w8(pb, 0); // AVC sequence header | |
356 | avio_wb24(pb, 0); // composition time | |
f23496b5 BC |
357 | ff_isom_write_avcc(pb, enc->extradata, enc->extradata_size); |
358 | } | |
a2704c97 | 359 | data_size = avio_tell(pb) - pos; |
6b4aa5da | 360 | avio_seek(pb, -data_size - 10, SEEK_CUR); |
77eb5504 | 361 | avio_wb24(pb, data_size); |
45a8a02a | 362 | avio_skip(pb, data_size + 10 - 3); |
77eb5504 | 363 | avio_wb32(pb, data_size + 11); // previous tag size |
f23496b5 BC |
364 | } |
365 | } | |
366 | ||
d4f5d74a GM |
367 | return 0; |
368 | } | |
369 | ||
d4f5d74a GM |
370 | static int flv_write_trailer(AVFormatContext *s) |
371 | { | |
14b32253 | 372 | int64_t file_size; |
14b32253 | 373 | |
ae628ec1 | 374 | AVIOContext *pb = s->pb; |
d4f5d74a | 375 | FLVContext *flv = s->priv_data; |
df4f1d51 TF |
376 | int i; |
377 | ||
378 | /* Add EOS tag */ | |
379 | for (i = 0; i < s->nb_streams; i++) { | |
380 | AVCodecContext *enc = s->streams[i]->codec; | |
ef74e397 | 381 | FLVStreamContext *sc = s->streams[i]->priv_data; |
b9f9e59a | 382 | if (enc->codec_type == AVMEDIA_TYPE_VIDEO && |
df4f1d51 | 383 | enc->codec_id == CODEC_ID_H264) { |
ef74e397 | 384 | put_avc_eos_tag(pb, sc->last_ts); |
df4f1d51 TF |
385 | } |
386 | } | |
d4f5d74a | 387 | |
a2704c97 | 388 | file_size = avio_tell(pb); |
634b8cfa | 389 | |
da9cea77 | 390 | /* update information */ |
6b4aa5da | 391 | avio_seek(pb, flv->duration_offset, SEEK_SET); |
634b8cfa | 392 | put_amf_double(pb, flv->duration / (double)1000); |
6b4aa5da | 393 | avio_seek(pb, flv->filesize_offset, SEEK_SET); |
634b8cfa BC |
394 | put_amf_double(pb, file_size); |
395 | ||
6b4aa5da | 396 | avio_seek(pb, file_size, SEEK_SET); |
d4f5d74a GM |
397 | return 0; |
398 | } | |
399 | ||
e928649b | 400 | static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) |
d4f5d74a | 401 | { |
ae628ec1 | 402 | AVIOContext *pb = s->pb; |
01f4895c | 403 | AVCodecContext *enc = s->streams[pkt->stream_index]->codec; |
d4f5d74a | 404 | FLVContext *flv = s->priv_data; |
ef74e397 | 405 | FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; |
f23496b5 | 406 | unsigned ts; |
e928649b | 407 | int size= pkt->size; |
71e685b0 | 408 | uint8_t *data= NULL; |
f683dbdc | 409 | int flags, flags_size; |
d4f5d74a | 410 | |
949b1a13 | 411 | // av_log(s, AV_LOG_DEBUG, "type:%d pts: %"PRId64" size:%d\n", enc->codec_type, timestamp, size); |
115329f1 | 412 | |
f23496b5 BC |
413 | if(enc->codec_id == CODEC_ID_VP6 || enc->codec_id == CODEC_ID_VP6F || |
414 | enc->codec_id == CODEC_ID_AAC) | |
f683dbdc | 415 | flags_size= 2; |
f23496b5 BC |
416 | else if(enc->codec_id == CODEC_ID_H264) |
417 | flags_size= 5; | |
f683dbdc MN |
418 | else |
419 | flags_size= 1; | |
420 | ||
21e2dc9f LB |
421 | switch (enc->codec_type) { |
422 | case AVMEDIA_TYPE_VIDEO: | |
77eb5504 | 423 | avio_w8(pb, FLV_TAG_TYPE_VIDEO); |
09d8c0ae | 424 | |
bb85077f | 425 | flags = enc->codec_tag; |
09d8c0ae | 426 | if(flags == 0) { |
0a3ad7ff | 427 | av_log(s, AV_LOG_ERROR, "video codec %X not compatible with flv\n",enc->codec_id); |
09d8c0ae BL |
428 | return -1; |
429 | } | |
430 | ||
cc947f04 | 431 | flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; |
21e2dc9f LB |
432 | break; |
433 | case AVMEDIA_TYPE_AUDIO: | |
0a3ad7ff | 434 | flags = get_audio_flags(s, enc); |
115329f1 | 435 | |
068f2a22 | 436 | assert(size); |
068f2a22 | 437 | |
77eb5504 | 438 | avio_w8(pb, FLV_TAG_TYPE_AUDIO); |
21e2dc9f | 439 | break; |
75293f05 | 440 | |
21e2dc9f LB |
441 | case AVMEDIA_TYPE_DATA: |
442 | avio_w8(pb, FLV_TAG_TYPE_META); | |
443 | break; | |
444 | default: | |
445 | return AVERROR(EINVAL); | |
446 | } | |
7d637efa | 447 | if (enc->codec_id == CODEC_ID_H264) { |
da9cea77 | 448 | /* check if extradata looks like MP4 */ |
71e685b0 BC |
449 | if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) { |
450 | if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) | |
451 | return -1; | |
452 | } | |
f23496b5 | 453 | } |
905de119 JR |
454 | if (flv->delay == AV_NOPTS_VALUE) |
455 | flv->delay = -pkt->dts; | |
456 | if (pkt->dts < -flv->delay) { | |
457 | av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with " | |
458 | "respect to DTS\n"); | |
459 | return AVERROR(EINVAL); | |
460 | } | |
f23496b5 | 461 | |
905de119 | 462 | ts = pkt->dts + flv->delay; // add delay to force positive dts |
4ee247a2 JR |
463 | |
464 | /* check Speex packet duration */ | |
ef74e397 | 465 | if (enc->codec_id == CODEC_ID_SPEEX && ts - sc->last_ts > 160) { |
4ee247a2 JR |
466 | av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " |
467 | "8 frames per packet. Adobe Flash " | |
468 | "Player cannot handle this!\n"); | |
df4f1d51 | 469 | } |
4ee247a2 | 470 | |
ef74e397 JR |
471 | if (sc->last_ts < ts) |
472 | sc->last_ts = ts; | |
4ee247a2 | 473 | |
77eb5504 AK |
474 | avio_wb24(pb,size + flags_size); |
475 | avio_wb24(pb,ts); | |
476 | avio_w8(pb,(ts >> 24) & 0x7F); // timestamps are 32bits _signed_ | |
477 | avio_wb24(pb,flv->reserved); | |
21e2dc9f LB |
478 | |
479 | if (enc->codec_type == AVMEDIA_TYPE_DATA) { | |
480 | int data_size; | |
481 | int metadata_size_pos = avio_tell(pb); | |
482 | avio_w8(pb, AMF_DATA_TYPE_STRING); | |
483 | put_amf_string(pb, "onTextData"); | |
484 | avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); | |
485 | avio_wb32(pb, 2); | |
486 | put_amf_string(pb, "type"); | |
487 | avio_w8(pb, AMF_DATA_TYPE_STRING); | |
488 | put_amf_string(pb, "Text"); | |
489 | put_amf_string(pb, "text"); | |
490 | avio_w8(pb, AMF_DATA_TYPE_STRING); | |
491 | put_amf_string(pb, pkt->data); | |
492 | put_amf_string(pb, ""); | |
493 | avio_w8(pb, AMF_END_OF_OBJECT); | |
494 | /* write total size of tag */ | |
495 | data_size = avio_tell(pb) - metadata_size_pos; | |
496 | avio_seek(pb, metadata_size_pos - 10, SEEK_SET); | |
497 | avio_wb24(pb, data_size); | |
498 | avio_seek(pb, data_size + 10 - 3, SEEK_CUR); | |
499 | avio_wb32(pb, data_size + 11); | |
500 | } else { | |
77eb5504 | 501 | avio_w8(pb,flags); |
09d8c0ae | 502 | if (enc->codec_id == CODEC_ID_VP6) |
77eb5504 | 503 | avio_w8(pb,0); |
09d8c0ae | 504 | if (enc->codec_id == CODEC_ID_VP6F) |
77eb5504 | 505 | avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); |
f23496b5 | 506 | else if (enc->codec_id == CODEC_ID_AAC) |
77eb5504 | 507 | avio_w8(pb,1); // AAC raw |
f23496b5 | 508 | else if (enc->codec_id == CODEC_ID_H264) { |
77eb5504 AK |
509 | avio_w8(pb,1); // AVC NALU |
510 | avio_wb24(pb,pkt->pts - pkt->dts); | |
f23496b5 | 511 | } |
71e685b0 | 512 | |
77eb5504 | 513 | avio_write(pb, data ? data : pkt->data, size); |
71e685b0 | 514 | |
77eb5504 | 515 | avio_wb32(pb,size+flags_size+11); // previous tag size |
905de119 | 516 | flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); |
21e2dc9f | 517 | } |
b7f2fdde | 518 | avio_flush(pb); |
71e685b0 BC |
519 | |
520 | av_free(data); | |
521 | ||
0e28e9ca | 522 | return pb->error; |
d4f5d74a GM |
523 | } |
524 | ||
c6610a21 | 525 | AVOutputFormat ff_flv_muxer = { |
dfc2c4d9 AK |
526 | .name = "flv", |
527 | .long_name = NULL_IF_CONFIG_SMALL("FLV format"), | |
528 | .mime_type = "video/x-flv", | |
529 | .extensions = "flv", | |
530 | .priv_data_size = sizeof(FLVContext), | |
b250f9c6 | 531 | #if CONFIG_LIBMP3LAME |
dfc2c4d9 | 532 | .audio_codec = CODEC_ID_MP3, |
6ebe07fb | 533 | #else // CONFIG_LIBMP3LAME |
dfc2c4d9 | 534 | .audio_codec = CODEC_ID_ADPCM_SWF, |
6ebe07fb | 535 | #endif // CONFIG_LIBMP3LAME |
dfc2c4d9 AK |
536 | .video_codec = CODEC_ID_FLV1, |
537 | .write_header = flv_write_header, | |
538 | .write_packet = flv_write_packet, | |
539 | .write_trailer = flv_write_trailer, | |
20234a4b MS |
540 | .codec_tag = (const AVCodecTag* const []){ |
541 | flv_video_codec_ids, flv_audio_codec_ids, 0 | |
542 | }, | |
e9cef897 JZ |
543 | .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | |
544 | AVFMT_TS_NONSTRICT, | |
d4f5d74a | 545 | }; |