1/* 2 * Deluxe Paint Animation demuxer 3 * Copyright (c) 2009 Peter Ross 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * Deluxe Paint Animation demuxer 25 */ 26 27#include "libavutil/intreadwrite.h" 28#include "avformat.h" 29#include "internal.h" 30 31typedef struct { 32 int base_record; 33 unsigned int nb_records; 34 int size; 35} Page; 36 37typedef struct { 38 unsigned int nb_pages; /**< total pages in file */ 39 unsigned int nb_records; /**< total records in file */ 40 int page_table_offset; 41#define MAX_PAGES 256 /**< Deluxe Paint hardcoded value */ 42 Page pt[MAX_PAGES]; /**< page table */ 43 int page; /**< current page (or AVERROR_xxx code) */ 44 int record; /**< current record (with in page) */ 45} AnmDemuxContext; 46 47#define LPF_TAG MKTAG('L','P','F',' ') 48#define ANIM_TAG MKTAG('A','N','I','M') 49 50static int probe(AVProbeData *p) 51{ 52 /* verify tags and video dimensions */ 53 if (AV_RL32(&p->buf[0]) == LPF_TAG && 54 AV_RL32(&p->buf[16]) == ANIM_TAG && 55 AV_RL16(&p->buf[20]) && AV_RL16(&p->buf[22])) 56 return AVPROBE_SCORE_MAX; 57 return 0; 58} 59 60/** 61 * @return page containing the requested record or AVERROR_XXX 62 */ 63static int find_record(const AnmDemuxContext *anm, int record) 64{ 65 int i; 66 67 if (record >= anm->nb_records) 68 return AVERROR_EOF; 69 70 for (i = 0; i < MAX_PAGES; i++) { 71 const Page *p = &anm->pt[i]; 72 if (p->nb_records > 0 && record >= p->base_record && record < p->base_record + p->nb_records) 73 return i; 74 } 75 76 return AVERROR_INVALIDDATA; 77} 78 79static int read_header(AVFormatContext *s) 80{ 81 AnmDemuxContext *anm = s->priv_data; 82 AVIOContext *pb = s->pb; 83 AVStream *st; 84 int i, ret; 85 86 avio_skip(pb, 4); /* magic number */ 87 if (avio_rl16(pb) != MAX_PAGES) { 88 avpriv_request_sample(s, "max_pages != " AV_STRINGIFY(MAX_PAGES)); 89 return AVERROR_PATCHWELCOME; 90 } 91 92 anm->nb_pages = avio_rl16(pb); 93 anm->nb_records = avio_rl32(pb); 94 avio_skip(pb, 2); /* max records per page */ 95 anm->page_table_offset = avio_rl16(pb); 96 if (avio_rl32(pb) != ANIM_TAG) 97 return AVERROR_INVALIDDATA; 98 99 /* video stream */ 100 st = avformat_new_stream(s, NULL); 101 if (!st) 102 return AVERROR(ENOMEM); 103 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 104 st->codec->codec_id = AV_CODEC_ID_ANM; 105 st->codec->codec_tag = 0; /* no fourcc */ 106 st->codec->width = avio_rl16(pb); 107 st->codec->height = avio_rl16(pb); 108 if (avio_r8(pb) != 0) 109 goto invalid; 110 avio_skip(pb, 1); /* frame rate multiplier info */ 111 112 /* ignore last delta record (used for looping) */ 113 if (avio_r8(pb)) /* has_last_delta */ 114 anm->nb_records = FFMAX(anm->nb_records - 1, 0); 115 116 avio_skip(pb, 1); /* last_delta_valid */ 117 118 if (avio_r8(pb) != 0) 119 goto invalid; 120 121 if (avio_r8(pb) != 1) 122 goto invalid; 123 124 avio_skip(pb, 1); /* other recs per frame */ 125 126 if (avio_r8(pb) != 1) 127 goto invalid; 128 129 avio_skip(pb, 32); /* record_types */ 130 st->nb_frames = avio_rl32(pb); 131 avpriv_set_pts_info(st, 64, 1, avio_rl16(pb)); 132 avio_skip(pb, 58); 133 134 /* color cycling and palette data */ 135 st->codec->extradata_size = 16*8 + 4*256; 136 st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); 137 if (!st->codec->extradata) { 138 return AVERROR(ENOMEM); 139 } 140 ret = avio_read(pb, st->codec->extradata, st->codec->extradata_size); 141 if (ret < 0) 142 return ret; 143 144 /* read page table */ 145 ret = avio_seek(pb, anm->page_table_offset, SEEK_SET); 146 if (ret < 0) 147 return ret; 148 149 for (i = 0; i < MAX_PAGES; i++) { 150 Page *p = &anm->pt[i]; 151 p->base_record = avio_rl16(pb); 152 p->nb_records = avio_rl16(pb); 153 p->size = avio_rl16(pb); 154 } 155 156 /* find page of first frame */ 157 anm->page = find_record(anm, 0); 158 if (anm->page < 0) { 159 return anm->page; 160 } 161 162 anm->record = -1; 163 return 0; 164 165invalid: 166 avpriv_request_sample(s, "Invalid header element"); 167 return AVERROR_PATCHWELCOME; 168} 169 170static int read_packet(AVFormatContext *s, 171 AVPacket *pkt) 172{ 173 AnmDemuxContext *anm = s->priv_data; 174 AVIOContext *pb = s->pb; 175 Page *p; 176 int tmp, record_size; 177 178 if (url_feof(s->pb)) 179 return AVERROR(EIO); 180 181 if (anm->page < 0) 182 return anm->page; 183 184repeat: 185 p = &anm->pt[anm->page]; 186 187 /* parse page header */ 188 if (anm->record < 0) { 189 avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16), SEEK_SET); 190 avio_skip(pb, 8 + 2*p->nb_records); 191 anm->record = 0; 192 } 193 194 /* if we have fetched all records in this page, then find the 195 next page and repeat */ 196 if (anm->record >= p->nb_records) { 197 anm->page = find_record(anm, p->base_record + p->nb_records); 198 if (anm->page < 0) 199 return anm->page; 200 anm->record = -1; 201 goto repeat; 202 } 203 204 /* fetch record size */ 205 tmp = avio_tell(pb); 206 avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16) + 207 8 + anm->record * 2, SEEK_SET); 208 record_size = avio_rl16(pb); 209 avio_seek(pb, tmp, SEEK_SET); 210 211 /* fetch record */ 212 pkt->size = av_get_packet(s->pb, pkt, record_size); 213 if (pkt->size < 0) 214 return pkt->size; 215 if (p->base_record + anm->record == 0) 216 pkt->flags |= AV_PKT_FLAG_KEY; 217 218 anm->record++; 219 return 0; 220} 221 222AVInputFormat ff_anm_demuxer = { 223 .name = "anm", 224 .long_name = NULL_IF_CONFIG_SMALL("Deluxe Paint Animation"), 225 .priv_data_size = sizeof(AnmDemuxContext), 226 .read_probe = probe, 227 .read_header = read_header, 228 .read_packet = read_packet, 229}; 230