[FFmpeg-devel] [PATCH] PAF demuxer and decoder

Paul B Mahol onemda at gmail.com
Wed Jul 4 00:57:10 CEST 2012


On 7/3/12, Michael Niedermayer <michaelni at gmx.at> wrote:
> On Mon, Jul 02, 2012 at 02:28:32AM +0000, Paul B Mahol wrote:
>> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> [...]
>> +static int decode_0(AVCodecContext *avctx, uint8_t code, uint8_t *pkt)
>> +{
>> +    PAFVideoDecContext *c = avctx->priv_data;
>> +    uint32_t opcode_size, offset;
>> +    uint8_t *dst, *dend, mask = 0, color = 0, a, b, p;
>> +    const uint8_t *src, *send, *opcodes;
>> +    int cnt, i, j, x = 0;
>> +
>> +    cnt = bytestream2_get_byte(&c->gb);
>> +    if (cnt) {
>> +        if (code & 0x10) {
>> +            int align;
>> +
>> +            align = bytestream2_tell(&c->gb) & 3;
>> +            if (align)
>> +                bytestream2_skip(&c->gb, 4 - align);
>> +        }
>> +        do {
>> +            uint32_t cnt;
>> +
>> +            a      = bytestream2_get_byte(&c->gb);
>> +            b      = bytestream2_get_byte(&c->gb);
>> +            p      = (a & 0xC0) >> 6;
>> +            dst    = c->frame[p] + get_video_page_offset(avctx, a, b);
>> +            dend   = c->frame[p] + c->frame_size;
>> +            offset = (b & 0x7F) * 2;
>> +            cnt = bytestream2_get_le16(&c->gb) + offset;
>> +
>> +            do {
>> +                offset++;
>> +                if (dst + 3 * avctx->width + 4 > dend)
>> +                    return AVERROR_INVALIDDATA;
>> +                copy4h(avctx, dst);
>> +                if ((offset & 0x3F) == 0)
>> +                    dst += avctx->width * 3;
>> +                dst += 4;
>> +            } while (offset < cnt);
>> +        } while (--cnt);
>> +    }
>> +
>> +    dst = c->frame[c->current_frame];
>> +    do {
>> +        a    = bytestream2_get_byte(&c->gb);
>> +        b    = bytestream2_get_byte(&c->gb);
>> +        p    = (a & 0xC0) >> 6;
>> +        src  = c->frame[p] + get_video_page_offset(avctx, a, b);
>> +        send = c->frame[p] + c->frame_size;
>> +        if (src + 3 * avctx->width + 4 > send)
>> +            return AVERROR_INVALIDDATA;
>> +        copy_block4(dst, src, avctx->width, avctx->width, 4);
>> +        cnt++;
>> +        if ((cnt & 0x3F) == 0)
>> +            dst += avctx->width * 3;
>> +        dst += 4;
>> +    } while (cnt < c->video_size / 16);
>> +
>> +    opcode_size = bytestream2_get_le16(&c->gb);
>> +    bytestream2_skip(&c->gb, 2);
>> +
>> +    if (bytestream2_get_bytes_left(&c->gb) < opcode_size)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    opcodes = pkt + bytestream2_tell(&c->gb);
>> +    bytestream2_skipu(&c->gb, opcode_size);
>> +
>> +    dst = c->frame[c->current_frame];
>> +
>> +    for (i = 0; i < avctx->height; i += 4, dst += avctx->width * 3) {
>> +        for (j = 0; j < avctx->width; j += 4, dst += 4) {
>
> this seems to assume dimensions are a multiple of 4, paf_vid_init
> should check this so not too much is written

Agreed, fixed.
>
>
> [...]
>> +AVCodec ff_paf_video_decoder = {
>> +    .name           = "paf_video",
>> +    .type           = AVMEDIA_TYPE_VIDEO,
>> +    .id             = CODEC_ID_PAF_VIDEO,
>> +    .priv_data_size = sizeof(PAFVideoDecContext),
>> +    .init           = paf_vid_init,
>> +    .close          = paf_vid_close,
>> +    .decode         = paf_vid_decode,
>> +    .long_name      = NULL_IF_CONFIG_SMALL("Amazing Studio Packed
>> Animation File Video"),
>> +};
>> +
>> +AVCodec ff_paf_audio_decoder = {
>> +    .name           = "paf_audio",
>> +    .type           = AVMEDIA_TYPE_AUDIO,
>> +    .id             = CODEC_ID_PAF_AUDIO,
>> +    .priv_data_size = sizeof(PAFAudioDecContext),
>> +    .init           = paf_aud_init,
>> +    .decode         = paf_aud_decode,
>> +    .capabilities   = CODEC_CAP_DR1,
>> +    .long_name      = NULL_IF_CONFIG_SMALL("Amazing Studio Packed
>> Animation File Audio"),
>> +};
>
> the audio decoder could be split into a seperate file

I prefer to keep game video and audio decoders in same file
(like how it is done with bmv). paf audio decoder is so trivail that it
does not deserve separate file.

>
>
> [...]
>> +static int read_header(AVFormatContext *s)
>> +{
>> +    PAFDemuxContext *p = s->priv_data;
>> +    AVIOContext     *pb = s->pb;
>> +    AVStream        *ast, *vst;
>> +    int             ret = 0;
>> +
>> +    avio_skip(pb, 132);
>> +
>> +    vst = avformat_new_stream(s, 0);
>> +    if (!vst)
>> +        return AVERROR(ENOMEM);
>> +
>> +    vst->start_time = 0;
>> +    vst->nb_frames  =
>> +    vst->duration   =
>> +    p->nb_frames = avio_rl32(pb);
>> +    avio_skip(pb, 4);
>> +    vst->codec->width  = avio_rl32(pb);
>> +    vst->codec->height = avio_rl32(pb);
>> +    avio_skip(pb, 4);
>> +    vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
>> +    vst->codec->codec_tag  = 0;
>> +    vst->codec->codec_id   = CODEC_ID_PAF_VIDEO;
>> +    avpriv_set_pts_info(vst, 64, 1, 10);
>> +
>> +    ast = avformat_new_stream(s, 0);
>> +    if (!ast)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ast->start_time         = 0;
>> +    ast->codec->codec_type  = AVMEDIA_TYPE_AUDIO;
>> +    ast->codec->codec_tag   = 0;
>> +    ast->codec->codec_id    = CODEC_ID_PAF_AUDIO;
>> +    ast->codec->channels    = 2;
>> +    ast->codec->sample_rate = 22050;
>> +    avpriv_set_pts_info(ast, 64, 1, 22050);
>> +
>> +    p->buffer_size     = avio_rl32(pb);
>> +    if (p->buffer_size < 175 || p->buffer_size > 2048)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    p->preload_count  = avio_rl32(pb);
>> +    p->frame_blks     = avio_rl32(pb);
>> +    p->start_offset   = avio_rl32(pb);
>> +    p->max_video_blks = avio_rl32(pb);
>> +    p->max_audio_blks = avio_rl32(pb);
>> +    if (p->max_audio_blks < 1 ||
>> +        p->max_video_blks < 1 ||
>> +        p->frame_blks     < 1 ||
>> +        p->nb_frames      < 1 ||
>> +        p->preload_count  < 1)
>> +        return AVERROR_INVALIDDATA;
>> +
>
>> +    p->blocks_count_table  = av_mallocz(p->nb_frames  *
>> sizeof(uint32_t));
>> +    p->frames_offset_table = av_mallocz(p->nb_frames  *
>> sizeof(uint32_t));
>> +    p->blocks_offset_table = av_mallocz(p->frame_blks *
>> sizeof(uint32_t));
>
> possibke interger overflows in the multiplication can lead to out of
> array writes later
>
Fixed.

>
> [...]
>> +    size = p->video_size - p->frames_offset_table[p->current_frame];
>> +    if (size < 0)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    if (av_new_packet(pkt, size) < 0)
>> +        return AVERROR(ENOMEM);
>> +
>> +    pkt->stream_index = 0;
>> +    pkt->duration     = 1;
>> +    memcpy(pkt->data, p->video_frame +
>> p->frames_offset_table[p->current_frame], size);
>
> this creates video frames that are much larger than needed

Unfortunately using next video frame offset does not works always beacuse
it eats frame data (type 4 compression) and decoded output is black for
couple of first frames.

Also clearing video_frame/audio_frame after demuxing each packet
will break decoding too, so no trivial implementation of seeking is possible.

>
> rest looks ok


More information about the ffmpeg-devel mailing list