yading@11: /* yading@11: * GIF demuxer yading@11: * Copyright (c) 2012 Vitaliy E Sugrobov yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * GIF demuxer. yading@11: */ yading@11: yading@11: #include "avformat.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/opt.h" yading@11: #include "internal.h" yading@11: #include "libavcodec/gif.h" yading@11: yading@11: typedef struct GIFDemuxContext { yading@11: const AVClass *class; yading@11: /** yading@11: * Time span in hundredths of second before yading@11: * the next frame should be drawn on screen. yading@11: */ yading@11: int delay; yading@11: /** yading@11: * Minimum allowed delay between frames in hundredths of yading@11: * second. Values below this threshold considered to be yading@11: * invalid and set to value of default_delay. yading@11: */ yading@11: int min_delay; yading@11: int default_delay; yading@11: yading@11: /** yading@11: * loop options yading@11: */ yading@11: int total_iter; yading@11: int iter_count; yading@11: int ignore_loop; yading@11: } GIFDemuxContext; yading@11: yading@11: /** yading@11: * Major web browsers display gifs at ~10-15fps when rate yading@11: * is not explicitly set or have too low values. We assume default rate to be 10. yading@11: * Default delay = 100hundredths of second / 10fps = 10hos per frame. yading@11: */ yading@11: #define GIF_DEFAULT_DELAY 10 yading@11: /** yading@11: * By default delay values less than this threshold considered to be invalid. yading@11: */ yading@11: #define GIF_MIN_DELAY 2 yading@11: yading@11: static int gif_probe(AVProbeData *p) yading@11: { yading@11: /* check magick */ yading@11: if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6)) yading@11: return 0; yading@11: yading@11: /* width or height contains zero? */ yading@11: if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8])) yading@11: return 0; yading@11: yading@11: return AVPROBE_SCORE_MAX; yading@11: } yading@11: yading@11: static int resync(AVIOContext *pb) yading@11: { yading@11: int i; yading@11: for (i = 0; i < 6; i++) { yading@11: int b = avio_r8(pb); yading@11: if (b != gif87a_sig[i] && b != gif89a_sig[i]) yading@11: i = -(b != 'G'); yading@11: if (url_feof(pb)) yading@11: return AVERROR_EOF; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static int gif_read_header(AVFormatContext *s) yading@11: { yading@11: GIFDemuxContext *gdc = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st; yading@11: int width, height, ret; yading@11: yading@11: if ((ret = resync(pb)) < 0) yading@11: return ret; yading@11: yading@11: gdc->delay = gdc->default_delay; yading@11: width = avio_rl16(pb); yading@11: height = avio_rl16(pb); yading@11: yading@11: if (width == 0 || height == 0) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: /* GIF format operates with time in "hundredths of second", yading@11: * therefore timebase is 1/100 */ yading@11: avpriv_set_pts_info(st, 64, 1, 100); yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = AV_CODEC_ID_GIF; yading@11: st->codec->width = width; yading@11: st->codec->height = height; yading@11: yading@11: /* jump to start because gif decoder needs header data too */ yading@11: if (avio_seek(pb, 0, SEEK_SET) != 0) yading@11: return AVERROR(EIO); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int gif_skip_subblocks(AVIOContext *pb) yading@11: { yading@11: int sb_size, ret = 0; yading@11: yading@11: while (0x00 != (sb_size = avio_r8(pb))) { yading@11: if ((ret = avio_skip(pb, sb_size)) < 0) yading@11: return ret; yading@11: } yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: static int gif_read_ext(AVFormatContext *s) yading@11: { yading@11: GIFDemuxContext *gdc = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int sb_size, ext_label = avio_r8(pb); yading@11: int ret; yading@11: yading@11: if (ext_label == GIF_GCE_EXT_LABEL) { yading@11: if ((sb_size = avio_r8(pb)) < 4) { yading@11: av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: /* skip packed fields */ yading@11: if ((ret = avio_skip(pb, 1)) < 0) yading@11: return ret; yading@11: yading@11: gdc->delay = avio_rl16(pb); yading@11: yading@11: if (gdc->delay < gdc->min_delay) yading@11: gdc->delay = gdc->default_delay; yading@11: yading@11: /* skip the rest of the Graphic Control Extension block */ yading@11: if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) yading@11: return ret; yading@11: } else if (ext_label == GIF_APP_EXT_LABEL) { yading@11: uint8_t netscape_ext[sizeof(NETSCAPE_EXT_STR)-1 + 2]; yading@11: yading@11: if ((sb_size = avio_r8(pb)) != strlen(NETSCAPE_EXT_STR)) yading@11: return 0; yading@11: ret = avio_read(pb, netscape_ext, sizeof(netscape_ext)); yading@11: if (ret < sizeof(netscape_ext)) yading@11: return ret; yading@11: gdc->total_iter = avio_rl16(pb); yading@11: if (gdc->total_iter == 0) yading@11: gdc->total_iter = -1; yading@11: } yading@11: yading@11: if ((ret = gif_skip_subblocks(pb)) < 0) yading@11: return ret; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: GIFDemuxContext *gdc = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int packed_fields, block_label, ct_size, yading@11: keyframe, frame_parsed = 0, ret; yading@11: int64_t frame_start = avio_tell(pb), frame_end; yading@11: unsigned char buf[6]; yading@11: yading@11: if ((ret = avio_read(pb, buf, 6)) == 6) { yading@11: keyframe = memcmp(buf, gif87a_sig, 6) == 0 || yading@11: memcmp(buf, gif89a_sig, 6) == 0; yading@11: } else if (ret < 0) { yading@11: return ret; yading@11: } else { yading@11: keyframe = 0; yading@11: } yading@11: yading@11: if (keyframe) { yading@11: parse_keyframe: yading@11: /* skip 2 bytes of width and 2 of height */ yading@11: if ((ret = avio_skip(pb, 4)) < 0) yading@11: return ret; yading@11: yading@11: packed_fields = avio_r8(pb); yading@11: yading@11: /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */ yading@11: if ((ret = avio_skip(pb, 2)) < 0) yading@11: return ret; yading@11: yading@11: /* global color table presence */ yading@11: if (packed_fields & 0x80) { yading@11: ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); yading@11: yading@11: if ((ret = avio_skip(pb, ct_size)) < 0) yading@11: return ret; yading@11: } yading@11: } else { yading@11: avio_seek(pb, -ret, SEEK_CUR); yading@11: ret = AVERROR_EOF; yading@11: } yading@11: yading@11: while (GIF_TRAILER != (block_label = avio_r8(pb)) && !url_feof(pb)) { yading@11: if (block_label == GIF_EXTENSION_INTRODUCER) { yading@11: if ((ret = gif_read_ext (s)) < 0 ) yading@11: goto resync; yading@11: } else if (block_label == GIF_IMAGE_SEPARATOR) { yading@11: /* skip to last byte of Image Descriptor header */ yading@11: if ((ret = avio_skip(pb, 8)) < 0) yading@11: return ret; yading@11: yading@11: packed_fields = avio_r8(pb); yading@11: yading@11: /* local color table presence */ yading@11: if (packed_fields & 0x80) { yading@11: ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); yading@11: yading@11: if ((ret = avio_skip(pb, ct_size)) < 0) yading@11: return ret; yading@11: } yading@11: yading@11: /* read LZW Minimum Code Size */ yading@11: if (avio_r8(pb) < 1) { yading@11: av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n"); yading@11: goto resync; yading@11: } yading@11: yading@11: if ((ret = gif_skip_subblocks(pb)) < 0) yading@11: goto resync; yading@11: yading@11: frame_end = avio_tell(pb); yading@11: yading@11: if (avio_seek(pb, frame_start, SEEK_SET) != frame_start) yading@11: return AVERROR(EIO); yading@11: yading@11: ret = av_get_packet(pb, pkt, frame_end - frame_start); yading@11: if (ret < 0) yading@11: return ret; yading@11: yading@11: if (keyframe) yading@11: pkt->flags |= AV_PKT_FLAG_KEY; yading@11: yading@11: pkt->stream_index = 0; yading@11: pkt->duration = gdc->delay; yading@11: yading@11: /* Graphic Control Extension's scope is single frame. yading@11: * Remove its influence. */ yading@11: gdc->delay = gdc->default_delay; yading@11: frame_parsed = 1; yading@11: yading@11: break; yading@11: } else { yading@11: av_log(s, AV_LOG_ERROR, "invalid block label\n"); yading@11: resync: yading@11: if (!keyframe) yading@11: avio_seek(pb, frame_start, SEEK_SET); yading@11: if ((ret = resync(pb)) < 0) yading@11: return ret; yading@11: frame_start = avio_tell(pb) - 6; yading@11: keyframe = 1; yading@11: goto parse_keyframe; yading@11: } yading@11: } yading@11: yading@11: if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) { yading@11: /* This might happen when there is no image block yading@11: * between extension blocks and GIF_TRAILER or EOF */ yading@11: if (!gdc->ignore_loop && (block_label == GIF_TRAILER || url_feof(pb)) yading@11: && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter)) yading@11: return avio_seek(pb, 0, SEEK_SET); yading@11: return AVERROR_EOF; yading@11: } else yading@11: return ret; yading@11: } yading@11: yading@11: static const AVOption options[] = { yading@11: { "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 }, yading@11: { "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 }, yading@11: { "ignore_loop" , "ignore loop setting (netscape extension)" , offsetof(GIFDemuxContext, ignore_loop) , AV_OPT_TYPE_INT, {.i64 = 1} , 0, 1, AV_OPT_FLAG_DECODING_PARAM }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: static const AVClass demuxer_class = { yading@11: .class_name = "GIF demuxer", yading@11: .item_name = av_default_item_name, yading@11: .option = options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: .category = AV_CLASS_CATEGORY_DEMUXER, yading@11: }; yading@11: yading@11: AVInputFormat ff_gif_demuxer = { yading@11: .name = "gif", yading@11: .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), yading@11: .priv_data_size = sizeof(GIFDemuxContext), yading@11: .read_probe = gif_probe, yading@11: .read_header = gif_read_header, yading@11: .read_packet = gif_read_packet, yading@11: .flags = AVFMT_GENERIC_INDEX, yading@11: .priv_class = &demuxer_class, yading@11: };