Commit | Line | Data |
---|---|---|
d4f5d74a | 1 | /* |
7fbde343 | 2 | * FLV demuxer |
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 |
246f86a4 AJ |
20 | * |
21 | * | |
22 | * This demuxer will generate a 1 byte extradata for VP6F content. | |
23 | * It is composed of: | |
24 | * - upper 4bits: difference between encoded width and visible width | |
25 | * - lower 4bits: difference between encoded height and visible height | |
d4f5d74a GM |
26 | */ |
27 | #include "avformat.h" | |
6cac3a3b | 28 | #include "flv.h" |
d4f5d74a | 29 | |
d4f5d74a GM |
30 | static int flv_probe(AVProbeData *p) |
31 | { | |
32 | const uint8_t *d; | |
33 | ||
34 | if (p->buf_size < 6) | |
35 | return 0; | |
36 | d = p->buf; | |
37 | if (d[0] == 'F' && d[1] == 'L' && d[2] == 'V') { | |
38 | return 50; | |
39 | } | |
40 | return 0; | |
41 | } | |
42 | ||
896bcd2e | 43 | static int amf_get_string(ByteIOContext *ioc, char *buffer, int buffsize) { |
759dd138 | 44 | int length = get_be16(ioc); |
896bcd2e MN |
45 | if(length >= buffsize) { |
46 | url_fskip(ioc, length); | |
759dd138 | 47 | return -1; |
896bcd2e MN |
48 | } |
49 | ||
50 | get_buffer(ioc, buffer, length); | |
51 | ||
52 | buffer[length] = '\0'; | |
53 | ||
54 | return length; | |
55 | } | |
56 | ||
d4f5d74a GM |
57 | static int flv_read_header(AVFormatContext *s, |
58 | AVFormatParameters *ap) | |
59 | { | |
15f14fc7 | 60 | int offset, flags; |
4eb0c665 | 61 | AVStream *st; |
d4f5d74a | 62 | |
d4f5d74a GM |
63 | url_fskip(&s->pb, 4); |
64 | flags = get_byte(&s->pb); | |
65 | ||
4eb0c665 MN |
66 | if(flags & FLV_HEADER_FLAG_HASVIDEO){ |
67 | st = av_new_stream(s, 0); | |
68 | if (!st) | |
69 | return AVERROR_NOMEM; | |
70 | st->codec->codec_type = CODEC_TYPE_VIDEO; | |
71 | av_set_pts_info(st, 24, 1, 1000); /* 24 bit pts in ms */ | |
72 | } | |
73 | if(flags & FLV_HEADER_FLAG_HASAUDIO){ | |
74 | st = av_new_stream(s, 1); | |
75 | if (!st) | |
76 | return AVERROR_NOMEM; | |
77 | st->codec->codec_type = CODEC_TYPE_AUDIO; | |
78 | av_set_pts_info(st, 24, 1, 1000); /* 24 bit pts in ms */ | |
79 | } | |
80 | ||
d4f5d74a GM |
81 | offset = get_be32(&s->pb); |
82 | url_fseek(&s->pb, offset, SEEK_SET); | |
83 | ||
aeb20f7f N |
84 | s->start_time = 0; |
85 | ||
d4f5d74a GM |
86 | return 0; |
87 | } | |
88 | ||
89 | static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) | |
90 | { | |
aeb20f7f | 91 | int ret, i, type, size, pts, flags, is_audio, next, pos; |
923bd441 | 92 | AVStream *st = NULL; |
115329f1 | 93 | |
068f2a22 | 94 | for(;;){ |
aeb20f7f | 95 | pos = url_ftell(&s->pb); |
d4f5d74a GM |
96 | url_fskip(&s->pb, 4); /* size of previous packet */ |
97 | type = get_byte(&s->pb); | |
98 | size = get_be24(&s->pb); | |
99 | pts = get_be24(&s->pb); | |
92a26775 | 100 | // av_log(s, AV_LOG_DEBUG, "type:%d, size:%d, pts:%d\n", type, size, pts); |
d4f5d74a | 101 | if (url_feof(&s->pb)) |
0bd586c5 | 102 | return AVERROR_IO; |
d4f5d74a GM |
103 | url_fskip(&s->pb, 4); /* reserved */ |
104 | flags = 0; | |
115329f1 | 105 | |
068f2a22 MN |
106 | if(size == 0) |
107 | continue; | |
115329f1 | 108 | |
dd9f5916 MN |
109 | next= size + url_ftell(&s->pb); |
110 | ||
6cac3a3b | 111 | if (type == FLV_TAG_TYPE_AUDIO) { |
068f2a22 | 112 | is_audio=1; |
d4f5d74a | 113 | flags = get_byte(&s->pb); |
6cac3a3b | 114 | } else if (type == FLV_TAG_TYPE_VIDEO) { |
068f2a22 | 115 | is_audio=0; |
d4f5d74a | 116 | flags = get_byte(&s->pb); |
6cac3a3b | 117 | } else if (type == FLV_TAG_TYPE_META && size > 13+1+4) { |
dd9f5916 MN |
118 | url_fskip(&s->pb, 13); //onMetaData blah |
119 | if(get_byte(&s->pb) == 8){ | |
120 | url_fskip(&s->pb, 4); | |
121 | } | |
122 | while(url_ftell(&s->pb) + 5 < next){ | |
123 | char tmp[128]; | |
124 | int type, len; | |
125 | double d= 0; | |
115329f1 | 126 | |
17fcb170 | 127 | if(amf_get_string(&s->pb, tmp, sizeof(tmp))<0) |
dd9f5916 | 128 | break; |
115329f1 | 129 | |
dd9f5916 | 130 | type= get_byte(&s->pb); |
6cac3a3b | 131 | if(type == AMF_DATA_TYPE_NUMBER){ |
dd9f5916 | 132 | d= av_int2dbl(get_be64(&s->pb)); |
6cac3a3b | 133 | }else if(type == AMF_DATA_TYPE_STRING){ |
17fcb170 | 134 | amf_get_string(&s->pb, NULL, 0); |
6cac3a3b | 135 | }else if(type == AMF_DATA_TYPE_MIXEDARRAY){ |
dd9f5916 MN |
136 | //array |
137 | break; | |
6cac3a3b | 138 | }else if(type == AMF_DATA_TYPE_DATE){ |
dd9f5916 MN |
139 | d= av_int2dbl(get_be64(&s->pb)); |
140 | get_be16(&s->pb); | |
141 | } | |
115329f1 | 142 | |
dd9f5916 MN |
143 | if(!strcmp(tmp, "duration")){ |
144 | s->duration = d*AV_TIME_BASE; | |
145 | }else if(!strcmp(tmp, "videodatarate")){ | |
146 | }else if(!strcmp(tmp, "audiodatarate")){ | |
147 | } | |
148 | } | |
149 | url_fseek(&s->pb, next, SEEK_SET); | |
150 | continue; | |
d4f5d74a | 151 | } else { |
d4f5d74a | 152 | /* skip packet */ |
bc874dae | 153 | av_log(s, AV_LOG_ERROR, "skipping flv packet: type %d, size %d, flags %d\n", type, size, flags); |
dd9f5916 | 154 | url_fseek(&s->pb, next, SEEK_SET); |
068f2a22 | 155 | continue; |
d4f5d74a GM |
156 | } |
157 | ||
158 | /* now find stream */ | |
159 | for(i=0;i<s->nb_streams;i++) { | |
160 | st = s->streams[i]; | |
068f2a22 MN |
161 | if (st->id == is_audio) |
162 | break; | |
d4f5d74a | 163 | } |
068f2a22 | 164 | if(i == s->nb_streams){ |
4eb0c665 MN |
165 | av_log(NULL, AV_LOG_ERROR, "invalid stream\n"); |
166 | url_fseek(&s->pb, next, SEEK_SET); | |
167 | continue; | |
6ed08157 | 168 | } |
f3356e9c | 169 | // av_log(NULL, AV_LOG_DEBUG, "%d %X %d \n", is_audio, flags, st->discard); |
6cac3a3b AH |
170 | if( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || is_audio)) |
171 | ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && !is_audio)) | |
f3356e9c MN |
172 | || st->discard >= AVDISCARD_ALL |
173 | ){ | |
dd9f5916 | 174 | url_fseek(&s->pb, next, SEEK_SET); |
b9866ebc MN |
175 | continue; |
176 | } | |
6cac3a3b | 177 | if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) |
aeb20f7f | 178 | av_add_index_entry(st, pos, pts, size, 0, AVINDEX_KEYFRAME); |
068f2a22 MN |
179 | break; |
180 | } | |
181 | ||
15f14fc7 MN |
182 | // if not streamed and no duration from metadata then seek to end to find the duration from the timestamps |
183 | if(!url_is_streamed(&s->pb) && s->duration==AV_NOPTS_VALUE){ | |
184 | int size; | |
185 | const int pos= url_ftell(&s->pb); | |
186 | const int fsize= url_fsize(&s->pb); | |
187 | url_fseek(&s->pb, fsize-4, SEEK_SET); | |
188 | size= get_be32(&s->pb); | |
189 | url_fseek(&s->pb, fsize-3-size, SEEK_SET); | |
190 | if(size == get_be24(&s->pb) + 11){ | |
191 | s->duration= get_be24(&s->pb) * (int64_t)AV_TIME_BASE / 1000; | |
192 | } | |
193 | url_fseek(&s->pb, pos, SEEK_SET); | |
194 | } | |
195 | ||
068f2a22 | 196 | if(is_audio){ |
01f4895c | 197 | if(st->codec->sample_rate == 0){ |
6cac3a3b AH |
198 | st->codec->channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1; |
199 | if((flags & FLV_AUDIO_CODECID_MASK) == FLV_CODECID_NELLYMOSER_8HZ_MONO) | |
01f4895c | 200 | st->codec->sample_rate= 8000; |
068f2a22 | 201 | else |
6cac3a3b AH |
202 | st->codec->sample_rate = (44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3); |
203 | switch(flags & FLV_AUDIO_CODECID_MASK) { | |
204 | case FLV_CODECID_PCM_BE: if (flags & FLV_AUDIO_SAMPLESIZE_MASK) st->codec->codec_id = CODEC_ID_PCM_S16BE; | |
bb270c08 | 205 | else st->codec->codec_id = CODEC_ID_PCM_S8; break; |
6cac3a3b AH |
206 | case FLV_CODECID_ADPCM : st->codec->codec_id = CODEC_ID_ADPCM_SWF; break; |
207 | case FLV_CODECID_MP3 : st->codec->codec_id = CODEC_ID_MP3 ; st->need_parsing = 1; break; | |
bb270c08 | 208 | // this is not listed at FLV but at SWF, strange... |
6cac3a3b | 209 | case FLV_CODECID_PCM_LE: if (flags & FLV_AUDIO_SAMPLESIZE_MASK) st->codec->codec_id = CODEC_ID_PCM_S16LE; |
bb270c08 | 210 | else st->codec->codec_id = CODEC_ID_PCM_S8; break; |
068f2a22 | 211 | default: |
bb270c08 | 212 | av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n", flags >> 4); |
01f4895c | 213 | st->codec->codec_tag= (flags >> 4); |
068f2a22 | 214 | } |
6cac3a3b | 215 | st->codec->bits_per_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8; |
068f2a22 MN |
216 | } |
217 | }else{ | |
6cac3a3b AH |
218 | switch(flags & FLV_VIDEO_CODECID_MASK){ |
219 | case FLV_CODECID_H263 : st->codec->codec_id = CODEC_ID_FLV1 ; break; | |
220 | case FLV_CODECID_SCREEN: st->codec->codec_id = CODEC_ID_FLASHSV; break; | |
221 | case FLV_CODECID_VP6 : | |
5ce117c3 | 222 | st->codec->codec_id = CODEC_ID_VP6F; |
9e2424ce AJ |
223 | if (st->codec->extradata_size != 1) { |
224 | st->codec->extradata_size = 1; | |
225 | st->codec->extradata = av_malloc(1); | |
226 | } | |
227 | /* width and height adjustment */ | |
228 | st->codec->extradata[0] = get_byte(&s->pb); | |
5ce117c3 AJ |
229 | size--; |
230 | break; | |
068f2a22 | 231 | default: |
6cac3a3b AH |
232 | av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flags & FLV_VIDEO_CODECID_MASK); |
233 | st->codec->codec_tag = flags & FLV_VIDEO_CODECID_MASK; | |
068f2a22 | 234 | } |
bb01a3f0 MN |
235 | } |
236 | ||
dd9f5916 | 237 | ret= av_get_packet(&s->pb, pkt, size - 1); |
d4f5d74a | 238 | if (ret <= 0) { |
0bd586c5 | 239 | return AVERROR_IO; |
d4f5d74a GM |
240 | } |
241 | /* note: we need to modify the packet size here to handle the last | |
242 | packet */ | |
243 | pkt->size = ret; | |
244 | pkt->pts = pts; | |
245 | pkt->stream_index = st->index; | |
115329f1 | 246 | |
6cac3a3b | 247 | if (is_audio || ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)) |
bb270c08 | 248 | pkt->flags |= PKT_FLAG_KEY; |
115329f1 | 249 | |
d4f5d74a GM |
250 | return ret; |
251 | } | |
252 | ||
253 | static int flv_read_close(AVFormatContext *s) | |
254 | { | |
255 | return 0; | |
256 | } | |
257 | ||
aeb20f7f N |
258 | static int flv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
259 | { | |
260 | AVStream *st = s->streams[stream_index]; | |
261 | int index = av_index_search_timestamp(st, timestamp, flags); | |
262 | if (index < 0) | |
263 | return -1; | |
264 | url_fseek(&s->pb, st->index_entries[index].pos, SEEK_SET); | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
d2a067d1 | 269 | AVInputFormat flv_demuxer = { |
d4f5d74a GM |
270 | "flv", |
271 | "flv format", | |
272 | 0, | |
273 | flv_probe, | |
274 | flv_read_header, | |
275 | flv_read_packet, | |
276 | flv_read_close, | |
aeb20f7f | 277 | flv_read_seek, |
d4f5d74a GM |
278 | .extensions = "flv", |
279 | .value = CODEC_ID_FLV1, | |
280 | }; |