ffmpeg / libavformat / gifdec.c @ 5509bffa
History | View | Annotate | Download (16.6 KB)
1 |
/*
|
---|---|
2 |
* GIF decoder
|
3 |
* Copyright (c) 2003 Fabrice Bellard.
|
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18 |
*/
|
19 |
#include "avformat.h" |
20 |
|
21 |
int gif_write(ByteIOContext *pb, AVImageInfo *info);
|
22 |
|
23 |
//#define DEBUG
|
24 |
|
25 |
#define MAXBITS 12 |
26 |
#define SIZTABLE (1<<MAXBITS) |
27 |
|
28 |
#define GCE_DISPOSAL_NONE 0 |
29 |
#define GCE_DISPOSAL_INPLACE 1 |
30 |
#define GCE_DISPOSAL_BACKGROUND 2 |
31 |
#define GCE_DISPOSAL_RESTORE 3 |
32 |
|
33 |
typedef struct GifState { |
34 |
int screen_width;
|
35 |
int screen_height;
|
36 |
int bits_per_pixel;
|
37 |
int background_color_index;
|
38 |
int transparent_color_index;
|
39 |
int color_resolution;
|
40 |
uint8_t *image_buf; |
41 |
int image_linesize;
|
42 |
uint32_t *image_palette; |
43 |
int pix_fmt;
|
44 |
|
45 |
/* after the frame is displayed, the disposal method is used */
|
46 |
int gce_disposal;
|
47 |
/* delay during which the frame is shown */
|
48 |
int gce_delay;
|
49 |
|
50 |
/* LZW compatible decoder */
|
51 |
ByteIOContext *f; |
52 |
int eob_reached;
|
53 |
uint8_t *pbuf, *ebuf; |
54 |
int bbits;
|
55 |
unsigned int bbuf; |
56 |
|
57 |
int cursize; /* The current code size */ |
58 |
int curmask;
|
59 |
int codesize;
|
60 |
int clear_code;
|
61 |
int end_code;
|
62 |
int newcodes; /* First available code */ |
63 |
int top_slot; /* Highest code for current size */ |
64 |
int slot; /* Last read code */ |
65 |
int fc, oc;
|
66 |
uint8_t *sp; |
67 |
uint8_t stack[SIZTABLE]; |
68 |
uint8_t suffix[SIZTABLE]; |
69 |
uint16_t prefix[SIZTABLE]; |
70 |
|
71 |
/* aux buffers */
|
72 |
uint8_t global_palette[256 * 3]; |
73 |
uint8_t local_palette[256 * 3]; |
74 |
uint8_t buf[256];
|
75 |
} GifState; |
76 |
|
77 |
|
78 |
static const uint8_t gif87a_sig[6] = "GIF87a"; |
79 |
static const uint8_t gif89a_sig[6] = "GIF89a"; |
80 |
|
81 |
static const uint16_t mask[17] = |
82 |
{ |
83 |
0x0000, 0x0001, 0x0003, 0x0007, |
84 |
0x000F, 0x001F, 0x003F, 0x007F, |
85 |
0x00FF, 0x01FF, 0x03FF, 0x07FF, |
86 |
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF |
87 |
}; |
88 |
|
89 |
/* Probe gif video format or gif image format. The current heuristic
|
90 |
supposes the gif87a is always a single image. For gif89a, we
|
91 |
consider it as a video only if a GCE extension is present in the
|
92 |
first kilobyte. */
|
93 |
static int gif_video_probe(AVProbeData * pd) |
94 |
{ |
95 |
const uint8_t *p, *p_end;
|
96 |
int bits_per_pixel, has_global_palette, ext_code, ext_len;
|
97 |
int gce_flags, gce_disposal;
|
98 |
|
99 |
if (pd->buf_size < 24 || |
100 |
memcmp(pd->buf, gif89a_sig, 6) != 0) |
101 |
return 0; |
102 |
p_end = pd->buf + pd->buf_size; |
103 |
p = pd->buf + 6;
|
104 |
bits_per_pixel = (p[4] & 0x07) + 1; |
105 |
has_global_palette = (p[4] & 0x80); |
106 |
p += 7;
|
107 |
if (has_global_palette)
|
108 |
p += (1 << bits_per_pixel) * 3; |
109 |
for(;;) {
|
110 |
if (p >= p_end)
|
111 |
return 0; |
112 |
if (*p != '!') |
113 |
break;
|
114 |
p++; |
115 |
if (p >= p_end)
|
116 |
return 0; |
117 |
ext_code = *p++; |
118 |
if (p >= p_end)
|
119 |
return 0; |
120 |
ext_len = *p++; |
121 |
if (ext_code == 0xf9) { |
122 |
if (p >= p_end)
|
123 |
return 0; |
124 |
/* if GCE extension found with gce_disposal != 0: it is
|
125 |
likely to be an animation */
|
126 |
gce_flags = *p++; |
127 |
gce_disposal = (gce_flags >> 2) & 0x7; |
128 |
if (gce_disposal != 0) |
129 |
return AVPROBE_SCORE_MAX;
|
130 |
else
|
131 |
return 0; |
132 |
} |
133 |
for(;;) {
|
134 |
if (ext_len == 0) |
135 |
break;
|
136 |
p += ext_len; |
137 |
if (p >= p_end)
|
138 |
return 0; |
139 |
ext_len = *p++; |
140 |
} |
141 |
} |
142 |
return 0; |
143 |
} |
144 |
|
145 |
static int gif_image_probe(AVProbeData * pd) |
146 |
{ |
147 |
if (pd->buf_size >= 24 && |
148 |
(memcmp(pd->buf, gif87a_sig, 6) == 0 || |
149 |
memcmp(pd->buf, gif89a_sig, 6) == 0)) |
150 |
return AVPROBE_SCORE_MAX - 1; |
151 |
else
|
152 |
return 0; |
153 |
} |
154 |
|
155 |
|
156 |
static void GLZWDecodeInit(GifState * s, int csize) |
157 |
{ |
158 |
/* read buffer */
|
159 |
s->eob_reached = 0;
|
160 |
s->pbuf = s->buf; |
161 |
s->ebuf = s->buf; |
162 |
s->bbuf = 0;
|
163 |
s->bbits = 0;
|
164 |
|
165 |
/* decoder */
|
166 |
s->codesize = csize; |
167 |
s->cursize = s->codesize + 1;
|
168 |
s->curmask = mask[s->cursize]; |
169 |
s->top_slot = 1 << s->cursize;
|
170 |
s->clear_code = 1 << s->codesize;
|
171 |
s->end_code = s->clear_code + 1;
|
172 |
s->slot = s->newcodes = s->clear_code + 2;
|
173 |
s->oc = s->fc = 0;
|
174 |
s->sp = s->stack; |
175 |
} |
176 |
|
177 |
/* XXX: optimize */
|
178 |
static inline int GetCode(GifState * s) |
179 |
{ |
180 |
int c, sizbuf;
|
181 |
uint8_t *ptr; |
182 |
|
183 |
while (s->bbits < s->cursize) {
|
184 |
ptr = s->pbuf; |
185 |
if (ptr >= s->ebuf) {
|
186 |
if (!s->eob_reached) {
|
187 |
sizbuf = get_byte(s->f); |
188 |
s->ebuf = s->buf + sizbuf; |
189 |
s->pbuf = s->buf; |
190 |
if (sizbuf > 0) { |
191 |
get_buffer(s->f, s->buf, sizbuf); |
192 |
} else {
|
193 |
s->eob_reached = 1;
|
194 |
} |
195 |
} |
196 |
ptr = s->pbuf; |
197 |
} |
198 |
s->bbuf |= ptr[0] << s->bbits;
|
199 |
ptr++; |
200 |
s->pbuf = ptr; |
201 |
s->bbits += 8;
|
202 |
} |
203 |
c = s->bbuf & s->curmask; |
204 |
s->bbuf >>= s->cursize; |
205 |
s->bbits -= s->cursize; |
206 |
return c;
|
207 |
} |
208 |
|
209 |
/* NOTE: the algorithm here is inspired from the LZW GIF decoder
|
210 |
written by Steven A. Bennett in 1987. */
|
211 |
/* return the number of byte decoded */
|
212 |
static int GLZWDecode(GifState * s, uint8_t * buf, int len) |
213 |
{ |
214 |
int l, c, code, oc, fc;
|
215 |
uint8_t *sp; |
216 |
|
217 |
if (s->end_code < 0) |
218 |
return 0; |
219 |
|
220 |
l = len; |
221 |
sp = s->sp; |
222 |
oc = s->oc; |
223 |
fc = s->fc; |
224 |
|
225 |
while (sp > s->stack) {
|
226 |
*buf++ = *(--sp); |
227 |
if ((--l) == 0) |
228 |
goto the_end;
|
229 |
} |
230 |
|
231 |
for (;;) {
|
232 |
c = GetCode(s); |
233 |
if (c == s->end_code) {
|
234 |
s->end_code = -1;
|
235 |
break;
|
236 |
} else if (c == s->clear_code) { |
237 |
s->cursize = s->codesize + 1;
|
238 |
s->curmask = mask[s->cursize]; |
239 |
s->slot = s->newcodes; |
240 |
s->top_slot = 1 << s->cursize;
|
241 |
while ((c = GetCode(s)) == s->clear_code);
|
242 |
if (c == s->end_code) {
|
243 |
s->end_code = -1;
|
244 |
break;
|
245 |
} |
246 |
/* test error */
|
247 |
if (c >= s->slot)
|
248 |
c = 0;
|
249 |
fc = oc = c; |
250 |
*buf++ = c; |
251 |
if ((--l) == 0) |
252 |
break;
|
253 |
} else {
|
254 |
code = c; |
255 |
if (code >= s->slot) {
|
256 |
*sp++ = fc; |
257 |
code = oc; |
258 |
} |
259 |
while (code >= s->newcodes) {
|
260 |
*sp++ = s->suffix[code]; |
261 |
code = s->prefix[code]; |
262 |
} |
263 |
*sp++ = code; |
264 |
if (s->slot < s->top_slot) {
|
265 |
s->suffix[s->slot] = fc = code; |
266 |
s->prefix[s->slot++] = oc; |
267 |
oc = c; |
268 |
} |
269 |
if (s->slot >= s->top_slot) {
|
270 |
if (s->cursize < MAXBITS) {
|
271 |
s->top_slot <<= 1;
|
272 |
s->curmask = mask[++s->cursize]; |
273 |
} |
274 |
} |
275 |
while (sp > s->stack) {
|
276 |
*buf++ = *(--sp); |
277 |
if ((--l) == 0) |
278 |
goto the_end;
|
279 |
} |
280 |
} |
281 |
} |
282 |
the_end:
|
283 |
s->sp = sp; |
284 |
s->oc = oc; |
285 |
s->fc = fc; |
286 |
return len - l;
|
287 |
} |
288 |
|
289 |
static int gif_read_image(GifState *s) |
290 |
{ |
291 |
ByteIOContext *f = s->f; |
292 |
int left, top, width, height, bits_per_pixel, code_size, flags;
|
293 |
int is_interleaved, has_local_palette, y, x, pass, y1, linesize, n, i;
|
294 |
uint8_t *ptr, *line, *d, *spal, *palette, *sptr, *ptr1; |
295 |
|
296 |
left = get_le16(f); |
297 |
top = get_le16(f); |
298 |
width = get_le16(f); |
299 |
height = get_le16(f); |
300 |
flags = get_byte(f); |
301 |
is_interleaved = flags & 0x40;
|
302 |
has_local_palette = flags & 0x80;
|
303 |
bits_per_pixel = (flags & 0x07) + 1; |
304 |
#ifdef DEBUG
|
305 |
printf("gif: image x=%d y=%d w=%d h=%d\n", left, top, width, height);
|
306 |
#endif
|
307 |
|
308 |
if (has_local_palette) {
|
309 |
get_buffer(f, s->local_palette, 3 * (1 << bits_per_pixel)); |
310 |
palette = s->local_palette; |
311 |
} else {
|
312 |
palette = s->global_palette; |
313 |
bits_per_pixel = s->bits_per_pixel; |
314 |
} |
315 |
|
316 |
/* verify that all the image is inside the screen dimensions */
|
317 |
if (left + width > s->screen_width ||
|
318 |
top + height > s->screen_height) |
319 |
return -EINVAL;
|
320 |
|
321 |
/* build the palette */
|
322 |
if (s->pix_fmt == PIX_FMT_RGB24) {
|
323 |
line = av_malloc(width); |
324 |
if (!line)
|
325 |
return -ENOMEM;
|
326 |
} else {
|
327 |
n = (1 << bits_per_pixel);
|
328 |
spal = palette; |
329 |
for(i = 0; i < n; i++) { |
330 |
s->image_palette[i] = (0xff << 24) | |
331 |
(spal[0] << 16) | (spal[1] << 8) | (spal[2]); |
332 |
spal += 3;
|
333 |
} |
334 |
for(; i < 256; i++) |
335 |
s->image_palette[i] = (0xff << 24); |
336 |
/* handle transparency */
|
337 |
if (s->transparent_color_index >= 0) |
338 |
s->image_palette[s->transparent_color_index] = 0;
|
339 |
line = NULL;
|
340 |
} |
341 |
|
342 |
/* now get the image data */
|
343 |
s->f = f; |
344 |
code_size = get_byte(f); |
345 |
GLZWDecodeInit(s, code_size); |
346 |
|
347 |
/* read all the image */
|
348 |
linesize = s->image_linesize; |
349 |
ptr1 = s->image_buf + top * linesize + (left * 3);
|
350 |
ptr = ptr1; |
351 |
pass = 0;
|
352 |
y1 = 0;
|
353 |
for (y = 0; y < height; y++) { |
354 |
if (s->pix_fmt == PIX_FMT_RGB24) {
|
355 |
/* transcode to RGB24 */
|
356 |
GLZWDecode(s, line, width); |
357 |
d = ptr; |
358 |
sptr = line; |
359 |
for(x = 0; x < width; x++) { |
360 |
spal = palette + sptr[0] * 3; |
361 |
d[0] = spal[0]; |
362 |
d[1] = spal[1]; |
363 |
d[2] = spal[2]; |
364 |
d += 3;
|
365 |
sptr++; |
366 |
} |
367 |
} else {
|
368 |
GLZWDecode(s, ptr, width); |
369 |
} |
370 |
if (is_interleaved) {
|
371 |
switch(pass) {
|
372 |
default:
|
373 |
case 0: |
374 |
case 1: |
375 |
y1 += 8;
|
376 |
ptr += linesize * 8;
|
377 |
if (y1 >= height) {
|
378 |
y1 = 4;
|
379 |
if (pass == 0) |
380 |
ptr = ptr1 + linesize * 4;
|
381 |
else
|
382 |
ptr = ptr1 + linesize * 2;
|
383 |
pass++; |
384 |
} |
385 |
break;
|
386 |
case 2: |
387 |
y1 += 4;
|
388 |
ptr += linesize * 4;
|
389 |
if (y1 >= height) {
|
390 |
y1 = 1;
|
391 |
ptr = ptr1 + linesize; |
392 |
pass++; |
393 |
} |
394 |
break;
|
395 |
case 3: |
396 |
y1 += 2;
|
397 |
ptr += linesize * 2;
|
398 |
break;
|
399 |
} |
400 |
} else {
|
401 |
ptr += linesize; |
402 |
} |
403 |
} |
404 |
av_free(line); |
405 |
|
406 |
/* read the garbage data until end marker is found */
|
407 |
while (!s->eob_reached)
|
408 |
GetCode(s); |
409 |
return 0; |
410 |
} |
411 |
|
412 |
static int gif_read_extension(GifState *s) |
413 |
{ |
414 |
ByteIOContext *f = s->f; |
415 |
int ext_code, ext_len, i, gce_flags, gce_transparent_index;
|
416 |
|
417 |
/* extension */
|
418 |
ext_code = get_byte(f); |
419 |
ext_len = get_byte(f); |
420 |
#ifdef DEBUG
|
421 |
printf("gif: ext_code=0x%x len=%d\n", ext_code, ext_len);
|
422 |
#endif
|
423 |
switch(ext_code) {
|
424 |
case 0xf9: |
425 |
if (ext_len != 4) |
426 |
goto discard_ext;
|
427 |
s->transparent_color_index = -1;
|
428 |
gce_flags = get_byte(f); |
429 |
s->gce_delay = get_le16(f); |
430 |
gce_transparent_index = get_byte(f); |
431 |
if (gce_flags & 0x01) |
432 |
s->transparent_color_index = gce_transparent_index; |
433 |
else
|
434 |
s->transparent_color_index = -1;
|
435 |
s->gce_disposal = (gce_flags >> 2) & 0x7; |
436 |
#ifdef DEBUG
|
437 |
printf("gif: gce_flags=%x delay=%d tcolor=%d disposal=%d\n",
|
438 |
gce_flags, s->gce_delay, |
439 |
s->transparent_color_index, s->gce_disposal); |
440 |
#endif
|
441 |
ext_len = get_byte(f); |
442 |
break;
|
443 |
} |
444 |
|
445 |
/* NOTE: many extension blocks can come after */
|
446 |
discard_ext:
|
447 |
while (ext_len != 0) { |
448 |
for (i = 0; i < ext_len; i++) |
449 |
get_byte(f); |
450 |
ext_len = get_byte(f); |
451 |
#ifdef DEBUG
|
452 |
printf("gif: ext_len1=%d\n", ext_len);
|
453 |
#endif
|
454 |
} |
455 |
return 0; |
456 |
} |
457 |
|
458 |
static int gif_read_header1(GifState *s) |
459 |
{ |
460 |
ByteIOContext *f = s->f; |
461 |
uint8_t sig[6];
|
462 |
int ret, v, n;
|
463 |
int has_global_palette;
|
464 |
|
465 |
/* read gif signature */
|
466 |
ret = get_buffer(f, sig, 6);
|
467 |
if (ret != 6) |
468 |
return -1; |
469 |
if (memcmp(sig, gif87a_sig, 6) != 0 && |
470 |
memcmp(sig, gif89a_sig, 6) != 0) |
471 |
return -1; |
472 |
|
473 |
/* read screen header */
|
474 |
s->transparent_color_index = -1;
|
475 |
s->screen_width = get_le16(f); |
476 |
s->screen_height = get_le16(f); |
477 |
if( (unsigned)s->screen_width > 32767 |
478 |
|| (unsigned)s->screen_height > 32767){ |
479 |
av_log(NULL, AV_LOG_ERROR, "picture size too large\n"); |
480 |
return -1; |
481 |
} |
482 |
|
483 |
v = get_byte(f); |
484 |
s->color_resolution = ((v & 0x70) >> 4) + 1; |
485 |
has_global_palette = (v & 0x80);
|
486 |
s->bits_per_pixel = (v & 0x07) + 1; |
487 |
s->background_color_index = get_byte(f); |
488 |
get_byte(f); /* ignored */
|
489 |
#ifdef DEBUG
|
490 |
printf("gif: screen_w=%d screen_h=%d bpp=%d global_palette=%d\n",
|
491 |
s->screen_width, s->screen_height, s->bits_per_pixel, |
492 |
has_global_palette); |
493 |
#endif
|
494 |
if (has_global_palette) {
|
495 |
n = 1 << s->bits_per_pixel;
|
496 |
get_buffer(f, s->global_palette, n * 3);
|
497 |
} |
498 |
return 0; |
499 |
} |
500 |
|
501 |
static int gif_parse_next_image(GifState *s) |
502 |
{ |
503 |
ByteIOContext *f = s->f; |
504 |
int ret, code;
|
505 |
|
506 |
for (;;) {
|
507 |
code = url_fgetc(f); |
508 |
#ifdef DEBUG
|
509 |
printf("gif: code=%02x '%c'\n", code, code);
|
510 |
#endif
|
511 |
switch (code) {
|
512 |
case ',': |
513 |
if (gif_read_image(s) < 0) |
514 |
return AVERROR_IO;
|
515 |
ret = 0;
|
516 |
goto the_end;
|
517 |
case ';': |
518 |
/* end of image */
|
519 |
ret = AVERROR_IO; |
520 |
goto the_end;
|
521 |
case '!': |
522 |
if (gif_read_extension(s) < 0) |
523 |
return AVERROR_IO;
|
524 |
break;
|
525 |
case EOF: |
526 |
default:
|
527 |
/* error or errneous EOF */
|
528 |
ret = AVERROR_IO; |
529 |
goto the_end;
|
530 |
} |
531 |
} |
532 |
the_end:
|
533 |
return ret;
|
534 |
} |
535 |
|
536 |
static int gif_read_header(AVFormatContext * s1, |
537 |
AVFormatParameters * ap) |
538 |
{ |
539 |
GifState *s = s1->priv_data; |
540 |
ByteIOContext *f = &s1->pb; |
541 |
AVStream *st; |
542 |
|
543 |
s->f = f; |
544 |
if (gif_read_header1(s) < 0) |
545 |
return -1; |
546 |
|
547 |
/* allocate image buffer */
|
548 |
s->image_linesize = s->screen_width * 3;
|
549 |
s->image_buf = av_malloc(s->screen_height * s->image_linesize); |
550 |
if (!s->image_buf)
|
551 |
return -ENOMEM;
|
552 |
s->pix_fmt = PIX_FMT_RGB24; |
553 |
/* now we are ready: build format streams */
|
554 |
st = av_new_stream(s1, 0);
|
555 |
if (!st)
|
556 |
return -1; |
557 |
|
558 |
st->codec->codec_type = CODEC_TYPE_VIDEO; |
559 |
st->codec->codec_id = CODEC_ID_RAWVIDEO; |
560 |
st->codec->time_base.den = 5;
|
561 |
st->codec->time_base.num = 1;
|
562 |
/* XXX: check if screen size is always valid */
|
563 |
st->codec->width = s->screen_width; |
564 |
st->codec->height = s->screen_height; |
565 |
st->codec->pix_fmt = PIX_FMT_RGB24; |
566 |
return 0; |
567 |
} |
568 |
|
569 |
static int gif_read_packet(AVFormatContext * s1, |
570 |
AVPacket * pkt) |
571 |
{ |
572 |
GifState *s = s1->priv_data; |
573 |
int ret;
|
574 |
|
575 |
ret = gif_parse_next_image(s); |
576 |
if (ret < 0) |
577 |
return ret;
|
578 |
|
579 |
/* XXX: avoid copying */
|
580 |
if (av_new_packet(pkt, s->screen_width * s->screen_height * 3)) { |
581 |
return AVERROR_IO;
|
582 |
} |
583 |
pkt->stream_index = 0;
|
584 |
memcpy(pkt->data, s->image_buf, s->screen_width * s->screen_height * 3);
|
585 |
return 0; |
586 |
} |
587 |
|
588 |
static int gif_read_close(AVFormatContext *s1) |
589 |
{ |
590 |
GifState *s = s1->priv_data; |
591 |
av_free(s->image_buf); |
592 |
return 0; |
593 |
} |
594 |
|
595 |
/* read gif as image */
|
596 |
static int gif_read(ByteIOContext *f, |
597 |
int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque) |
598 |
{ |
599 |
GifState s1, *s = &s1; |
600 |
AVImageInfo info1, *info = &info1; |
601 |
int ret;
|
602 |
|
603 |
memset(s, 0, sizeof(GifState)); |
604 |
s->f = f; |
605 |
if (gif_read_header1(s) < 0) |
606 |
return -1; |
607 |
info->width = s->screen_width; |
608 |
info->height = s->screen_height; |
609 |
info->pix_fmt = PIX_FMT_PAL8; |
610 |
ret = alloc_cb(opaque, info); |
611 |
if (ret)
|
612 |
return ret;
|
613 |
s->image_buf = info->pict.data[0];
|
614 |
s->image_linesize = info->pict.linesize[0];
|
615 |
s->image_palette = (uint32_t *)info->pict.data[1];
|
616 |
|
617 |
if (gif_parse_next_image(s) < 0) |
618 |
return -1; |
619 |
return 0; |
620 |
} |
621 |
|
622 |
AVInputFormat gif_iformat = |
623 |
{ |
624 |
"gif",
|
625 |
"gif format",
|
626 |
sizeof(GifState),
|
627 |
gif_video_probe, |
628 |
gif_read_header, |
629 |
gif_read_packet, |
630 |
gif_read_close, |
631 |
}; |
632 |
|
633 |
AVImageFormat gif_image_format = { |
634 |
"gif",
|
635 |
"gif",
|
636 |
gif_image_probe, |
637 |
gif_read, |
638 |
(1 << PIX_FMT_PAL8),
|
639 |
gif_write, |
640 |
}; |