Mercurial > libavcodec.hg
annotate flicvideo.c @ 2892:41315d0120b3 libavcodec
replace a few mov + psrlq with pshufw, there are more cases which could benefit from this but they would require us to duplicate some functions ...
the trick is from various places (my own code in libpostproc, a patch on the x264 list, ...)
author | michael |
---|---|
date | Wed, 21 Sep 2005 21:17:09 +0000 |
parents | faa53103dde0 |
children | f7114e03d8dd |
rev | line source |
---|---|
1624 | 1 /* |
2 * FLI/FLC Animation Video Decoder | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
3 * Copyright (C) 2003, 2004 the ffmpeg project |
1624 | 4 * |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 * | |
19 */ | |
20 | |
21 /** | |
22 * @file flic.c | |
23 * Autodesk Animator FLI/FLC Video Decoder | |
24 * by Mike Melanson (melanson@pcisys.net) | |
25 * for more information on the .fli/.flc file format and all of its many | |
26 * variations, visit: | |
27 * http://www.compuphase.com/flic.htm | |
28 * | |
29 * This decoder outputs PAL8 colorspace data. To use this decoder, be | |
30 * sure that your demuxer sends the FLI file header to the decoder via | |
31 * the extradata chunk in AVCodecContext. The chunk should be 128 bytes | |
32 * large. The only exception is for FLI files from the game "Magic Carpet", | |
33 * in which the header is only 12 bytes. | |
34 */ | |
35 | |
36 #include <stdio.h> | |
37 #include <stdlib.h> | |
38 #include <string.h> | |
39 #include <unistd.h> | |
40 | |
41 #include "common.h" | |
42 #include "avcodec.h" | |
43 #include "bswap.h" | |
44 | |
45 #define FLI_256_COLOR 4 | |
46 #define FLI_DELTA 7 | |
47 #define FLI_COLOR 11 | |
48 #define FLI_LC 12 | |
49 #define FLI_BLACK 13 | |
50 #define FLI_BRUN 15 | |
51 #define FLI_COPY 16 | |
52 #define FLI_MINI 18 | |
53 | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
54 #define CHECK_PIXEL_PTR(n) \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
55 if (pixel_ptr + n > pixel_limit) { \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
56 av_log (s->avctx, AV_LOG_INFO, "Problem: pixel_ptr >= pixel_limit (%d >= %d)\n", \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
57 pixel_ptr + n, pixel_limit); \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
58 return -1; \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
59 } \ |
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
60 |
1624 | 61 typedef struct FlicDecodeContext { |
62 AVCodecContext *avctx; | |
63 AVFrame frame; | |
64 | |
65 unsigned int palette[256]; | |
66 int new_palette; | |
67 int fli_type; /* either 0xAF11 or 0xAF12, affects palette resolution */ | |
68 } FlicDecodeContext; | |
69 | |
70 static int flic_decode_init(AVCodecContext *avctx) | |
71 { | |
72 FlicDecodeContext *s = (FlicDecodeContext *)avctx->priv_data; | |
73 unsigned char *fli_header = (unsigned char *)avctx->extradata; | |
74 | |
75 s->avctx = avctx; | |
76 avctx->pix_fmt = PIX_FMT_PAL8; | |
77 avctx->has_b_frames = 0; | |
78 | |
79 if (s->avctx->extradata_size == 12) { | |
80 /* special case for magic carpet FLIs */ | |
81 s->fli_type = 0xAF13; | |
82 } else if (s->avctx->extradata_size == 128) { | |
83 s->fli_type = LE_16(&fli_header[4]); | |
84 } else { | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
85 av_log(avctx, AV_LOG_ERROR, "Expected extradata of 12 or 128 bytes\n"); |
1624 | 86 return -1; |
87 } | |
88 | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
89 s->frame.data[0] = NULL; |
1624 | 90 s->new_palette = 0; |
91 | |
92 return 0; | |
93 } | |
94 | |
95 static int flic_decode_frame(AVCodecContext *avctx, | |
96 void *data, int *data_size, | |
97 uint8_t *buf, int buf_size) | |
98 { | |
99 FlicDecodeContext *s = (FlicDecodeContext *)avctx->priv_data; | |
100 | |
101 int stream_ptr = 0; | |
102 int stream_ptr_after_color_chunk; | |
103 int pixel_ptr; | |
104 int palette_ptr; | |
105 unsigned char palette_idx1; | |
106 unsigned char palette_idx2; | |
107 | |
108 unsigned int frame_size; | |
109 int num_chunks; | |
110 | |
111 unsigned int chunk_size; | |
112 int chunk_type; | |
113 | |
114 int i, j; | |
115 | |
116 int color_packets; | |
117 int color_changes; | |
118 int color_shift; | |
119 unsigned char r, g, b; | |
120 | |
121 int lines; | |
122 int compressed_lines; | |
123 int starting_line; | |
124 signed short line_packets; | |
125 int y_ptr; | |
126 signed char byte_run; | |
127 int pixel_skip; | |
128 int pixel_countdown; | |
129 unsigned char *pixels; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
130 int pixel_limit; |
1624 | 131 |
132 s->frame.reference = 1; | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
133 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
134 if (avctx->reget_buffer(avctx, &s->frame) < 0) { |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
135 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); |
1624 | 136 return -1; |
137 } | |
138 | |
139 pixels = s->frame.data[0]; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
140 pixel_limit = s->avctx->height * s->frame.linesize[0]; |
1624 | 141 |
142 frame_size = LE_32(&buf[stream_ptr]); | |
143 stream_ptr += 6; /* skip the magic number */ | |
144 num_chunks = LE_16(&buf[stream_ptr]); | |
145 stream_ptr += 10; /* skip padding */ | |
146 | |
147 frame_size -= 16; | |
148 | |
149 /* iterate through the chunks */ | |
150 while ((frame_size > 0) && (num_chunks > 0)) { | |
151 chunk_size = LE_32(&buf[stream_ptr]); | |
152 stream_ptr += 4; | |
153 chunk_type = LE_16(&buf[stream_ptr]); | |
154 stream_ptr += 2; | |
155 | |
156 switch (chunk_type) { | |
157 case FLI_256_COLOR: | |
158 case FLI_COLOR: | |
159 stream_ptr_after_color_chunk = stream_ptr + chunk_size - 6; | |
160 s->new_palette = 1; | |
161 | |
162 /* check special case: If this file is from the Magic Carpet | |
163 * game and uses 6-bit colors even though it reports 256-color | |
164 * chunks in a 0xAF12-type file (fli_type is set to 0xAF13 during | |
165 * initialization) */ | |
166 if ((chunk_type == FLI_256_COLOR) && (s->fli_type != 0xAF13)) | |
167 color_shift = 0; | |
168 else | |
169 color_shift = 2; | |
170 /* set up the palette */ | |
171 color_packets = LE_16(&buf[stream_ptr]); | |
172 stream_ptr += 2; | |
173 palette_ptr = 0; | |
174 for (i = 0; i < color_packets; i++) { | |
175 /* first byte is how many colors to skip */ | |
176 palette_ptr += buf[stream_ptr++]; | |
177 | |
178 /* next byte indicates how many entries to change */ | |
179 color_changes = buf[stream_ptr++]; | |
180 | |
181 /* if there are 0 color changes, there are actually 256 */ | |
182 if (color_changes == 0) | |
183 color_changes = 256; | |
184 | |
185 for (j = 0; j < color_changes; j++) { | |
186 | |
187 /* wrap around, for good measure */ | |
2422 | 188 if ((unsigned)palette_ptr >= 256) |
1624 | 189 palette_ptr = 0; |
190 | |
191 r = buf[stream_ptr++] << color_shift; | |
192 g = buf[stream_ptr++] << color_shift; | |
193 b = buf[stream_ptr++] << color_shift; | |
194 s->palette[palette_ptr++] = (r << 16) | (g << 8) | b; | |
195 } | |
196 } | |
197 | |
198 /* color chunks sometimes have weird 16-bit alignment issues; | |
199 * therefore, take the hardline approach and set the stream_ptr | |
200 * to the value calculated w.r.t. the size specified by the color | |
201 * chunk header */ | |
202 stream_ptr = stream_ptr_after_color_chunk; | |
203 | |
204 break; | |
205 | |
206 case FLI_DELTA: | |
207 y_ptr = 0; | |
208 compressed_lines = LE_16(&buf[stream_ptr]); | |
209 stream_ptr += 2; | |
210 while (compressed_lines > 0) { | |
211 line_packets = LE_16(&buf[stream_ptr]); | |
212 stream_ptr += 2; | |
213 if (line_packets < 0) { | |
214 line_packets = -line_packets; | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
215 y_ptr += line_packets * s->frame.linesize[0]; |
1624 | 216 } else { |
217 compressed_lines--; | |
218 pixel_ptr = y_ptr; | |
219 pixel_countdown = s->avctx->width; | |
220 for (i = 0; i < line_packets; i++) { | |
221 /* account for the skip bytes */ | |
222 pixel_skip = buf[stream_ptr++]; | |
223 pixel_ptr += pixel_skip; | |
224 pixel_countdown -= pixel_skip; | |
225 byte_run = buf[stream_ptr++]; | |
226 if (byte_run < 0) { | |
227 byte_run = -byte_run; | |
228 palette_idx1 = buf[stream_ptr++]; | |
229 palette_idx2 = buf[stream_ptr++]; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
230 CHECK_PIXEL_PTR(byte_run); |
1624 | 231 for (j = 0; j < byte_run; j++, pixel_countdown -= 2) { |
232 pixels[pixel_ptr++] = palette_idx1; | |
233 pixels[pixel_ptr++] = palette_idx2; | |
234 } | |
235 } else { | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
236 CHECK_PIXEL_PTR(byte_run * 2); |
1624 | 237 for (j = 0; j < byte_run * 2; j++, pixel_countdown--) { |
238 palette_idx1 = buf[stream_ptr++]; | |
239 pixels[pixel_ptr++] = palette_idx1; | |
240 } | |
241 } | |
242 } | |
243 | |
244 y_ptr += s->frame.linesize[0]; | |
245 } | |
246 } | |
247 break; | |
248 | |
249 case FLI_LC: | |
250 /* line compressed */ | |
251 starting_line = LE_16(&buf[stream_ptr]); | |
252 stream_ptr += 2; | |
253 y_ptr = 0; | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
254 y_ptr += starting_line * s->frame.linesize[0]; |
1624 | 255 |
256 compressed_lines = LE_16(&buf[stream_ptr]); | |
257 stream_ptr += 2; | |
258 while (compressed_lines > 0) { | |
259 pixel_ptr = y_ptr; | |
260 pixel_countdown = s->avctx->width; | |
261 line_packets = buf[stream_ptr++]; | |
262 if (line_packets > 0) { | |
263 for (i = 0; i < line_packets; i++) { | |
264 /* account for the skip bytes */ | |
265 pixel_skip = buf[stream_ptr++]; | |
266 pixel_ptr += pixel_skip; | |
267 pixel_countdown -= pixel_skip; | |
268 byte_run = buf[stream_ptr++]; | |
269 if (byte_run > 0) { | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
270 CHECK_PIXEL_PTR(byte_run); |
1624 | 271 for (j = 0; j < byte_run; j++, pixel_countdown--) { |
272 palette_idx1 = buf[stream_ptr++]; | |
273 pixels[pixel_ptr++] = palette_idx1; | |
274 } | |
275 } else { | |
276 byte_run = -byte_run; | |
277 palette_idx1 = buf[stream_ptr++]; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
278 CHECK_PIXEL_PTR(byte_run); |
1624 | 279 for (j = 0; j < byte_run; j++, pixel_countdown--) { |
280 pixels[pixel_ptr++] = palette_idx1; | |
281 } | |
282 } | |
283 } | |
284 } | |
285 | |
286 y_ptr += s->frame.linesize[0]; | |
287 compressed_lines--; | |
288 } | |
289 break; | |
290 | |
291 case FLI_BLACK: | |
292 /* set the whole frame to color 0 (which is usually black) */ | |
293 memset(pixels, 0, | |
294 s->frame.linesize[0] * s->avctx->height); | |
295 break; | |
296 | |
297 case FLI_BRUN: | |
298 /* Byte run compression: This chunk type only occurs in the first | |
299 * FLI frame and it will update the entire frame. */ | |
300 y_ptr = 0; | |
301 for (lines = 0; lines < s->avctx->height; lines++) { | |
302 pixel_ptr = y_ptr; | |
303 /* disregard the line packets; instead, iterate through all | |
304 * pixels on a row */ | |
305 stream_ptr++; | |
306 pixel_countdown = s->avctx->width; | |
307 while (pixel_countdown > 0) { | |
308 byte_run = buf[stream_ptr++]; | |
309 if (byte_run > 0) { | |
310 palette_idx1 = buf[stream_ptr++]; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
311 CHECK_PIXEL_PTR(byte_run); |
1624 | 312 for (j = 0; j < byte_run; j++) { |
313 pixels[pixel_ptr++] = palette_idx1; | |
314 pixel_countdown--; | |
315 if (pixel_countdown < 0) | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
316 av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d)\n", |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
317 pixel_countdown); |
1624 | 318 } |
319 } else { /* copy bytes if byte_run < 0 */ | |
320 byte_run = -byte_run; | |
2825
faa53103dde0
tinfoil patch: make sure that pixel pointer does not go out of bounds
melanson
parents:
2422
diff
changeset
|
321 CHECK_PIXEL_PTR(byte_run); |
1624 | 322 for (j = 0; j < byte_run; j++) { |
323 palette_idx1 = buf[stream_ptr++]; | |
324 pixels[pixel_ptr++] = palette_idx1; | |
325 pixel_countdown--; | |
326 if (pixel_countdown < 0) | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
327 av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d)\n", |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
328 pixel_countdown); |
1624 | 329 } |
330 } | |
331 } | |
332 | |
333 y_ptr += s->frame.linesize[0]; | |
334 } | |
335 break; | |
336 | |
337 case FLI_COPY: | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
338 /* copy the chunk (uncompressed frame) */ |
1624 | 339 if (chunk_size - 6 > s->avctx->width * s->avctx->height) { |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
340 av_log(avctx, AV_LOG_ERROR, "In chunk FLI_COPY : source data (%d bytes) " \ |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
341 "bigger than image, skipping chunk\n", chunk_size - 6); |
1624 | 342 stream_ptr += chunk_size - 6; |
343 } else { | |
344 for (y_ptr = 0; y_ptr < s->frame.linesize[0] * s->avctx->height; | |
345 y_ptr += s->frame.linesize[0]) { | |
346 memcpy(&pixels[y_ptr], &buf[stream_ptr], | |
347 s->avctx->width); | |
348 stream_ptr += s->avctx->width; | |
349 } | |
350 } | |
351 break; | |
352 | |
353 case FLI_MINI: | |
354 /* some sort of a thumbnail? disregard this chunk... */ | |
355 stream_ptr += chunk_size - 6; | |
356 break; | |
357 | |
358 default: | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
359 av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n", chunk_type); |
1624 | 360 break; |
361 } | |
362 | |
363 frame_size -= chunk_size; | |
364 num_chunks--; | |
365 } | |
366 | |
367 /* by the end of the chunk, the stream ptr should equal the frame | |
368 * size (minus 1, possibly); if it doesn't, issue a warning */ | |
369 if ((stream_ptr != buf_size) && (stream_ptr != buf_size - 1)) | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
370 av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d " \ |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
371 "and final chunk ptr = %d\n", buf_size, stream_ptr); |
1624 | 372 |
373 /* make the palette available on the way out */ | |
374 // if (s->new_palette) { | |
375 if (1) { | |
376 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); | |
377 s->frame.palette_has_changed = 1; | |
378 s->new_palette = 0; | |
379 } | |
380 | |
381 *data_size=sizeof(AVFrame); | |
382 *(AVFrame*)data = s->frame; | |
383 | |
384 return buf_size; | |
385 } | |
386 | |
387 static int flic_decode_end(AVCodecContext *avctx) | |
388 { | |
389 FlicDecodeContext *s = avctx->priv_data; | |
390 | |
1759
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
391 if (s->frame.data[0]) |
e73a3b86565f
Use reget buffer instead of copying from prev frame
rtognimp
parents:
1624
diff
changeset
|
392 avctx->release_buffer(avctx, &s->frame); |
1624 | 393 |
394 return 0; | |
395 } | |
396 | |
397 AVCodec flic_decoder = { | |
398 "flic", | |
399 CODEC_TYPE_VIDEO, | |
400 CODEC_ID_FLIC, | |
401 sizeof(FlicDecodeContext), | |
402 flic_decode_init, | |
403 NULL, | |
404 flic_decode_end, | |
405 flic_decode_frame, | |
406 CODEC_CAP_DR1, | |
407 NULL | |
408 }; |