[FFmpeg-devel] [PATCH] DeluxePaint Animation playback system
Reimar Döffinger
Reimar.Doeffinger
Wed Aug 26 14:15:06 CEST 2009
On Wed, Aug 26, 2009 at 08:45:33PM +1000, Peter Ross wrote:
> +static av_cold int decode_init(AVCodecContext *avctx){
> + AnmContext *s = avctx->priv_data;
> + uint8_t *buf;
> + int i;
> +
> + avctx->pix_fmt = PIX_FMT_PAL8;
> +
> + if (avctx->extradata_size != 16*8 + 4*256)
> + return -1;
> +
> + /* manually allocate frame of width * height */
> + s->frame.reference = 1;
> + s->frame.buffer_hints = FF_BUFFER_HINTS_VALID;
> + s->frame.linesize[0] = avctx->width;
> + s->frame.data[0] = av_malloc(avctx->width*avctx->height);
> + if (!s->frame.data[0])
> + return AVERROR_NOMEM;
> + s->frame.data[1] = av_malloc(AVPALETTE_SIZE);
> + if (!s->frame.data[1]) {
> + av_freep(&s->frame.data[0]);
> + return AVERROR_NOMEM;
> + }
Supporting DR1 and using reget_buffer sure would be nicer.
Yes, you'd have to split the encoded data by lines, though I think this
should be possible without making it too ugly with a bit of refactoring.
> + buf = avctx->extradata + 16*8;
> + for(i = 0; i < 256; i++) {
> + ((int*)s->frame.data[1])[i] = AV_RL32(buf);
> + buf += 4;
> + }
Use uint32_t and bytestream_get_le32
> +static int decode_frame(AVCodecContext *avctx,
> + void *data, int *data_size,
> + AVPacket *avpkt)
> +{
> + AnmContext *s = avctx->priv_data;
> + const uint8_t *buf = avpkt->data;
> + const int buf_size = avpkt->size;
> + const uint8_t *buf_end = buf + buf_size;
> + uint8_t *dst = s->frame.data[0];
> + int d = 0;
> + int count;
> +
> + if (buf[0] != 0x42) {
> + av_log_ask_for_sample(avctx, "unknown record type\n");
> + return buf_size;
> + }
> + if (buf[1]) {
> + av_log_ask_for_sample(avctx, "padding bytes not supported\n");
> + return buf_size;
> + }
> + buf += 4;
> +
> +#define LIMITCOUNT(c) FFMIN( (c), avctx->width*avctx->height - d )
Wherever possible use functions instead of macros. Also
avctx->width*avctx->height should be stored somewhere.
Or actually I think you should use a dst_end, remove d and
increment dst.
Not to mention that creating and using bytestream_fill_byte and
bytestream_copy functions would slightly simplify the code.
> + count = bytestream_get_byte(&buf);
Well, I still consider bytestream_get_byte overkill where *buf++ would
do.
> + memcpy(dst + d, buf, LIMITCOUNT(count));
You must limit against buf size, too.
> + } else if (count == 0x80) { /* longop */
> + count = bytestream_get_le16(&buf);
> + if (count == 0) { /* stop */
> + break;
> + } else if (count < 0x8000) { /* longskip */
> + d += count;
> + } else if (count == 0x8000) {
> + av_log_ask_for_sample(avctx, "unknown opcode");
> + return AVERROR_INVALIDDATA;
> + } else if (count < 0xC000) { /* longrun */
> + count -= 0x8000;
> + memcpy(dst + d, buf, LIMITCOUNT(count));
Same as for the other memcpy
> + buf += count;
> + d += count;
> + } else { /* longdump; count==0xc000 gives nop */
> + int pixel = bytestream_get_byte(&buf);
> + count -= 0xC000;
> + memset(dst + d, pixel, LIMITCOUNT(count));
> + d += count;
> + }
These should either be ordered by probability or maybe use a
switch (count >> 14)
Also it might make sense to factor out the "count & 0x3fff" (which is
what those subtractions actually calculate).
> +#define LPF_TAG MKTAG('L','P','F',' ')
> +#define ANIM_TAG MKTAG('A','N','I','M')
> +
> +static int probe(AVProbeData *p)
> +{
> + /* verify tags and video dimensions */
> + if (AV_RL32(&p->buf[0]) == LPF_TAG &&
> + AV_RL32(&p->buf[16]) == ANIM_TAG &&
AV_RN32(p->buf ) == AV_RN32("LPF ") &&
AV_RN32(p->buf + 16) == AV_RN32("ANIM") &&
Seems nicer to me.
> + for (i = 0; i < MAX_PAGES; i++) {
> + const Page *p = &anm->pt[i];
> + if (p->nb_records > 0 && record >= p->base_record && record < p->base_record + p->nb_records) {
> + return i;
> + }
The {} and the p->nb_records > 0 are both pointless?
> +static int read_header(AVFormatContext *s,
> + AVFormatParameters *ap)
> +{
> + AnmDemuxContext *anm = s->priv_data;
> + ByteIOContext *pb = s->pb;
> + AVStream *st;
> + int i;
> +
> + if (get_le32(pb) != LPF_TAG)
> + return AVERROR_INVALIDDATA;
I'm not sure if that kind of thing should even be checked in
read_header.
> + /* video stream */
> + st = av_new_stream(s, 0);
> + if (!st)
> + return AVERROR(ENOMEM);
> + st->codec->codec_type = CODEC_TYPE_VIDEO;
> + st->codec->codec_id = CODEC_ID_ANM;
> + st->codec->codec_tag = 0; /* no fourcc */
> + st->codec->width = get_le16(pb);
> + st->codec->height = get_le16(pb);
> + if (get_byte(pb) != 0)
> + return AVERROR_INVALIDDATA;
[...]
> + /* ignore last delta record (used for looping) */
> + if (get_byte(pb)) /* has_last_delta */
> + anm->nb_records = FFMAX(anm->nb_records - 1, 0);
[...]
> + if (get_byte(pb) != 0) {
> + av_log_ask_for_sample(s, "unknown pixel type");
> + return AVERROR_INVALIDDATA;
> + }
Quite inconsistent. Also aren't you leaking the created video stream?
> + if (get_buffer(pb, st->codec->extradata, st->codec->extradata_size) < 0)
> + return AVERROR(EIO);
You should return the error that get_buffer returned.
> + /* read page table */
> + url_fseek(pb, anm->page_table_offset, SEEK_SET);
Better check the return value.
> + url_fseek(pb, anm->page_table_offset + MAX_PAGES*6 + anm->page*0x10000, SEEK_SET);
That looks rather magic. Also anm->page << 16 maybe?
> + if (!p->record_sizes) {
> + url_fskip(pb, 6); /* base_record, nb_records, size */
> + if (get_le16(pb)) {
> + av_log_ask_for_sample(s, "page_continuation_size is non-zero\n");
> + return AVERROR_INVALIDDATA;
> + }
Trailing whitespace.
> + p->record_sizes = av_malloc(sizeof(p->record_sizes[0]) * p->nb_records);
> + if (!p->record_sizes)
> + return AVERROR(ENOMEM);
> + for(j = 0; j < p->nb_records; j++)
> + p->record_sizes[j] = get_le16(pb);
The whole block could be in a separate function.
> + }else{
> + url_fskip(pb, 8 + 2*p->nb_records);
> + }
> + anm->record = 0;
> + }
> +
> + /* if we have fetched all records in this page, then find the
> + next page and repeat */
> +
> + if (anm->record >= p->nb_records) {
> + anm->page = find_record(anm, anm->pts);
> + if (anm->page < 0)
> + return anm->page;
> + anm->record = -1;
> + goto repeat;
> + }
You created a loop that never checks for EOF here. I have some doubts
this is all correct.
> + /* fetch record */
> + if (av_get_packet(s->pb, pkt, p->record_sizes[anm->record])<0)
> + return AVERROR(ENOMEM);
Certainly not! Return whichever error av_get_packet returned.
> + pkt->size = p->record_sizes[anm->record];
That's unnecessary for complete packets and completely wrong for partial
ones, possibly even introducing a crash.
> + pkt->pts = anm->pts;
> + if (pkt->pts == 0)
> + pkt->flags |= PKT_FLAG_KEY;
> +
> + anm->pts++;
> + anm->record++;
That will probably mean your code can't work with AVFMT_GENERIC_INDEX.
I really think that seeking should be working.
> + for(i = 0; i < MAX_PAGES; i++)
> + av_free(anm->pt[i].record_sizes);
av_freep just to be safe?
More information about the ffmpeg-devel
mailing list