[FFmpeg-devel] [PATCH] Electronic Arts TGQ decoder

Michael Niedermayer michaelni
Sat Sep 27 03:23:18 CEST 2008


On Sat, Sep 27, 2008 at 10:12:34AM +1000, Peter Ross wrote:
> Patches enclosed.
> 
> Info: http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGQ
> Samples: http://samples.mplayerhq.hu/game-formats/ea-tgq-uv/

[...]
> +const uint8_t ea_zigzag_scan[64]={
> +   0,  8,  1,  2,  9, 16, 24, 17,
> +  10,  3,  4, 11, 18, 25, 32, 40,
> +  33, 26, 19, 12,  5,  6, 13, 20,
> +  27, 34, 41, 48, 56, 49, 42, 35,
> +  28, 21, 14,  7, 15, 22, 29, 36,
> +  43, 50, 57, 58, 51, 44, 37, 30,
> +  23, 31, 38, 45, 52, 59, 60, 53,
> +  46, 39, 47, 54, 61, 62, 55, 63,
> +};

private non static things should have ff_ prefixes


> +
> +const int ea_base_qtable[64]={
> +   8192,   5906,   6270,   6967,   8192,  10426,  15137,  29692,
> +   5906,   4258,   4520,   5023,   5906,   7517,  10913,  21407,
> +   6270,   4520,   4799,   5332,   6270,   7980,  11585,  22725,
> +   6967,   5023,   5332,   5925,   6967,   8867,  12873,  25251,
> +   8192,   5906,   6270,   6967,   8192,  10426,  15137,  29692,
> +  10426,   7517,   7980,   8867,  10426,  13270,  19266,  37791,
> +  15137,  10913,  11585,  12873,  15137,  19266,  27969,  54864,
> +  29692,  21407,  22725,  25251,  29692,  37791,  54864, 107619,
> +};

duplicate of inv_aanscales


> +
> +const uint8_t tqi_coeff_vlc_tab[113][2]={
> +  {0x02, 2},//EOB
> +  {0x01, 6},//escape
> +  {0x03, 2},
> +  {0x03, 3},
> +  {0x04, 4},{0x05, 4},
> +  {0x05, 5},{0x06, 5},{0x07, 5},
> +  {0x04, 6},{0x05, 6},{0x06, 6},{0x07, 6},
> +  {0x04, 7},{0x05, 7},{0x06, 7},{0x07, 7},
> +  {0x20, 8},{0x21, 8},{0x22, 8},{0x23, 8},{0x24, 8},{0x25, 8},{0x26, 8},{0x27, 8},
> +  {0x08,10},{0x09,10},{0x0A,10},{0x0B,10},{0x0C,10},{0x0D,10},{0x0E,10},{0x0F,10},
> +  {0x10,12},{0x11,12},{0x12,12},{0x13,12},{0x14,12},{0x15,12},{0x16,12},{0x17,12},
> +  {0x18,12},{0x19,12},{0x1A,12},{0x1B,12},{0x1C,12},{0x1D,12},{0x1E,12},{0x1F,12},
> +  {0x10,13},{0x11,13},{0x12,13},{0x13,13},{0x14,13},{0x15,13},{0x16,13},{0x17,13},
> +  {0x18,13},{0x19,13},{0x1A,13},{0x1B,13},{0x1C,13},{0x1D,13},{0x1E,13},{0x1F,13},
> +  {0x10,14},{0x11,14},{0x12,14},{0x13,14},{0x14,14},{0x15,14},{0x16,14},{0x17,14},
> +  {0x18,14},{0x19,14},{0x1A,14},{0x1B,14},{0x1C,14},{0x1D,14},{0x1E,14},{0x1F,14},
> +  {0x10,15},{0x11,15},{0x12,15},{0x13,15},{0x14,15},{0x15,15},{0x16,15},{0x17,15},
> +  {0x18,15},{0x19,15},{0x1A,15},{0x1B,15},{0x1C,15},{0x1D,15},{0x1E,15},{0x1F,15},
> +  {0x10,16},{0x11,16},{0x12,16},{0x13,16},{0x14,16},{0x15,16},{0x16,16},{0x17,16},
> +  {0x18,16},{0x19,16},{0x1A,16},{0x1B,16},{0x1C,16},{0x1D,16},{0x1E,16},{0x1F,16},
> +};

looks like a duplicate of mpeg1_vlc[]
the other tables and related code are possibly as well duplicates of mpeg1.


[...]

