libavcodec/eatgv.c
cfc78718
 /*
  * 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
  */
 
 /**
ba87f080
  * @file
cfc78718
  * Electronic Arts TGV Video Decoder
8600106a
  * by Peter Ross (pross@xvid.org)
cfc78718
  *
  * Technical details here:
  * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGV
  */
 
b6686629
 #include "libavutil/imgutils.h"
 #include "libavutil/mem.h"
 
aaf47bcd
 #define BITSTREAM_READER_LE
b6686629
 #include "avcodec.h"
9106a698
 #include "get_bits.h"
759001c5
 #include "internal.h"
cfc78718
 
 #define EA_PREAMBLE_SIZE    8
 #define kVGT_TAG MKTAG('k', 'V', 'G', 'T')
 
 typedef struct TgvContext {
     AVCodecContext *avctx;
97168b20
     AVFrame *last_frame;
759001c5
     uint8_t *frame_buffer;
cfc78718
     int width,height;
f1c39594
     uint32_t palette[AVPALETTE_COUNT];
cfc78718
 
     int (*mv_codebook)[2];
f1c39594
     uint8_t (*block_codebook)[16];
cfc78718
     int num_mvs;           ///< current length of mv_codebook
     int num_blocks_packed; ///< current length of block_codebook
 } TgvContext;
 
4b7598e2
 static av_cold int tgv_decode_init(AVCodecContext *avctx)
 {
cfc78718
     TgvContext *s = avctx->priv_data;
4b7598e2
     s->avctx         = avctx;
7ea1b347
     avctx->framerate = (AVRational){ 15, 1 };
4b7598e2
     avctx->pix_fmt   = AV_PIX_FMT_PAL8;
97168b20
 
     s->last_frame = av_frame_alloc();
     if (!s->last_frame)
         return AVERROR(ENOMEM);
 
cfc78718
     return 0;
 }
 
 /**
  * Unpack buffer
  * @return 0 on success, -1 on critical buffer underflow
  */
4b7598e2
 static int unpack(const uint8_t *src, const uint8_t *src_end,
f1c39594
                   uint8_t *dst, int width, int height)
4b7598e2
 {
f1c39594
     uint8_t *dst_end = dst + width*height;
a8798c7e
     int size, size1, size2, offset, run;
f1c39594
     uint8_t *dst_start = dst;
cfc78718
 
     if (src[0] & 0x01)
         src += 5;
     else
         src += 2;
 
6bfe0d4c
     if (src_end - src < 3)
ade40280
         return AVERROR_INVALIDDATA;
cfc78718
     size = AV_RB24(src);
     src += 3;
 
4b7598e2
     while (size > 0 && src < src_end) {
cfc78718
 
         /* determine size1 and size2 */
         size1 = (src[0] & 3);
4b7598e2
         if (src[0] & 0x80) {  // 1
cfc78718
             if (src[0] & 0x40 ) {  // 11
4b7598e2
                 if (src[0] & 0x20) {  // 111
                     if (src[0] < 0xFC)  // !(111111)
cfc78718
                         size1 = (((src[0] & 31) + 1) << 2);
                     src++;
                     size2 = 0;
                 } else {  // 110
                     offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1;
4b7598e2
                     size2  = ((src[0] & 0xC) << 6) + src[3] + 5;
                     src   += 4;
cfc78718
                 }
             } else {  // 10
4b7598e2
                 size1  = ((src[1] & 0xC0) >> 6);
cfc78718
                 offset = (AV_RB16(&src[1]) & 0x3FFF) + 1;
4b7598e2
                 size2  = (src[0] & 0x3F) + 4;
                 src   += 3;
cfc78718
             }
         } else {  // 0
             offset = ((src[0] & 0x60) << 3) + src[1] + 1;
4b7598e2
             size2  = ((src[0] & 0x1C) >> 2) + 3;
             src   += 2;
cfc78718
         }
 
 
         /* fetch strip from src */
4b7598e2
         if (size1 > src_end - src)
cfc78718
             break;
 
4b7598e2
         if (size1 > 0) {
cfc78718
             size -= size1;
4b7598e2
             run   = FFMIN(size1, dst_end - dst);
cfc78718
             memcpy(dst, src, run);
             dst += run;
             src += run;
         }
 
4b7598e2
         if (size2 > 0) {
             if (dst - dst_start < offset)
cfc78718
                 return 0;
             size -= size2;
4b7598e2
             run   = FFMIN(size2, dst_end - dst);
cfc78718
             av_memcpy_backptr(dst, offset, run);
             dst += run;
         }
     }
 
     return 0;
 }
 
 /**
  * Decode inter-frame
  * @return 0 on success, -1 on critical buffer underflow
  */
