From cb928fc448f9566e6f6c28d53fa4c2388e732a2b Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Tue, 10 May 2016 23:48:50 +0200 Subject: lavc: add IFF ANIM decoder Signed-off-by: Paul B Mahol --- libavformat/iff.c | 88 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 21 deletions(-) (limited to 'libavformat/iff.c') diff --git a/libavformat/iff.c b/libavformat/iff.c index 8b8bf01868..4fb79edfe1 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -60,6 +60,8 @@ #define ID_RGBN MKTAG('R','G','B','N') #define ID_DSD MKTAG('D','S','D',' ') #define ID_ANIM MKTAG('A','N','I','M') +#define ID_ANHD MKTAG('A','N','H','D') +#define ID_DLTA MKTAG('D','L','T','A') #define ID_FORM MKTAG('F','O','R','M') #define ID_FRM8 MKTAG('F','R','M','8') @@ -113,6 +115,7 @@ typedef struct IffDemuxContext { unsigned transparency; ///< transparency color index in palette unsigned masking; ///< masking method used uint8_t tvdc[32]; ///< TVDC lookup table + int64_t pts; } IffDemuxContext; /* Metadata string read */ @@ -147,7 +150,6 @@ static int iff_probe(AVProbeData *p) AV_RL32(d+8) == ID_DEEP || AV_RL32(d+8) == ID_ILBM || AV_RL32(d+8) == ID_RGB8 || - AV_RL32(d+8) == ID_RGB8 || AV_RL32(d+8) == ID_ANIM || AV_RL32(d+8) == ID_RGBN)) || (AV_RL32(d) == ID_FRM8 && AV_RL32(d+12) == ID_DSD)) @@ -367,8 +369,7 @@ static int iff_read_header(AVFormatContext *s) // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content st->codecpar->codec_tag = avio_rl32(pb); if (st->codecpar->codec_tag == ID_ANIM) { - avio_skip(pb, 8); - st->codecpar->codec_tag = avio_rl32(pb); + avio_skip(pb, 12); } iff->bitmap_compression = -1; iff->svx8_compression = -1; @@ -484,6 +485,9 @@ static int iff_read_header(AVFormatContext *s) } break; + case ID_ANHD: + break; + case ID_DPEL: if (data_size < 4 || (data_size & 3)) return AVERROR_INVALIDDATA; @@ -626,7 +630,10 @@ static int iff_read_header(AVFormatContext *s) avio_skip(pb, data_size - (avio_tell(pb) - orig_pos) + (data_size & 1)); } - avio_seek(pb, iff->body_pos, SEEK_SET); + if (st->codecpar->codec_tag == ID_ANIM) + avio_seek(pb, 12, SEEK_SET); + else + avio_seek(pb, iff->body_pos, SEEK_SET); switch(st->codecpar->codec_type) { case AVMEDIA_TYPE_AUDIO: @@ -672,6 +679,8 @@ static int iff_read_header(AVFormatContext *s) case AVMEDIA_TYPE_VIDEO: iff->bpp = st->codecpar->bits_per_coded_sample; + if (st->codecpar->codec_tag == ID_ANIM) + avpriv_set_pts_info(st, 32, 1, 60); if ((screenmode & 0x800 /* Hold And Modify */) && iff->bpp <= 8) { iff->ham = iff->bpp > 6 ? 6 : 4; st->codecpar->bits_per_coded_sample = 24; @@ -705,6 +714,28 @@ static int iff_read_header(AVFormatContext *s) return 0; } +static unsigned get_anim_duration(uint8_t *buf, int size) +{ + GetByteContext gb; + + bytestream2_init(&gb, buf, size); + bytestream2_skip(&gb, 4); + while (bytestream2_get_bytes_left(&gb) > 8) { + unsigned chunk = bytestream2_get_le32(&gb); + unsigned size = bytestream2_get_be32(&gb); + + if (chunk == ID_ANHD) { + if (size < 40) + break; + bytestream2_skip(&gb, 14); + return bytestream2_get_be32(&gb); + } else { + bytestream2_skip(&gb, size + size & 1); + } + } + return 10; +} + static int iff_read_packet(AVFormatContext *s, AVPacket *pkt) { @@ -714,8 +745,12 @@ static int iff_read_packet(AVFormatContext *s, int ret; int64_t pos = avio_tell(pb); - if (pos >= iff->body_end) + if (st->codecpar->codec_tag == ID_ANIM) { + if (avio_feof(pb)) + return AVERROR_EOF; + } else if (pos >= iff->body_end) { return AVERROR_EOF; + } if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { if (st->codecpar->codec_tag == ID_DSD || st->codecpar->codec_tag == ID_MAUD) { @@ -725,28 +760,39 @@ static int iff_read_packet(AVFormatContext *s, return AVERROR_INVALIDDATA; ret = av_get_packet(pb, pkt, iff->body_size); } - } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - uint8_t *buf; + } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + st->codecpar->codec_tag == ID_ANIM) { + uint64_t data_size, orig_pos; + uint32_t chunk_id = 0; - if (iff->body_size > INT_MAX - 2) - return AVERROR_INVALIDDATA; - if (av_new_packet(pkt, iff->body_size + 2) < 0) { - return AVERROR(ENOMEM); - } + while (!avio_feof(pb)) { + if (avio_feof(pb)) + return AVERROR_EOF; + + chunk_id = avio_rl32(pb); + data_size = avio_rb32(pb); + orig_pos = avio_tell(pb); - buf = pkt->data; - bytestream_put_be16(&buf, 2); - ret = avio_read(pb, buf, iff->body_size); - if (ret<0) { - av_packet_unref(pkt); - } else if (ret < iff->body_size) - av_shrink_packet(pkt, ret + 2); + if (chunk_id == ID_FORM) + break; + else + avio_skip(pb, data_size); + } + ret = av_get_packet(pb, pkt, data_size); + pkt->pos = orig_pos; + pkt->duration = get_anim_duration(pkt->data, pkt->size); + if (pos == 12) + pkt->flags |= AV_PKT_FLAG_KEY; + } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + st->codecpar->codec_tag != ID_ANIM) { + ret = av_get_packet(pb, pkt, iff->body_size); + pkt->pos = pos; + if (pos == iff->body_pos) + pkt->flags |= AV_PKT_FLAG_KEY; } else { av_assert0(0); } - if (pos == iff->body_pos) - pkt->flags |= AV_PKT_FLAG_KEY; if (ret < 0) return ret; pkt->stream_index = 0; -- cgit v1.2.3