> +/** Electronic Arts TGQ/TQI/MAD IDCT algorithm */
> +
> +#define A4 1.3065630f
> +#define A2 0.5411961f
> +#define A5 0.3826834f
> +
> +#if 0
> +/* not portable, but retained for bit-for-bit compatibility on x86 */
> +#define DIV_SQRT2(x)  ((((int64_t)(x)*0x5a82799aL)>>32)<<1)
> +static int EA_FTOL(double d) {
> +  int result;
> +  d += 6755399441055744.0f;
> +  memcpy(&result, &d, 4);
> +  return result;
> +}
> +#else
> +#define DIV_SQRT2(x)  ((int)((x)/1.41421356237309514547))
> +#define EA_FTOL(x)    floor(x)
> +#endif
> +
> +#define IDCT_TRANSFORM(dest,d0,d1,d2,d3,d4,d5,d6,d7,munge,src) {\
> +    const int src7add1 = (src)[7] + (src)[1]; \
> +    const int src3add5 = (src)[3] + (src)[5]; \
> +    const int value1 = DIV_SQRT2(src7add1 - src3add5); \
> +    const int value2 = DIV_SQRT2((src)[2] - (src)[6]); \
> +    const int src1sub7 = (src)[1] - (src)[7]; \
> +    const int src5sub3 = (src)[5] - (src)[3]; \
> +    const int result0 = EA_FTOL( src5sub3*A2 + (src5sub3+src1sub7)*A5 ); \
> +    const int result2 = EA_FTOL( src1sub7*A4 - (src5sub3+src1sub7)*A5 ); \
> +    const int b0 = result2 + src3add5 + src7add1; \
> +    const int b1 = result2 + value1; \
> +    const int b2 = result0 + value1; \
> +    const int b3 = result0; \
> +    const int src0add4 = (src)[0] + (src)[4]; \
> +    const int src0sub4 = (src)[0] - (src)[4]; \
> +    const int src26value2 = (src)[2] + (src)[6] + value2; \
> +    const int a0 = src0add4 + src26value2; \
> +    const int a1 = src0sub4 + value2; \
> +    const int a2 = src0sub4 - value2; \
> +    const int a3 = src0add4 - src26value2; \
> +    (dest)[d0] = munge(a0 + b0); \
> +    (dest)[d1] = munge(a1 + b1); \
> +    (dest)[d2] = munge(a2 + b2); \
> +    (dest)[d3] = munge(a3 + b3); \
> +    (dest)[d4] = munge(a3 - b3); \
> +    (dest)[d5] = munge(a2 - b2); \
> +    (dest)[d6] = munge(a1 - b1); \
> +    (dest)[d7] = munge(a0 - b0); \
> +}
> +/* end IDCT_TRANSFORM macro */
> +
> +#define MUNGE_NONE(x) (x)
> +#define IDCT_ROW(dest,src)  IDCT_TRANSFORM(dest,0,8,16,24,32,40,48,56,MUNGE_NONE,src)
> +
> +#define MUNGE_16(x)   av_clip_uint8((x)>>16)
> +#define IDCT_COL(dest,src)  IDCT_TRANSFORM(dest,0,1, 2, 3, 4, 5, 6, 7,MUNGE_16,src)
> +
> +static inline void idct_row(int *dest, const int *src) {
> +    if ((src[1]|src[2]|src[3]|src[4]|src[5]|src[6]|src[7])==0) {
> +        dest[0] =
> +        dest[8] =
> +        dest[16] =
> +        dest[24] =
> +        dest[32] =
> +        dest[40] =
> +        dest[48] =
> +        dest[56] = src[0];
> +    }else{
> +        IDCT_ROW(dest, src);
> +    }
> +}

If you want to add a new IDCT, that should be done cleanly through dsputil.


> +
> +void ea_idct16_put(uint8_t *dst, int linesize, const int *block) {
> +    int i;
> +    int temp[64];
> +    for (i=0; i<8; i++)
> +        idct_row(&temp[i], &block[i*8]);
> +    for (i=0; i<8; i++)
> +        IDCT_COL( (&dst[i*linesize]), (&temp[8*i]) );
> +}

unused