759001c5
 static int tgv_decode_inter(TgvContext *s, AVFrame *frame,
                             const uint8_t *buf, const uint8_t *buf_end)
4b7598e2
 {
cfc78718
     int num_mvs;
     int num_blocks_raw;
     int num_blocks_packed;
     int vector_bits;
     int i,j,x,y;
     GetBitContext gb;
     int mvbits;
f1c39594
     const uint8_t *blocks_raw;
cfc78718
 
6bfe0d4c
     if(buf_end - buf < 12)
ade40280
         return AVERROR_INVALIDDATA;
cfc78718
 
     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;
 
b962932c
     if (vector_bits > MIN_CACHE_BITS || !vector_bits) {
71a3c59e
         av_log(s->avctx, AV_LOG_ERROR,
                "Invalid value for motion vector bits: %d\n", vector_bits);
b962932c
         return AVERROR_INVALIDDATA;
     }
 
da9cea77
     /* allocate codebook buffers as necessary */
cfc78718
     if (num_mvs > s->num_mvs) {
10531d48
         int err = av_reallocp_array(&s->mv_codebook, num_mvs, sizeof(*s->mv_codebook));
         if (err < 0) {
a5615b82
             s->num_mvs = 0;
7fccc96d
             return err;
a5615b82
         }
cfc78718
         s->num_mvs = num_mvs;
     }
 
     if (num_blocks_packed > s->num_blocks_packed) {
9b8d11a7
         int err;
         if ((err = av_reallocp(&s->block_codebook, num_blocks_packed * 16)) < 0) {
a5615b82
             s->num_blocks_packed = 0;
9b8d11a7
             return err;
a5615b82
         }
cfc78718
         s->num_blocks_packed = num_blocks_packed;
     }
 
     /* read motion vectors */
4b7598e2
     mvbits = (num_mvs * 2 * 10 + 31) & ~31;
cfc78718
 
325ee4ed
     if (buf_end - buf < (mvbits>>3) + 16*num_blocks_raw + 8*num_blocks_packed)
ade40280
         return AVERROR_INVALIDDATA;
cfc78718
 
     init_get_bits(&gb, buf, mvbits);
4b7598e2
     for (i = 0; i < num_mvs; i++) {
cfc78718
         s->mv_codebook[i][0] = get_sbits(&gb, 10);
         s->mv_codebook[i][1] = get_sbits(&gb, 10);
     }
4b7598e2
     buf += mvbits >> 3;
cfc78718
 
     /* note ptr to uncompressed blocks */
     blocks_raw = buf;
4b7598e2
     buf       += num_blocks_raw * 16;
cfc78718
 
     /* read compressed blocks */
4b7598e2
     init_get_bits(&gb, buf, (buf_end - buf) << 3);
     for (i = 0; i < num_blocks_packed; i++) {
cfc78718
         int tmp[4];
4b7598e2
         for (j = 0; j < 4; j++)
cfc78718
             tmp[j] = get_bits(&gb, 8);
4b7598e2
         for (j = 0; j < 16; j++)
cfc78718
             s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)];
     }
 
4784b092
     if (get_bits_left(&gb) < vector_bits *
4b7598e2
         (s->avctx->height / 4) * (s->avctx->width / 4))
ade40280
         return AVERROR_INVALIDDATA;
4784b092
 
cfc78718
     /* read vectors and build frame */
4b7598e2
     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);
f1c39594
             const uint8_t *src;
a339e919
             ptrdiff_t src_stride;
4b7598e2
 
             if (vector < num_mvs) {
                 int mx = x * 4 + s->mv_codebook[vector][0];
                 int my = y * 4 + s->mv_codebook[vector][1];
 
                 if (mx < 0 || mx + 4 > s->avctx->width ||
325ee4ed
                     my < 0 || my + 4 > s->avctx->height) {
                     av_log(s->avctx, AV_LOG_ERROR, "MV %d %d out of picture\n", mx, my);
4b7598e2
                     continue;
325ee4ed
                 }
a390aa0e
 
97168b20
                 src = s->last_frame->data[0] + mx + my * s->last_frame->linesize[0];
                 src_stride = s->last_frame->linesize[0];
4b7598e2
             } 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;
             }
cfc78718
 
4b7598e2
             for (j = 0; j < 4; j++)
                 for (i = 0; i < 4; i++)
759001c5
                     frame->data[0][(y * 4 + j) * frame->linesize[0] + (x * 4 + i)] =
4b7598e2
                         src[j * src_stride + i];
cfc78718
     }
 
     return 0;
 }
 
 static int tgv_decode_frame(AVCodecContext *avctx,
df9b9567
                             void *data, int *got_frame,
7a00bbad
                             AVPacket *avpkt)
