[FFmpeg-devel] [PATCH] Animated GIF Support
Paul B Mahol
onemda at gmail.com
Thu Oct 18 13:35:56 CEST 2012
On 10/18/12, Vitaliy Sugrobov <vsugrob at hotmail.com> wrote:
>
[...]
>
> Added gif demuxer. Changed gif decoder: now it supports all disposal methods.
>
> Signed-off-by: Vitaliy E Sugrobov <vsugrob at hotmail.com>
> ---
> libavcodec/gifdec.c | 315 +++++++++++++++++++++++++++++++++++++---------
> libavcodec/version.h | 2 +-
> libavformat/Makefile | 1 +
> libavformat/allformats.c | 2 +-
> libavformat/gifdec.c | 309 +++++++++++++++++++++++++++++++++++++++++++++
> libavformat/version.h | 2 +-
> 6 files changed, 568 insertions(+), 63 deletions(-)
> mode change 100644 => 100755 libavcodec/gifdec.c
> create mode 100755 libavformat/gifdec.c
>
> diff --git a/libavcodec/gifdec.c b/libavcodec/gifdec.c
> old mode 100644
> new mode 100755
> index 3e7799f..8ba61ae
> --- a/libavcodec/gifdec.c
> +++ b/libavcodec/gifdec.c
> @@ -2,6 +2,7 @@
> * GIF decoder
> * Copyright (c) 2003 Fabrice Bellard
> * Copyright (c) 2006 Baptiste Coudurier
> + * Copyright (c) 2012 Vitaliy E Sugrobov
> *
> * This file is part of FFmpeg.
> *
> @@ -32,20 +33,36 @@
> #define GCE_DISPOSAL_BACKGROUND 2
> #define GCE_DISPOSAL_RESTORE 3
>
> +/* This value is intentionally set to "transparent white" color.
> + * It is much better to have white background instead of black
> + * when gif image converted to format which not support transparency.
> + */
> +#define GIF_TRANSPARENT_COLOR 0x00ffffff
put this into AVOption, do not hardcode it here.
> +
> typedef struct GifState {
> AVFrame picture;
> int screen_width;
> int screen_height;
> + int has_global_palette;
> int bits_per_pixel;
> + uint32_t bg_color;
> int background_color_index;
[...]
> ret = gif_parse_next_image(s);
> if (ret < 0)
> return ret;
> @@ -322,6 +513,10 @@ static av_cold int gif_decode_close(AVCodecContext *avctx)
> ff_lzw_decode_close(&s->lzw);
> if(s->picture.data[0])
> avctx->release_buffer(avctx, &s->picture);
> +
> + av_free(s->idx_line);
> + av_free(s->stored_img);
av_freep()
> +
> return 0;
> }
>
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 17e7468..b2d5c1b 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -29,7 +29,7 @@
> #include "libavutil/avutil.h"
>
[...]
> +++ b/libavformat/gifdec.c
> @@ -0,0 +1,309 @@
> +/*
> + * GIF demuxer
> + * Copyright (c) 2012 Vitaliy E Sugrobov
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * GIF demuxer.
> + */
> +
> +#include "avformat.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +#include "internal.h"
> +
> +typedef struct GIFDemuxContext {
> + const AVClass *class;
> + uint32_t width;
> + uint32_t height;
> + /**
> + * Time span in hundredths of second before
> + * the next frame should be drawn on screen.
> + */
> + int delay;
> + /**
> + * Minimum allowed delay between frames in hundredths of
> + * second. Values below this threshold considered to be
> + * invalid and set to value of default_delay.
> + */
> + int min_delay;
> + int default_delay;
> + int total_duration; ///< In hundredths of second.
> + int frame_idx;
> +} GIFDemuxContext;
> +
> +static const uint8_t gif87a_sig[6] = "GIF87a";
> +static const uint8_t gif89a_sig[6] = "GIF89a";
Duplicated from lavc/gifdec. This should be instead in header and that
is patch prior to this one.
> +
> +#define GIF_TRAILER 0x3b
> +#define GIF_EXTENSION_INTRODUCER 0x21
> +#define GIF_IMAGE_SEPARATOR 0x2c
> +#define GIF_GCE_EXT_LABEL 0xf9
> +
> +/**
> + * Major web browsers display gifs at ~10-15fps when rate
> + * is not explicitly set or have too low values. We assume default rate to be 10.
> + * Default delay = 100hundredths of second / 10fps = 10hos per frame.
> + */
> +#define GIF_DEFAULT_DELAY 10
> +/**
> + * By default delay values less than this threshold considered to be invalid.
> + */
> +#define GIF_MIN_DELAY 2
> +
> +static int gif_probe(AVProbeData *p)
> +{
> + if (p->buf_size < 6)
> + return 0;
> +
> + /* check magick */
[..]
> +
> +static int gif_skip_subblocks(AVIOContext *pb)
> +{
> + int sb_size, ret = 0;
> +
> + while (0x00 != (sb_size = avio_r8(pb))) {
> + if ((ret = avio_skip(pb, sb_size)) < 0)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int gif_read_ext (AVFormatContext *s)
extra whitespace
> +{
> + GIFDemuxContext *gdc = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int sb_size, ext_label = avio_r8(pb);
> + int ret;
> +
> + if (ext_label == GIF_GCE_EXT_LABEL) {
> + if ((sb_size = avio_r8(pb)) < 4) {
> + av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n");
> + return AVERROR_INVALIDDATA;
> + }
> +
> + /* skip packed fields */
> + if ((ret = avio_skip(pb, 1)) < 0)
> + return ret;
> +
> + gdc->delay = avio_rl16(pb);
> +
> + if (gdc->delay < gdc->min_delay)
> + gdc->delay = gdc->default_delay;
> +
> + /* skip the rest of the Graphic Control Extension block */
> + if ((ret = avio_skip(pb, sb_size - 3)) < 0 )
> + return ret;
> + }
> +
> + if ((ret = gif_skip_subblocks(pb)) < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int gif_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + GIFDemuxContext *gdc = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int packed_fields, block_label, ct_size,
> + is_first_frame, frame_parsed = 0, ret;
> + int64_t frame_start = avio_tell(pb), frame_end;
> + unsigned char buf[6];
> +
> + if ((ret = avio_read(pb, buf, 6)) == 6) {
> + is_first_frame = memcmp(buf, gif87a_sig, 6) == 0 ||
> + memcmp(buf, gif89a_sig, 6) == 0;
> + } else if (ret < 0)
> + return ret;
> + else
> + is_first_frame = 0;
i would use {, } anway.
> +
> + if (is_first_frame) {
> + /* skip 2 bytes of width and 2 of height */
> + if ((ret = avio_skip(pb, 4)) < 0)
> + return ret;
> +
> + packed_fields = avio_r8(pb);
> +
> + /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */
> + if ((ret = avio_skip(pb, 2)) < 0)
> + return ret;
> +
> + /* glogal color table presence */
> + if (packed_fields & 0x80) {
> + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1));
> +
> + if ((ret = avio_skip(pb, ct_size)) < 0)
> + return ret;
> + }
> +
> + gdc->total_duration = 0;
> + gdc->frame_idx = 0;
> + } else {
> + avio_seek(pb, -ret, SEEK_CUR);
> + ret = AVERROR_EOF;
> + }
> +
> + while(GIF_TRAILER != (block_label = avio_r8(pb)) && !url_feof(pb)) {
nit: whitespace between while and (
> + if (block_label == GIF_EXTENSION_INTRODUCER) {
> + if ((ret = gif_read_ext (s)) < 0 )
> + return ret;
> + } else if (block_label == GIF_IMAGE_SEPARATOR) {
> + /* skip to last byte of Image Descriptor header */
> + if ((ret = avio_skip(pb, 8)) < 0)
> + return ret;
> +
> + packed_fields = avio_r8(pb);
> +
> + /* local color table presence */
> + if (packed_fields & 0x80) {
> + ct_size = 3 * (1 << ((packed_fields & 0x07) + 1));
> +
> + if ((ret = avio_skip(pb, ct_size)) < 0)
> + return ret;
> + }
> +
> + /* read LZW Minimum Code Size */
> + if (avio_r8(pb) < 1) {
> + av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n");
> + return AVERROR_INVALIDDATA;
> + }
> +
> + if ((ret = gif_skip_subblocks(pb)) < 0)
> + return ret;
> +
> + frame_end = avio_tell(pb);
> +
> + if (avio_seek(pb, frame_start, SEEK_SET) != frame_start)
> + return AVERROR(EIO);
> +
> + ret = av_get_packet(pb, pkt, (int)(frame_end - frame_start));
> + if (ret < 0)
> + return ret;
> +
> + if (is_first_frame)
> + pkt->flags |= AV_PKT_FLAG_KEY;
> +
> + pkt->stream_index = 0;
> + pkt->pts = gdc->total_duration;
> + gdc->total_duration += gdc->delay;
> + pkt->duration = gdc->delay;
> + pkt->dts = gdc->frame_idx;
> +
> + /* Avoid invalid timestamps. */
> + if (pkt->pts < pkt->dts)
> + pkt->pts = pkt->dts;
Is that really needed? Timestamps are not stored in gif bitstream.
> +
> + /* Graphic Control Extension's scope is single frame.
> + * Remove its influence. */
> + gdc->delay = gdc->default_delay;
> + gdc->frame_idx++;
> + frame_parsed = 1;
> +
> + break;
> + } else {
> + av_log(s, AV_LOG_ERROR, "invalid block label\n");
> + return AVERROR_INVALIDDATA;
> + }
> + }
> +
> + if (ret >= 0 && !frame_parsed) {
> + /* This might happen when there is no image block
> + * between extension blocks and GIF_TRAILER or EOF */
> + return AVERROR_EOF;
> + } else
> + return ret;
> +}
> +
> +static const AVOption options[] = {
> + { "gif_min_delay" , "minimum valid delay between frames (in hundredths of second)", offsetof(GIFDemuxContext, min_delay) , AV_OPT_TYPE_INT, {.i64 = GIF_MIN_DELAY} , 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM },
> + { "gif_default_delay", "default delay between frames (in hundredths of second)" , offsetof(GIFDemuxContext, default_delay), AV_OPT_TYPE_INT, {.i64 = GIF_DEFAULT_DELAY}, 0, 100 * 60, AV_OPT_FLAG_DECODING_PARAM },
I do not see much point in gif_ prefix.
> + { NULL },
> +};
> +
> +static const AVClass demuxer_class = {
> + .class_name = "GIFDemuxContext",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> + .category = AV_CLASS_CATEGORY_DEMUXER,
> +};
> +
> +AVInputFormat ff_gif_demuxer = {
> + .name = "gif",
> + .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"),
> + .priv_data_size = sizeof(GIFDemuxContext),
> + .read_probe = gif_probe,
> + .read_header = gif_read_header,
> + .read_packet = gif_read_packet,
> + .priv_class = &demuxer_class,
> +};
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 749b9e0..fe31a7d 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -30,7 +30,7 @@
> #include "libavutil/avutil.h"
>
> #define LIBAVFORMAT_VERSION_MAJOR 54
> -#define LIBAVFORMAT_VERSION_MINOR 33
> +#define LIBAVFORMAT_VERSION_MINOR 34
> #define LIBAVFORMAT_VERSION_MICRO 100
>
> #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
> --
> 1.7.2.5
Missing doc change for demuxer.
More information about the ffmpeg-devel
mailing list