[...]
> +static void tgq_decode_block(TgqContext *s, int block[64], GetBitContext *gb){
> +    int i,j,value;

> +    block[0] = (get_sbits(gb,8)) * s->qtable[0];

superflous ()


> +    for(i=1; i<64; ) {
> +        switch(show_bits(gb,3)) {
> +        case 0:
> +            skip_bits(gb,3);
> +            block[ea_zigzag_scan[i++]] = 0;
> +            break;
> +        case 5:  /* see case 1 for skip bits */
> +            block[ea_zigzag_scan[i++]] = 0;
> +        case 1:
> +            skip_bits(gb,3);
> +            value = 2*get_bits(gb,5);
> +            for(j=0; j<value; j++)
> +                block[ea_zigzag_scan[i++]] = 0;
> +            break;
> +        case 2:
> +            skip_bits(gb,3);
> +            block[ea_zigzag_scan[i]] = s->qtable[ea_zigzag_scan[i]];
> +            i++;
> +            break;
> +        case 3: // 011b
> +        case 7: // 111b

> +            skip_bits(gb,2);
> +            if (show_bits(gb,6)==0x3F) {
> +              skip_bits(gb, 6);
> +              block[ea_zigzag_scan[i]] = get_sbits(gb,8) * s->qtable[ea_zigzag_scan[i]];
> +            }else{
> +              block[ea_zigzag_scan[i]] = get_sbits(gb,6) * s->qtable[ea_zigzag_scan[i]];
> +            }

indention is inconsistant


> +            i++;
> +            break;

> +        case 4:
> +            skip_bits(gb,3);
> +            block[ea_zigzag_scan[i++]] = 0;
> +            block[ea_zigzag_scan[i++]] = 0;
> +            break;

this case can be merged with case 0
I also think it then shows a interresting symmetry with case 1/5 that
likely allows more simplifications


> +        case 6:
> +            skip_bits(gb,3);
> +            block[ea_zigzag_scan[i]] = -(s->qtable[ea_zigzag_scan[i]]);
> +            i++;
> +            break;
> +        }
> +    }
> +}
> +
> +static void tgq_idct_put_mb(TgqContext *s, int (*block)[64], int mb_x, int mb_y){
> +    int linesize= s->frame.linesize[0];
> +    uint8_t *dest_y  = s->frame.data[0] + (mb_y * 16* linesize            ) + mb_x * 16;
> +    uint8_t *dest_cb = s->frame.data[1] + (mb_y * 8 * s->frame.linesize[1]) + mb_x * 8;
> +    uint8_t *dest_cr = s->frame.data[2] + (mb_y * 8 * s->frame.linesize[2]) + mb_x * 8;
> +
> +    ea_idct16_put_128(dest_y                 , linesize, block[0]);
> +    ea_idct16_put_128(dest_y              + 8, linesize, block[1]);
> +    ea_idct16_put_128(dest_y + 8*linesize    , linesize, block[2]);
> +    ea_idct16_put_128(dest_y + 8*linesize + 8, linesize, block[3]);
> +    if(!(s->avctx->flags&CODEC_FLAG_GRAY)){
> +         ea_idct16_put_128(dest_cb, s->frame.linesize[1], block[4]);
> +         ea_idct16_put_128(dest_cr, s->frame.linesize[2], block[5]);
> +    }
> +}
> +
> +static inline void tgq_dconly(TgqContext *s, unsigned char *dst, int dst_stride, int dc){
> +    int j;
> +    for(j=0;j<8;j++)
> +        memset(dst+j*dst_stride, dc, 8);
> +}
> +
> +static inline void tgq_dconly_block(TgqContext *s, int mb_x, int mb_y, int i, int dc_level){
> +    int linesize= s->frame.linesize[0];
> +    uint8_t *dest_y  = s->frame.data[0] + (mb_y * 16* linesize            ) + mb_x * 16;
> +    uint8_t *dest_cb = s->frame.data[1] + (mb_y * 8 * s->frame.linesize[1]) + mb_x * 8;
> +    uint8_t *dest_cr = s->frame.data[2] + (mb_y * 8 * s->frame.linesize[2]) + mb_x * 8;
> +    int dc = av_clip_uint8(128 + ((dc_level*s->qtable[0]) >> 16));
> +
> +    switch(i) {
> +    case 0: tgq_dconly(s,dest_y                 , linesize, dc); break;
> +    case 1: tgq_dconly(s,dest_y              + 8, linesize, dc); break;
> +    case 2: tgq_dconly(s,dest_y + 8*linesize    , linesize, dc); break;
> +    case 3: tgq_dconly(s,dest_y + 8*linesize + 8, linesize, dc); break;
> +    case 4: if(!(s->avctx->flags&CODEC_FLAG_GRAY))
> +                tgq_dconly(s,dest_cb, s->frame.linesize[1], dc);
> +            break;
> +    case 5: if(!(s->avctx->flags&CODEC_FLAG_GRAY))
> +                tgq_dconly(s,dest_cr, s->frame.linesize[2], dc);
> +            break;
> +    }
> +}
> +
> +static void tgq_decode_mb(TgqContext *s, int mb_y, int mb_x, const int8_t **bs, const int8_t *buf_end){
> +    int mode;
> +    int i; // block counter
> +    int block[6][64];
> +
> +    mode = bytestream_get_byte((const uint8_t**)bs);
> +    if (mode>buf_end-*bs) {
> +        av_log(s->avctx, AV_LOG_ERROR, "truncated macroblock\n");
> +        return;
> +    }
> +    if (mode==3) {
> +        for(i=0; i<4;i++)
> +            tgq_dconly_block(s, mb_x, mb_y, i, (*bs)[0]);
> +        tgq_dconly_block(s, mb_x, mb_y, 4, (*bs)[1]);
> +        tgq_dconly_block(s, mb_x, mb_y, 5, (*bs)[2]);
> +    }else if (mode==6) {
> +        for(i=0; i<6;i++)
> +            tgq_dconly_block(s, mb_x, mb_y, i, (*bs)[i]);
> +    }else if (mode==12) {
> +        for(i=0; i<6;i++)
> +            tgq_dconly_block(s, mb_x, mb_y, i, (*bs)[i*2]);
> +    }else if (mode>12) {
> +        GetBitContext gb;
> +        init_get_bits(&gb, *bs, mode*8);
> +        for(i=0; i<6; i++)
> +            tgq_decode_block(s, block[i], &gb);
> +        tgq_idct_put_mb(s, block, mb_x, mb_y);
> +    }else {
> +        av_log(s->avctx, AV_LOG_ERROR, "unsupported mb mode %i\n", mode);
> +    }
> +    *bs += mode;
> +}