cfc78718
 {
4b7598e2
     const uint8_t *buf     = avpkt->data;
     int buf_size           = avpkt->size;
     TgvContext *s          = avctx->priv_data;
cfc78718
     const uint8_t *buf_end = buf + buf_size;
759001c5
     AVFrame *frame         = data;
ade40280
     int chunk_type, ret;
cfc78718
 
6bfe0d4c
     if (buf_end - buf < EA_PREAMBLE_SIZE)
         return AVERROR_INVALIDDATA;
 
cfc78718
     chunk_type = AV_RL32(&buf[0]);
4b7598e2
     buf       += EA_PREAMBLE_SIZE;
cfc78718
 
4b7598e2
     if (chunk_type == kVGT_TAG) {
cfc78718
         int pal_count, i;
6bfe0d4c
         if(buf_end - buf < 12) {
cfc78718
             av_log(avctx, AV_LOG_WARNING, "truncated header\n");
ade40280
             return AVERROR_INVALIDDATA;
cfc78718
         }
 
         s->width  = AV_RL16(&buf[0]);
         s->height = AV_RL16(&buf[2]);
4b7598e2
         if (s->avctx->width != s->width || s->avctx->height != s->height) {
759001c5
             av_freep(&s->frame_buffer);
97168b20
             av_frame_unref(s->last_frame);
a8773938
             if ((ret = ff_set_dimensions(s->avctx, s->width, s->height)) < 0)
                 return ret;
cfc78718
         }
 
         pal_count = AV_RL16(&buf[6]);
         buf += 12;
325ee4ed
         for(i = 0; i < pal_count && i < AVPALETTE_COUNT && buf_end - buf >= 3; i++) {
b12d92ef
             s->palette[i] = 0xFFU << 24 | AV_RB24(buf);
cfc78718
             buf += 3;
         }
2a43d9f8
         if (buf_end - buf < 5) {
             return AVERROR_INVALIDDATA;
         }
cfc78718
     }
 
759001c5
     if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
         return ret;
cfc78718
 
759001c5
     memcpy(frame->data[1], s->palette, AVPALETTE_SIZE);
cfc78718
 
4b7598e2
     if (chunk_type == kVGT_TAG) {
759001c5
         int y;
         frame->key_frame = 1;
         frame->pict_type = AV_PICTURE_TYPE_I;
 
         if (!s->frame_buffer &&
bb9f5516
             !(s->frame_buffer = av_mallocz(s->width * s->height)))
759001c5
             return AVERROR(ENOMEM);
 
         if (unpack(buf, buf_end, s->frame_buffer, s->avctx->width, s->avctx->height) < 0) {
cfc78718
             av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n");
ade40280
             return AVERROR_INVALIDDATA;
cfc78718
         }
759001c5
         for (y = 0; y < s->height; y++)
             memcpy(frame->data[0]  + y * frame->linesize[0],
                    s->frame_buffer + y * s->width,
                    s->width);
4b7598e2
     } else {
97168b20
         if (!s->last_frame->data[0]) {
cfc78718
             av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n");
             return buf_size;
         }
759001c5
         frame->key_frame = 0;
         frame->pict_type = AV_PICTURE_TYPE_P;
         if (tgv_decode_inter(s, frame, buf, buf_end) < 0) {
cfc78718
             av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n");
ade40280
             return AVERROR_INVALIDDATA;
cfc78718
         }
     }
 
97168b20
     av_frame_unref(s->last_frame);
     if ((ret = av_frame_ref(s->last_frame, frame)) < 0)
759001c5
         return ret;
 
df9b9567
     *got_frame = 1;
cfc78718
 
     return buf_size;
 }
 
 static av_cold int tgv_decode_end(AVCodecContext *avctx)
 {
     TgvContext *s = avctx->priv_data;
97168b20
     av_frame_free(&s->last_frame);
759001c5
     av_freep(&s->frame_buffer);
acea53fe
     av_freep(&s->mv_codebook);
     av_freep(&s->block_codebook);
cfc78718
     return 0;
 }
 
e7e2df27
 AVCodec ff_eatgv_decoder = {
ec6402b7
     .name           = "eatgv",
b2bed932
     .long_name      = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"),
ec6402b7
     .type           = AVMEDIA_TYPE_VIDEO,
36ef5369
     .id             = AV_CODEC_ID_TGV,
ec6402b7
     .priv_data_size = sizeof(TgvContext),
     .init           = tgv_decode_init,
     .close          = tgv_decode_end,
     .decode         = tgv_decode_frame,
def97856
     .capabilities   = AV_CODEC_CAP_DR1,
cfc78718
 };