Commit 8e12e008 authored by bellard's avatar bellard

added automatic GIF/animated GIF probing - added GIF as an image format too -...

added automatic GIF/animated GIF probing - added GIF as an image format too - added interlaced gif support


git-svn-id: file:///var/local/repositories/ffmpeg/trunk@1539 9553f0bf-9b14-0410-a0b8-cfaf0461ba5b
parent 1f4646f0
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
*/ */
#include "avformat.h" #include "avformat.h"
int gif_write(ByteIOContext *pb, AVImageInfo *info);
//#define DEBUG //#define DEBUG
#define MAXBITS 12 #define MAXBITS 12
...@@ -35,8 +37,8 @@ typedef struct GifState { ...@@ -35,8 +37,8 @@ typedef struct GifState {
int background_color_index; int background_color_index;
int transparent_color_index; int transparent_color_index;
int color_resolution; int color_resolution;
int image_count;
uint8_t *image_buf; uint8_t *image_buf;
int image_linesize;
/* after the frame is displayed, the disposal method is used */ /* after the frame is displayed, the disposal method is used */
int gce_disposal; int gce_disposal;
/* delay during which the frame is shown */ /* delay during which the frame is shown */
...@@ -81,16 +83,60 @@ static const uint16_t mask[17] = ...@@ -81,16 +83,60 @@ static const uint16_t mask[17] =
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
}; };
static int gif_probe(AVProbeData * pd) /* Probe gif video format or gif image format. The current heuristic
supposes the gif87a is always a single image. For gif89a, we
consider it as a video only if a GCE extension is present in the
first kilobyte. */
static int gif_video_probe(AVProbeData * pd)
{
const uint8_t *p, *p_end;
int bits_per_pixel, has_global_palette, ext_code, ext_len;
if (pd->buf_size < 24 ||
memcmp(pd->buf, gif89a_sig, 6) != 0)
return 0;
p_end = pd->buf + pd->buf_size;
p = pd->buf + 6;
bits_per_pixel = (p[4] & 0x07) + 1;
has_global_palette = (p[4] & 0x80);
p += 7;
if (has_global_palette)
p += (1 << bits_per_pixel) * 3;
for(;;) {
if (p >= p_end)
return 0;
if (*p != '!')
break;
p++;
if (p >= p_end)
return 0;
ext_code = *p++;
/* if GCE extension found: it is likely to be an animation */
if (ext_code == 0xf9)
return AVPROBE_SCORE_MAX;
for(;;) {
if (p >= p_end)
return 0;
ext_len = *p++;
if (ext_len == 0)
break;
p += ext_len;
}
}
return 0;
}
static int gif_image_probe(AVProbeData * pd)
{ {
if (pd->buf_size >= 24 && if (pd->buf_size >= 24 &&
(memcmp(pd->buf, gif87a_sig, 6) == 0 || (memcmp(pd->buf, gif87a_sig, 6) == 0 ||
memcmp(pd->buf, gif89a_sig, 6) == 0)) memcmp(pd->buf, gif89a_sig, 6) == 0))
return AVPROBE_SCORE_MAX; return AVPROBE_SCORE_MAX - 1;
else else
return 0; return 0;
} }
static void GLZWDecodeInit(GifState * s, int csize) static void GLZWDecodeInit(GifState * s, int csize)
{ {
/* read buffer */ /* read buffer */
...@@ -224,13 +270,12 @@ static int GLZWDecode(GifState * s, uint8_t * buf, int len) ...@@ -224,13 +270,12 @@ static int GLZWDecode(GifState * s, uint8_t * buf, int len)
return len - l; return len - l;
} }
static int gif_read_image(AVFormatContext * s1, AVPacket * pkt) static int gif_read_image(GifState *s)
{ {
GifState *s = s1->priv_data; ByteIOContext *f = s->f;
int left, top, width, height, bits_per_pixel, code_size, flags; int left, top, width, height, bits_per_pixel, code_size, flags;
int is_interleaved, has_local_palette, y, x, linesize; int is_interleaved, has_local_palette, y, x, pass, y1, linesize;
ByteIOContext *f = &s1->pb; uint8_t *ptr, *line, *d, *spal, *palette, *sptr, *ptr1;
uint8_t *ptr, *line, *d, *spal, *palette, *sptr;
left = get_le16(f); left = get_le16(f);
top = get_le16(f); top = get_le16(f);
...@@ -251,23 +296,10 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt) ...@@ -251,23 +296,10 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt)
palette = s->global_palette; palette = s->global_palette;
} }
/* allocate local image (XXX: horrible, needs API change) */
if (s->image_count == 0) {
/* modify screen width/height if invalid */
if (left + width > s->screen_width)
s->screen_width = left + width;
if (top + height > s->screen_height)
s->screen_width = top + height;
s->image_buf = av_malloc(s->screen_width * s->screen_height * 3);
if (!s->image_buf)
return -ENOMEM;
} else {
/* verify that all the image is inside the screen dimensions */ /* verify that all the image is inside the screen dimensions */
if (left + width > s->screen_width || if (left + width > s->screen_width ||
top + height > s->screen_height) top + height > s->screen_height)
return -EINVAL; return -EINVAL;
}
s->image_count++;
line = av_malloc(width); line = av_malloc(width);
if (!line) if (!line)
...@@ -279,8 +311,11 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt) ...@@ -279,8 +311,11 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt)
GLZWDecodeInit(s, code_size); GLZWDecodeInit(s, code_size);
/* read all the image and transcode it to RGB24 (horrible) */ /* read all the image and transcode it to RGB24 (horrible) */
linesize = s->screen_width * 3; linesize = s->image_linesize;
ptr = s->image_buf + top * linesize + (left * 3); ptr1 = s->image_buf + top * linesize + (left * 3);
ptr = ptr1;
pass = 0;
y1 = 0;
for (y = 0; y < height; y++) { for (y = 0; y < height; y++) {
GLZWDecode(s, line, width); GLZWDecode(s, line, width);
d = ptr; d = ptr;
...@@ -293,29 +328,51 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt) ...@@ -293,29 +328,51 @@ static int gif_read_image(AVFormatContext * s1, AVPacket * pkt)
d += 3; d += 3;
sptr++; sptr++;
} }
if (is_interleaved) {
switch(pass) {
default:
case 0:
case 1:
y1 += 8;
ptr += linesize * 8;
if (y1 >= height) {
y1 = 4;
if (pass == 0)
ptr = ptr1 + linesize * 4;
else
ptr = ptr1 + linesize * 2;
pass++;
}
break;
case 2:
y1 += 4;
ptr += linesize * 4;
if (y1 >= height) {
y1 = 1;
ptr = ptr1 + linesize;
pass++;
}
break;
case 3:
y1 += 2;
ptr += linesize * 2;
break;
}
} else {
ptr += linesize; ptr += linesize;
} }
}
av_free(line); av_free(line);
/* read the garbage data until end marker is found */ /* read the garbage data until end marker is found */
while (!s->eob_reached) while (!s->eob_reached)
GetCode(s); GetCode(s);
/* output image */
/* XXX: avoid copying */
if (av_new_packet(pkt, s->screen_width * s->screen_height * 3)) {
av_free(line);
return -EIO;
}
pkt->stream_index = 0;
memcpy(pkt->data, s->image_buf, s->screen_width * s->screen_height * 3);
return 0; return 0;
} }
static int gif_read_extension(AVFormatContext * s1) static int gif_read_extension(GifState *s)
{ {
GifState *s = s1->priv_data; ByteIOContext *f = s->f;
ByteIOContext *f = &s1->pb;
int ext_code, ext_len, i, gce_flags, gce_transparent_index; int ext_code, ext_len, i, gce_flags, gce_transparent_index;
/* extension */ /* extension */
...@@ -359,12 +416,9 @@ static int gif_read_extension(AVFormatContext * s1) ...@@ -359,12 +416,9 @@ static int gif_read_extension(AVFormatContext * s1)
return 0; return 0;
} }
static int gif_read_header(AVFormatContext * s1, static int gif_read_header1(GifState *s)
AVFormatParameters * ap)
{ {
GifState *s = s1->priv_data; ByteIOContext *f = s->f;
ByteIOContext *f = &s1->pb;
AVStream *st;
uint8_t sig[6]; uint8_t sig[6];
int ret, v, n; int ret, v, n;
int has_global_palette; int has_global_palette;
...@@ -396,25 +450,12 @@ static int gif_read_header(AVFormatContext * s1, ...@@ -396,25 +450,12 @@ static int gif_read_header(AVFormatContext * s1,
n = 1 << s->bits_per_pixel; n = 1 << s->bits_per_pixel;
get_buffer(f, s->global_palette, n * 3); get_buffer(f, s->global_palette, n * 3);
} }
/* now we are ready: build format streams */
st = av_new_stream(s1, 0);
if (!st)
return -1;
st->codec.codec_type = CODEC_TYPE_VIDEO;
st->codec.codec_id = CODEC_ID_RAWVIDEO;
st->codec.frame_rate = 5 * FRAME_RATE_BASE;
/* XXX: check if screen size is always valid */
st->codec.width = s->screen_width;
st->codec.height = s->screen_height;
st->codec.pix_fmt = PIX_FMT_RGB24;
return 0; return 0;
} }
static int gif_read_packet(AVFormatContext * s1, static int gif_parse_next_image(GifState *s)
AVPacket * pkt)
{ {
ByteIOContext *f = &s1->pb; ByteIOContext *f = s->f;
int ret, code; int ret, code;
for (;;) { for (;;) {
...@@ -424,7 +465,7 @@ static int gif_read_packet(AVFormatContext * s1, ...@@ -424,7 +465,7 @@ static int gif_read_packet(AVFormatContext * s1,
#endif #endif
switch (code) { switch (code) {
case ',': case ',':
if (gif_read_image(s1, pkt) < 0) if (gif_read_image(s) < 0)
return -EIO; return -EIO;
ret = 0; ret = 0;
goto the_end; goto the_end;
...@@ -433,7 +474,7 @@ static int gif_read_packet(AVFormatContext * s1, ...@@ -433,7 +474,7 @@ static int gif_read_packet(AVFormatContext * s1,
ret = -EIO; ret = -EIO;
goto the_end; goto the_end;
case '!': case '!':
if (gif_read_extension(s1) < 0) if (gif_read_extension(s) < 0)
return -EIO; return -EIO;
break; break;
case EOF: case EOF:
...@@ -447,6 +488,56 @@ static int gif_read_packet(AVFormatContext * s1, ...@@ -447,6 +488,56 @@ static int gif_read_packet(AVFormatContext * s1,
return ret; return ret;
} }
static int gif_read_header(AVFormatContext * s1,
AVFormatParameters * ap)
{
GifState *s = s1->priv_data;
ByteIOContext *f = &s1->pb;
AVStream *st;
s->f = f;
if (gif_read_header1(s) < 0)
return -1;
/* allocate image buffer */
s->image_linesize = s->screen_width * 3;
s->image_buf = av_malloc(s->screen_height * s->image_linesize);
if (!s->image_buf)
return -ENOMEM;
/* now we are ready: build format streams */
st = av_new_stream(s1, 0);
if (!st)
return -1;
st->codec.codec_type = CODEC_TYPE_VIDEO;
st->codec.codec_id = CODEC_ID_RAWVIDEO;
st->codec.frame_rate = 5 * FRAME_RATE_BASE;
/* XXX: check if screen size is always valid */
st->codec.width = s->screen_width;
st->codec.height = s->screen_height;
st->codec.pix_fmt = PIX_FMT_RGB24;
return 0;
}
static int gif_read_packet(AVFormatContext * s1,
AVPacket * pkt)
{
GifState *s = s1->priv_data;
int ret;
ret = gif_parse_next_image(s);
if (ret < 0)
return ret;
/* XXX: avoid copying */
if (av_new_packet(pkt, s->screen_width * s->screen_height * 3)) {
return -EIO;
}
pkt->stream_index = 0;
memcpy(pkt->data, s->image_buf, s->screen_width * s->screen_height * 3);
return 0;
}
static int gif_read_close(AVFormatContext *s1) static int gif_read_close(AVFormatContext *s1)
{ {
GifState *s = s1->priv_data; GifState *s = s1->priv_data;
...@@ -454,13 +545,48 @@ static int gif_read_close(AVFormatContext *s1) ...@@ -454,13 +545,48 @@ static int gif_read_close(AVFormatContext *s1)
return 0; return 0;
} }
/* read gif as image */
static int gif_read(ByteIOContext *f,
int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque)
{
GifState s1, *s = &s1;
AVImageInfo info1, *info = &info1;
int ret;
memset(s, 0, sizeof(GifState));
s->f = f;
if (gif_read_header1(s) < 0)
return -1;
info->width = s->screen_width;
info->height = s->screen_height;
info->pix_fmt = PIX_FMT_RGB24;
ret = alloc_cb(opaque, info);
if (ret)
return ret;
s->image_buf = info->pict.data[0];
s->image_linesize = info->pict.linesize[0];
if (gif_parse_next_image(s) < 0)
return -1;
return 0;
}
AVInputFormat gif_iformat = AVInputFormat gif_iformat =
{ {
"gif", "gif",
"gif format", "gif format",
sizeof(GifState), sizeof(GifState),
gif_probe, gif_video_probe,
gif_read_header, gif_read_header,
gif_read_packet, gif_read_packet,
gif_read_close, gif_read_close,
}; };
AVImageFormat gif_image_format = {
"gif",
"gif",
gif_image_probe,
gif_read,
(1 << PIX_FMT_RGB24),
gif_write,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment