view eatgv.c @ 11980:263b4ef7ad87 libavcodec

tablegen: implement and use WRITE_ARRAY macros Two macros (WRITE_ARRAY and WRITE_ARRAY_2D) take the prefix (modifiers) (not all tables are static, and they might not be constant either), the type, and the name of the array. It'll be copied with same name and type, and with the correct size of the currently-defined object.
author flameeyes
date Sun, 27 Jun 2010 12:21:12 +0000
parents 7dd2a45249a9
children 914f484bb476
line wrap: on
line source

/*
 * Electronic Arts TGV Video Decoder
 * Copyright (c) 2007-2008 Peter Ross
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/**
 * @file
 * Electronic Arts TGV Video Decoder
 * by Peter Ross (pross@xvid.org)
 *
 * Technical details here:
 * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGV
 */

#include "avcodec.h"
#define ALT_BITSTREAM_READER_LE
#include "get_bits.h"
#include "libavutil/lzo.h"

#define EA_PREAMBLE_SIZE    8
#define kVGT_TAG MKTAG('k', 'V', 'G', 'T')

typedef struct TgvContext {
    AVCodecContext *avctx;
    AVFrame frame;
    AVFrame last_frame;
    int width,height;
    unsigned int palette[AVPALETTE_COUNT];

    int (*mv_codebook)[2];
    unsigned char (*block_codebook)[16];
    int num_mvs;           ///< current length of mv_codebook
    int num_blocks_packed; ///< current length of block_codebook
} TgvContext;

static av_cold int tgv_decode_init(AVCodecContext *avctx){
    TgvContext *s = avctx->priv_data;
    s->avctx = avctx;
    avctx->time_base = (AVRational){1, 15};
    avctx->pix_fmt = PIX_FMT_PAL8;
    return 0;
}

/**
 * Unpack buffer
 * @return 0 on success, -1 on critical buffer underflow
 */
static int unpack(const uint8_t *src, const uint8_t *src_end, unsigned char *dst, int width, int height) {
    unsigned char *dst_end = dst + width*height;
    int size, size1, size2, av_uninit(offset), run;
    unsigned char *dst_start = dst;

    if (src[0] & 0x01)
        src += 5;
    else
        src += 2;

    if (src+3>src_end)
        return -1;
    size = AV_RB24(src);
    src += 3;

    while(size>0 && src<src_end) {

        /* determine size1 and size2 */
        size1 = (src[0] & 3);
        if ( src[0] & 0x80 ) {  // 1
            if (src[0] & 0x40 ) {  // 11
                if ( src[0] & 0x20 ) {  // 111
                    if ( src[0] < 0xFC )  // !(111111)
                        size1 = (((src[0] & 31) + 1) << 2);
                    src++;
                    size2 = 0;
                } else {  // 110
                    offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1;
                    size2 = ((src[0] & 0xC) << 6) + src[3] + 5;
                    src += 4;
                }
            } else {  // 10
                size1 = ( ( src[1] & 0xC0) >> 6 );
                offset = (AV_RB16(&src[1]) & 0x3FFF) + 1;
                size2 = (src[0] & 0x3F) + 4;
                src += 3;
            }
        } else {  // 0
            offset = ((src[0] & 0x60) << 3) + src[1] + 1;
            size2 = ((src[0] & 0x1C) >> 2) + 3;
            src += 2;
        }


        /* fetch strip from src */
        if (size1>src_end-src)
            break;

        if (size1>0) {
            size -= size1;
            run = FFMIN(size1, dst_end-dst);
            memcpy(dst, src, run);
            dst += run;
            src += run;
        }

        if (size2>0) {
            if (dst-dst_start<offset)
                return 0;
            size -= size2;
            run = FFMIN(size2, dst_end-dst);
            av_memcpy_backptr(dst, offset, run);
            dst += run;
        }
    }

    return 0;
}

/**
 * Decode inter-frame
 * @return 0 on success, -1 on critical buffer underflow
 */
static int tgv_decode_inter(TgvContext * s, const uint8_t *buf, const uint8_t *buf_end){
    unsigned char *frame0_end = s->last_frame.data[0] + s->avctx->width*s->last_frame.linesize[0];
    int num_mvs;
    int num_blocks_raw;
    int num_blocks_packed;
    int vector_bits;
    int i,j,x,y;
    GetBitContext gb;
    int mvbits;
    const unsigned char *blocks_raw;

    if(buf+12>buf_end)
        return -1;

    num_mvs           = AV_RL16(&buf[0]);
    num_blocks_raw    = AV_RL16(&buf[2]);
    num_blocks_packed = AV_RL16(&buf[4]);
    vector_bits       = AV_RL16(&buf[6]);
    buf += 12;

    /* allocate codebook buffers as neccessary */
    if (num_mvs > s->num_mvs) {
        s->mv_codebook = av_realloc(s->mv_codebook, num_mvs*2*sizeof(int));
        s->num_mvs = num_mvs;
    }

    if (num_blocks_packed > s->num_blocks_packed) {
        s->block_codebook = av_realloc(s->block_codebook, num_blocks_packed*16*sizeof(unsigned char));
        s->num_blocks_packed = num_blocks_packed;
    }

    /* read motion vectors */
    mvbits = (num_mvs*2*10+31) & ~31;

    if (buf+(mvbits>>3)+16*num_blocks_raw+8*num_blocks_packed>buf_end)
        return -1;

    init_get_bits(&gb, buf, mvbits);
    for (i=0; i<num_mvs; i++) {
        s->mv_codebook[i][0] = get_sbits(&gb, 10);
        s->mv_codebook[i][1] = get_sbits(&gb, 10);
    }
    buf += mvbits>>3;

    /* note ptr to uncompressed blocks */
    blocks_raw = buf;
    buf += num_blocks_raw*16;

    /* read compressed blocks */
    init_get_bits(&gb, buf, (buf_end-buf)<<3);
    for (i=0; i<num_blocks_packed; i++) {
        int tmp[4];
        for(j=0; j<4; j++)
            tmp[j] = get_bits(&gb, 8);
        for(j=0; j<16; j++)
            s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)];
    }

    if (get_bits_left(&gb) < vector_bits *
        (s->avctx->height/4) * (s->avctx->width/4))
        return -1;

    /* read vectors and build frame */
    for(y=0; y<s->avctx->height/4; y++)
    for(x=0; x<s->avctx->width/4; x++) {
        unsigned int vector = get_bits(&gb, vector_bits);
        const unsigned char *src;
        int src_stride;

        if (vector < num_mvs) {
            src = s->last_frame.data[0] +
                  (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] +
                   x*4 + s->mv_codebook[vector][0];
            src_stride = s->last_frame.linesize[0];
            if (src+3*src_stride+3>=frame0_end)
                continue;
        }else{
            int offset = vector - num_mvs;
            if (offset<num_blocks_raw)
                src = blocks_raw + 16*offset;
            else if (offset-num_blocks_raw<num_blocks_packed)
                src = s->block_codebook[offset-num_blocks_raw];
            else
                continue;
            src_stride = 4;
        }

        for(j=0; j<4; j++)
        for(i=0; i<4; i++)
            s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i)  ] =
               src[j*src_stride + i];
    }

    return 0;
}

/** release AVFrame buffers if allocated */
static void cond_release_buffer(AVFrame *pic)
{
    if (pic->data[0]) {
        av_freep(&pic->data[0]);
        av_free(pic->data[1]);
    }
}

static int tgv_decode_frame(AVCodecContext *avctx,
                            void *data, int *data_size,
                            AVPacket *avpkt)
{
    const uint8_t *buf = avpkt->data;
    int buf_size = avpkt->size;
    TgvContext *s = avctx->priv_data;
    const uint8_t *buf_end = buf + buf_size;
    int chunk_type;

    chunk_type = AV_RL32(&buf[0]);
    buf += EA_PREAMBLE_SIZE;

    if (chunk_type==kVGT_TAG) {
        int pal_count, i;
        if(buf+12>buf_end) {
            av_log(avctx, AV_LOG_WARNING, "truncated header\n");
            return -1;
        }

        s->width  = AV_RL16(&buf[0]);
        s->height = AV_RL16(&buf[2]);
        if (s->avctx->width!=s->width || s->avctx->height!=s->height) {
            avcodec_set_dimensions(s->avctx, s->width, s->height);
            cond_release_buffer(&s->frame);
            cond_release_buffer(&s->last_frame);
        }

        pal_count = AV_RL16(&buf[6]);
        buf += 12;
        for(i=0; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) {
            s->palette[i] = AV_RB24(buf);
            buf += 3;
        }
    }

    if (avcodec_check_dimensions(avctx, s->width, s->height))
        return -1;

    /* shuffle */
    FFSWAP(AVFrame, s->frame, s->last_frame);
    if (!s->frame.data[0]) {
        s->frame.reference = 1;
        s->frame.buffer_hints = FF_BUFFER_HINTS_VALID;
        s->frame.linesize[0] = s->width;

        /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */
        s->frame.data[0] = av_malloc(s->width*s->height + 12);
        if (!s->frame.data[0])
            return AVERROR(ENOMEM);
        s->frame.data[1] = av_malloc(AVPALETTE_SIZE);
        if (!s->frame.data[1]) {
            av_freep(&s->frame.data[0]);
            return AVERROR(ENOMEM);
        }
    }
    memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);

    if(chunk_type==kVGT_TAG) {
        s->frame.key_frame = 1;
        s->frame.pict_type = FF_I_TYPE;
        if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) {
            av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n");
            return -1;
        }
    }else{
        if (!s->last_frame.data[0]) {
            av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n");
            return buf_size;
        }
        s->frame.key_frame = 0;
        s->frame.pict_type = FF_P_TYPE;
        if (tgv_decode_inter(s, buf, buf_end)<0) {
            av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n");
            return -1;
        }
    }

    *data_size = sizeof(AVFrame);
    *(AVFrame*)data = s->frame;

    return buf_size;
}

static av_cold int tgv_decode_end(AVCodecContext *avctx)
{
    TgvContext *s = avctx->priv_data;
    cond_release_buffer(&s->frame);
    cond_release_buffer(&s->last_frame);
    av_free(s->mv_codebook);
    av_free(s->block_codebook);
    return 0;
}

AVCodec eatgv_decoder = {
    "eatgv",
    AVMEDIA_TYPE_VIDEO,
    CODEC_ID_TGV,
    sizeof(TgvContext),
    tgv_decode_init,
    NULL,
    tgv_decode_end,
    tgv_decode_frame,
    .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"),
};