/src/ffmpeg/libavformat/flic.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * FLI/FLC Animation File Demuxer |
3 | | * Copyright (c) 2003 The FFmpeg project |
4 | | * |
5 | | * This file is part of FFmpeg. |
6 | | * |
7 | | * FFmpeg is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * FFmpeg is distributed in the hope that it will be useful, |
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 |
18 | | * License along with FFmpeg; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | /** |
23 | | * @file |
24 | | * FLI/FLC file demuxer |
25 | | * by Mike Melanson (melanson@pcisys.net) |
26 | | * for more information on the .fli/.flc file format and all of its many |
27 | | * variations, visit: |
28 | | * http://www.compuphase.com/flic.htm |
29 | | * |
30 | | * This demuxer handles standard 0xAF11- and 0xAF12-type FLIs. It also handles |
31 | | * special FLIs from the PC games "Magic Carpet" and "X-COM: Terror from the Deep". |
32 | | */ |
33 | | |
34 | | #include "libavutil/channel_layout.h" |
35 | | #include "libavutil/intreadwrite.h" |
36 | | #include "avformat.h" |
37 | | #include "demux.h" |
38 | | #include "internal.h" |
39 | | |
40 | 220k | #define FLIC_FILE_MAGIC_1 0xAF11 |
41 | 220k | #define FLIC_FILE_MAGIC_2 0xAF12 |
42 | 220k | #define FLIC_FILE_MAGIC_3 0xAF44 /* Flic Type for Extended FLX Format which |
43 | | originated in Dave's Targa Animator (DTA) */ |
44 | 1.23M | #define FLIC_CHUNK_MAGIC_1 0xF1FA |
45 | 508k | #define FLIC_CHUNK_MAGIC_2 0xF5FA |
46 | 201 | #define FLIC_MC_SPEED 5 /* speed for Magic Carpet game FLIs */ |
47 | 65 | #define FLIC_DEFAULT_SPEED 5 /* for FLIs that have 0 speed */ |
48 | 509k | #define FLIC_TFTD_CHUNK_AUDIO 0xAAAA /* Audio chunk. Used in Terror from the Deep. |
49 | | Has 10 B extra header not accounted for in the chunk header */ |
50 | 1.62k | #define FLIC_TFTD_SAMPLE_RATE 22050 |
51 | | |
52 | 374k | #define FLIC_HEADER_SIZE 128 |
53 | 6.10M | #define FLIC_PREAMBLE_SIZE 6 |
54 | | |
55 | | typedef struct FlicDemuxContext { |
56 | | int video_stream_index; |
57 | | int audio_stream_index; |
58 | | int frame_number; |
59 | | } FlicDemuxContext; |
60 | | |
61 | | static int flic_probe(const AVProbeData *p) |
62 | 358k | { |
63 | 358k | int magic_number; |
64 | | |
65 | 358k | if(p->buf_size < FLIC_HEADER_SIZE) |
66 | 138k | return 0; |
67 | | |
68 | 220k | magic_number = AV_RL16(&p->buf[4]); |
69 | 220k | if ((magic_number != FLIC_FILE_MAGIC_1) && |
70 | 220k | (magic_number != FLIC_FILE_MAGIC_2) && |
71 | 220k | (magic_number != FLIC_FILE_MAGIC_3)) |
72 | 219k | return 0; |
73 | | |
74 | 683 | if(AV_RL16(&p->buf[0x10]) != FLIC_CHUNK_MAGIC_1){ |
75 | 680 | if(AV_RL32(&p->buf[0x10]) > 2000) |
76 | 345 | return 0; |
77 | 680 | } |
78 | | |
79 | 338 | if( AV_RL16(&p->buf[0x08]) > 4096 |
80 | 338 | || AV_RL16(&p->buf[0x0A]) > 4096) |
81 | 249 | return 0; |
82 | | |
83 | | |
84 | 89 | return AVPROBE_SCORE_MAX - 1; |
85 | 338 | } |
86 | | |
87 | | static int flic_read_header(AVFormatContext *s) |
88 | 6.14k | { |
89 | 6.14k | FlicDemuxContext *flic = s->priv_data; |
90 | 6.14k | AVIOContext *pb = s->pb; |
91 | 6.14k | unsigned char header[FLIC_HEADER_SIZE]; |
92 | 6.14k | AVStream *st, *ast; |
93 | 6.14k | int speed, ret; |
94 | 6.14k | int magic_number; |
95 | 6.14k | unsigned char preamble[FLIC_PREAMBLE_SIZE]; |
96 | | |
97 | 6.14k | flic->frame_number = 0; |
98 | | |
99 | | /* load the whole header and pull out the width and height */ |
100 | 6.14k | if (avio_read(pb, header, FLIC_HEADER_SIZE) != FLIC_HEADER_SIZE) |
101 | 4.56k | return AVERROR(EIO); |
102 | | |
103 | 1.58k | magic_number = AV_RL16(&header[4]); |
104 | 1.58k | speed = AV_RL32(&header[0x10]); |
105 | 1.58k | if (speed == 0) |
106 | 65 | speed = FLIC_DEFAULT_SPEED; |
107 | | |
108 | | /* initialize the decoder streams */ |
109 | 1.58k | st = avformat_new_stream(s, NULL); |
110 | 1.58k | if (!st) |
111 | 0 | return AVERROR(ENOMEM); |
112 | 1.58k | flic->video_stream_index = st->index; |
113 | 1.58k | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
114 | 1.58k | st->codecpar->codec_id = AV_CODEC_ID_FLIC; |
115 | 1.58k | st->codecpar->codec_tag = 0; /* no fourcc */ |
116 | 1.58k | st->codecpar->width = AV_RL16(&header[0x08]); |
117 | 1.58k | st->codecpar->height = AV_RL16(&header[0x0A]); |
118 | | |
119 | 1.58k | if (!st->codecpar->width || !st->codecpar->height) { |
120 | | /* Ugly hack needed for the following sample: */ |
121 | | /* http://samples.mplayerhq.hu/fli-flc/fli-bugs/specular.flc */ |
122 | 262 | av_log(s, AV_LOG_WARNING, |
123 | 262 | "File with no specified width/height. Trying 640x480.\n"); |
124 | 262 | st->codecpar->width = 640; |
125 | 262 | st->codecpar->height = 480; |
126 | 262 | } |
127 | | |
128 | | /* send over the whole 128-byte FLIC header */ |
129 | 1.58k | if ((ret = ff_alloc_extradata(st->codecpar, FLIC_HEADER_SIZE)) < 0) |
130 | 0 | return ret; |
131 | 1.58k | memcpy(st->codecpar->extradata, header, FLIC_HEADER_SIZE); |
132 | | |
133 | | /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */ |
134 | 1.58k | if (avio_read(pb, preamble, FLIC_PREAMBLE_SIZE) != FLIC_PREAMBLE_SIZE) { |
135 | 42 | av_log(s, AV_LOG_ERROR, "Failed to peek at preamble\n"); |
136 | 42 | return AVERROR(EIO); |
137 | 42 | } |
138 | | |
139 | 1.54k | avio_seek(pb, -FLIC_PREAMBLE_SIZE, SEEK_CUR); |
140 | | |
141 | | /* Time to figure out the framerate: |
142 | | * If the first preamble's magic number is 0xAAAA then this file is from |
143 | | * X-COM: Terror from the Deep. If on the other hand there is a FLIC chunk |
144 | | * magic number at offset 0x10 assume this file is from Magic Carpet instead. |
145 | | * If neither of the above is true then this is a normal FLIC file. |
146 | | */ |
147 | 1.54k | if (AV_RL16(&preamble[4]) == FLIC_TFTD_CHUNK_AUDIO) { |
148 | | /* TFTD videos have an extra 22050 Hz 8-bit mono audio stream */ |
149 | 541 | ast = avformat_new_stream(s, NULL); |
150 | 541 | if (!ast) |
151 | 0 | return AVERROR(ENOMEM); |
152 | | |
153 | 541 | flic->audio_stream_index = ast->index; |
154 | | |
155 | | /* all audio frames are the same size, so use the size of the first chunk for block_align */ |
156 | 541 | ast->codecpar->block_align = AV_RL32(&preamble[0]); |
157 | 541 | ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
158 | 541 | ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
159 | 541 | ast->codecpar->codec_tag = 0; |
160 | 541 | ast->codecpar->sample_rate = FLIC_TFTD_SAMPLE_RATE; |
161 | 541 | ast->codecpar->bit_rate = st->codecpar->sample_rate * 8; |
162 | 541 | ast->codecpar->bits_per_coded_sample = 8; |
163 | 541 | ast->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; |
164 | 541 | ast->codecpar->extradata_size = 0; |
165 | | |
166 | | /* Since the header information is incorrect we have to figure out the |
167 | | * framerate using block_align and the fact that the audio is 22050 Hz. |
168 | | * We usually have two cases: 2205 -> 10 fps and 1470 -> 15 fps */ |
169 | 541 | avpriv_set_pts_info(st, 64, ast->codecpar->block_align, FLIC_TFTD_SAMPLE_RATE); |
170 | 541 | avpriv_set_pts_info(ast, 64, 1, FLIC_TFTD_SAMPLE_RATE); |
171 | 1.00k | } else if (AV_RL16(&header[0x10]) == FLIC_CHUNK_MAGIC_1) { |
172 | 201 | avpriv_set_pts_info(st, 64, FLIC_MC_SPEED, 70); |
173 | | |
174 | | /* rewind the stream since the first chunk is at offset 12 */ |
175 | 201 | avio_seek(pb, 12, SEEK_SET); |
176 | | |
177 | | /* send over abbreviated FLIC header chunk */ |
178 | 201 | if ((ret = ff_alloc_extradata(st->codecpar, 12)) < 0) |
179 | 0 | return ret; |
180 | 201 | memcpy(st->codecpar->extradata, header, 12); |
181 | | |
182 | 800 | } else if (magic_number == FLIC_FILE_MAGIC_1) { |
183 | 145 | avpriv_set_pts_info(st, 64, speed, 70); |
184 | 655 | } else if ((magic_number == FLIC_FILE_MAGIC_2) || |
185 | 655 | (magic_number == FLIC_FILE_MAGIC_3)) { |
186 | 526 | avpriv_set_pts_info(st, 64, speed, 1000); |
187 | 526 | } else { |
188 | 129 | av_log(s, AV_LOG_ERROR, "Invalid or unsupported magic chunk in file\n"); |
189 | 129 | return AVERROR_INVALIDDATA; |
190 | 129 | } |
191 | | |
192 | 1.41k | return 0; |
193 | 1.54k | } |
194 | | |
195 | | static int flic_read_packet(AVFormatContext *s, |
196 | | AVPacket *pkt) |
197 | 976k | { |
198 | 976k | FlicDemuxContext *flic = s->priv_data; |
199 | 976k | AVIOContext *pb = s->pb; |
200 | 976k | int packet_read = 0; |
201 | 976k | unsigned int size; |
202 | 976k | int magic; |
203 | 976k | int ret = 0; |
204 | 976k | unsigned char preamble[FLIC_PREAMBLE_SIZE]; |
205 | 976k | int64_t pos = avio_tell(pb); |
206 | | |
207 | 2.21M | while (!packet_read && !avio_feof(pb)) { |
208 | | |
209 | 1.23M | if ((ret = avio_read(pb, preamble, FLIC_PREAMBLE_SIZE)) != |
210 | 1.23M | FLIC_PREAMBLE_SIZE) { |
211 | 992 | ret = AVERROR(EIO); |
212 | 992 | break; |
213 | 992 | } |
214 | | |
215 | 1.23M | size = AV_RL32(&preamble[0]); |
216 | 1.23M | magic = AV_RL16(&preamble[4]); |
217 | | |
218 | 1.23M | if (((magic == FLIC_CHUNK_MAGIC_1) || (magic == FLIC_CHUNK_MAGIC_2)) && size > FLIC_PREAMBLE_SIZE) { |
219 | 725k | if ((ret = av_new_packet(pkt, size)) < 0) |
220 | 63 | return ret; |
221 | | |
222 | 725k | pkt->stream_index = flic->video_stream_index; |
223 | 725k | pkt->pos = pos; |
224 | 725k | memcpy(pkt->data, preamble, FLIC_PREAMBLE_SIZE); |
225 | 725k | ret = avio_read(pb, pkt->data + FLIC_PREAMBLE_SIZE, |
226 | 725k | size - FLIC_PREAMBLE_SIZE); |
227 | 725k | if (ret != size - FLIC_PREAMBLE_SIZE) { |
228 | 81 | ret = AVERROR(EIO); |
229 | 81 | } |
230 | 725k | pkt->flags = flic->frame_number == 0 ? AV_PKT_FLAG_KEY : 0; |
231 | 725k | pkt->pts = flic->frame_number; |
232 | 725k | if (flic->frame_number == 0) |
233 | 915 | av_add_index_entry(s->streams[flic->video_stream_index], pkt->pos, pkt->pts, pkt->size, 0, AVINDEX_KEYFRAME); |
234 | 725k | packet_read = 1; |
235 | 725k | flic->frame_number++; |
236 | 725k | } else if (magic == FLIC_TFTD_CHUNK_AUDIO) { |
237 | 248k | if ((ret = av_new_packet(pkt, size)) < 0) |
238 | 89 | return ret; |
239 | | |
240 | | /* skip useless 10B sub-header (yes, it's not accounted for in the chunk header) */ |
241 | 248k | avio_skip(pb, 10); |
242 | | |
243 | 248k | pkt->stream_index = flic->audio_stream_index; |
244 | 248k | pkt->pos = pos; |
245 | 248k | pkt->flags = AV_PKT_FLAG_KEY; |
246 | 248k | ret = avio_read(pb, pkt->data, size); |
247 | | |
248 | 248k | if (ret != size) { |
249 | 82 | ret = AVERROR(EIO); |
250 | 82 | break; |
251 | 82 | } |
252 | | |
253 | 248k | packet_read = 1; |
254 | 259k | } else { |
255 | | /* not interested in this chunk */ |
256 | 259k | avio_skip(pb, size - 6); |
257 | 259k | } |
258 | 1.23M | } |
259 | | |
260 | 976k | return avio_feof(pb) ? AVERROR_EOF : ret; |
261 | 976k | } |
262 | | |
263 | | static int flic_read_seek(AVFormatContext *s, int stream_index, |
264 | | int64_t pts, int flags) |
265 | 0 | { |
266 | 0 | FlicDemuxContext *flic = s->priv_data; |
267 | 0 | AVStream *st = s->streams[stream_index]; |
268 | 0 | FFStream *const sti = ffstream(st); |
269 | 0 | int64_t pos, ts; |
270 | 0 | int index; |
271 | |
|
272 | 0 | if (!sti->index_entries || stream_index != flic->video_stream_index) |
273 | 0 | return -1; |
274 | | |
275 | 0 | index = av_index_search_timestamp(st, pts, flags); |
276 | |
|
277 | 0 | if (index < 0) |
278 | 0 | index = av_index_search_timestamp(st, pts, flags ^ AVSEEK_FLAG_BACKWARD); |
279 | 0 | if (index < 0) |
280 | 0 | return -1; |
281 | | |
282 | 0 | pos = sti->index_entries[index].pos; |
283 | 0 | ts = sti->index_entries[index].timestamp; |
284 | 0 | flic->frame_number = ts; |
285 | 0 | avio_seek(s->pb, pos, SEEK_SET); |
286 | 0 | return 0; |
287 | 0 | } |
288 | | |
289 | | const FFInputFormat ff_flic_demuxer = { |
290 | | .p.name = "flic", |
291 | | .p.long_name = NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation"), |
292 | | .priv_data_size = sizeof(FlicDemuxContext), |
293 | | .read_probe = flic_probe, |
294 | | .read_header = flic_read_header, |
295 | | .read_packet = flic_read_packet, |
296 | | .read_seek = flic_read_seek, |
297 | | }; |