I think it would be better if the tgq_dconly_block() would be factored
out and replaced by 6 tgq_dconly() calls.
The way it is currently things would become quite bloated when the compiler
inlined them.


> +
> +static void tgq_calculate_qtable(TgqContext *s, int quant){
> +    int i,j;

> +    const int a =((50*(100-quant))/100 - (22*(100-quant))/100) + 2;
> +    const int b = (22*(100-quant))/100 + 8;

this can be simplified, (22*(100-quant))/100 occurs twice for example.


[...]

> Index: libavcodec/avcodec.h
> ===================================================================
> --- libavcodec/avcodec.h	(revision 15434)
> +++ libavcodec/avcodec.h	(working copy)
> @@ -189,6 +189,7 @@
>      CODEC_ID_CMV,
>      CODEC_ID_MOTIONPIXELS,
>      CODEC_ID_TGV,
> +    CODEC_ID_TGQ,
>  
>      /* various PCM "codecs" */
>      CODEC_ID_PCM_S16LE= 0x10000,

ok


[...]
> Index: libavformat/electronicarts.c
> ===================================================================
> --- libavformat/electronicarts.c	(revision 15434)
> +++ libavformat/electronicarts.c	(working copy)
> @@ -47,6 +47,8 @@
>  #define mTCD_TAG MKTAG('m', 'T', 'C', 'D')    /* MDEC */
>  #define MADk_TAG MKTAG('M', 'A', 'D', 'k')    /* MAD i-frame */
>  #define MPCh_TAG MKTAG('M', 'P', 'C', 'h')    /* MPEG2 */
> +#define TGQs_TAG MKTAG('T', 'G', 'Q', 's')    /* TGQ i-frame (appears in .TGQ files) */
> +#define pQGT_TAG MKTAG('p', 'Q', 'G', 'T')    /* TGQ i-frame (appears in .UV files) */
>  #define MVhd_TAG MKTAG('M', 'V', 'h', 'd')
>  #define MV0K_TAG MKTAG('M', 'V', '0', 'K')
>  #define MV0F_TAG MKTAG('M', 'V', '0', 'F')
> @@ -341,6 +343,11 @@
>                  ea->video_codec = CODEC_ID_MPEG2VIDEO;
>                  break;
>  
> +            case pQGT_TAG:
> +            case TGQs_TAG:
> +                ea->video_codec = CODEC_ID_TGQ;
> +                break;
> +
>              case MVhd_TAG :
>                  err = process_video_header_vp6(s);
>                  break;
> @@ -497,6 +504,8 @@
>  
>          case MVIh_TAG:
>          case kVGT_TAG:
> +        case pQGT_TAG:
> +        case TGQs_TAG:
>              key = PKT_FLAG_KEY;
>          case MVIf_TAG:
>          case fVGT_TAG:

ok

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

I am the wisest man alive, for I know one thing, and that is that I know
nothing. -- Socrates
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20080927/2bcca452/attachment.pgp>



More information about the ffmpeg-devel mailing list