diff mbox series

[FFmpeg-devel] avcodec: add Actimagine VX video decoder

Message ID 20210314141501.28136-1-fnouwt2@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] avcodec: add Actimagine VX video decoder | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Florian Nouwt March 14, 2021, 2:15 p.m. UTC
This video format is mainly used in older Nintendo DS games in a .vx container. It is the predecessor of the Mobiclip format.

Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
---
 Changelog                 |    1 +
 configure                 |    1 +
 doc/general_contents.texi |    2 +
 libavcodec/Makefile       |    1 +
 libavcodec/actimagine.c   | 1644 +++++++++++++++++++++++++++++++++++++
 libavcodec/allcodecs.c    |    1 +
 libavcodec/codec_desc.c   |    7 +
 libavcodec/codec_id.h     |    1 +
 libavcodec/version.h      |    2 +-
 libavformat/riff.c        |    2 +
 10 files changed, 1661 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/actimagine.c

Comments

Andreas Rheinhardt March 15, 2021, 1:47 p.m. UTC | #1
Florian Nouwt:
> This video format is mainly used in older Nintendo DS games in a .vx container. It is the predecessor of the Mobiclip format.
> 
> Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
> ---
>  Changelog                 |    1 +
>  configure                 |    1 +
>  doc/general_contents.texi |    2 +
>  libavcodec/Makefile       |    1 +
>  libavcodec/actimagine.c   | 1644 +++++++++++++++++++++++++++++++++++++
>  libavcodec/allcodecs.c    |    1 +
>  libavcodec/codec_desc.c   |    7 +
>  libavcodec/codec_id.h     |    1 +
>  libavcodec/version.h      |    2 +-
>  libavformat/riff.c        |    2 +
>  10 files changed, 1661 insertions(+), 1 deletion(-)
>  create mode 100644 libavcodec/actimagine.c
> 
> diff --git a/Changelog b/Changelog
> index a96e350e09..8807f3dcb3 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -83,6 +83,7 @@ version <next>:
>  - msad video filter
>  - gophers protocol
>  - RIST protocol via librist
> +- Actimagine VX video decoder
>  
>  
>  version 4.3:
> diff --git a/configure b/configure
> index f0ac719d2d..10a07da95f 100755
> --- a/configure
> +++ b/configure
> @@ -2662,6 +2662,7 @@ ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct"
>  ac3_encoder_select="ac3dsp audiodsp mdct me_cmp"
>  ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp"
>  acelp_kelvin_decoder_select="audiodsp"
> +actimagine_decoder_select="bswapdsp golomb"
>  adpcm_g722_decoder_select="g722dsp"
>  adpcm_g722_encoder_select="g722dsp"
>  aic_decoder_select="golomb idctdsp"
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index 33ece6e884..d4261386fc 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -807,6 +807,8 @@ following image formats are supported:
>  @item 8088flex TMV           @tab     @tab  X
>  @item A64 multicolor         @tab  X  @tab
>      @tab Creates video suitable to be played on a commodore 64 (multicolor mode).
> +@item Actimagine VX Video    @tab     @tab  X
> +    @tab fourcc: vxs1, VXS1
>  @item Amazing Studio PAF Video @tab     @tab  X
>  @item American Laser Games MM  @tab    @tab X
>      @tab Used in games like Mad Dog McCree.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 81cc16471b..39b3bc968d 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -182,6 +182,7 @@ OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
>  OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o
>  OBJS-$(CONFIG_AC3_MF_ENCODER)          += mfenc.o mf_utils.o
>  OBJS-$(CONFIG_ACELP_KELVIN_DECODER)    += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o
> +OBJS-$(CONFIG_ACTIMAGINE_DECODER)      += actimagine.o
>  OBJS-$(CONFIG_AGM_DECODER)             += agm.o
>  OBJS-$(CONFIG_AIC_DECODER)             += aic.o
>  OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
> diff --git a/libavcodec/actimagine.c b/libavcodec/actimagine.c
> new file mode 100644
> index 0000000000..6bb5126b05
> --- /dev/null
> +++ b/libavcodec/actimagine.c
> @@ -0,0 +1,1644 @@
> +/*
> + * Actimagine VX Video decoder
> + * Copyright (c) 2021 Florian Nouwt
> + *
> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <inttypes.h>
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/thread.h"
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "bswapdsp.h"
> +#include "get_bits.h"
> +#include "golomb.h"
> +#include "internal.h"
> +
> +static const uint8_t predict_dc_shift_tab[] = {1, 2, 3, 0, 4};
> +
> +static const uint8_t zigzag4x4_tab[] =
> +{
> +    0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09,
> +    0x06, 0x03, 0x07, 0x0A, 0x0D, 0x0E, 0x0B, 0x0F
> +};
> +
> +static const uint8_t quant4x4_tab[][8] =
> +{
> +    { 0x0A, 0x0D, 0x0A, 0x0D, 0x0D, 0x10, 0x0D, 0x10 },
> +    { 0x0B, 0x0E, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12 },
> +    { 0x0D, 0x10, 0x0D, 0x10, 0x10, 0x14, 0x10, 0x14 },
> +    { 0x0E, 0x12, 0x0E, 0x12, 0x12, 0x17, 0x12, 0x17 },
> +    { 0x10, 0x14, 0x10, 0x14, 0x14, 0x19, 0x14, 0x19 },
> +    { 0x12, 0x17, 0x12, 0x17, 0x17, 0x1D, 0x17, 0x1D }
> +};
> +
> +static const uint8_t old_mb_mode_remap_tab[] =
> +{
> +     1,  2,  0,  4,  7,  3, 11, 15,  9,  5, 14,  6,
> +    12, 13,  8, 16, 23, 10, 22, 19, 20, 17, 21, 18
> +};
> +
> +static const uint8_t residu_mask_old_tab[] =
> +{
> +    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x03, 0x05,
> +    0x0A, 0x0C, 0x0F, 0x1F, 0x07, 0x0B, 0x0D, 0x0E,
> +    0x06, 0x09, 0x13, 0x15, 0x1A, 0x1C, 0x11, 0x12,
> +    0x14, 0x18, 0x17, 0x1B, 0x1D, 0x1E, 0x16, 0x19
> +};
> +
> +static const uint8_t residu_mask_new_tab[] =
> +{
> +    0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
> +    0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
> +    0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
> +    0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
> +};
> +
> +static const int cavlc_suffix_len_update_tab[] =
> +{
> +    2, 5, 11, 23, 47, 32768
> +};
> +
> +// same tables as h264 cavlc
> +static const uint8_t coeff_token_table_index[17] =
> +{
> +    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3
> +};
> +
> +static const uint8_t coeff_token_len[4][4 * 17] = {
> +{
> +     1, 0, 0, 0,
> +     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
> +    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
> +    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
> +    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
> +},
> +{
> +     2, 0, 0, 0,
> +     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
> +     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
> +    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
> +    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
> +},
> +{
> +     4, 0, 0, 0,
> +     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
> +     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
> +     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
> +    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
> +},
> +{
> +     6, 0, 0, 0,
> +     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +}
> +};
> +
> +static const uint8_t coeff_token_bits[4][4 * 17] = {
> +{
> +     1, 0, 0, 0,
> +     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
> +     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
> +    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
> +    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
> +},
> +{
> +     3, 0, 0, 0,
> +    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
> +     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
> +    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
> +    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
> +},
> +{
> +    15, 0, 0, 0,
> +    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
> +    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
> +    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
> +    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
> +},
> +{
> +     3, 0, 0, 0,
> +     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
> +    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
> +    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
> +    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
> +}
> +};
> +
> +static const uint8_t total_zeros_len[16][16] = {
> +    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
> +    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
> +    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
> +    {5,3,4,4,3,3,3,4,3,4,5,5,5},
> +    {4,4,4,3,3,3,3,3,4,5,4,5},
> +    {6,5,3,3,3,3,3,3,4,3,6},
> +    {6,5,3,3,3,2,3,4,3,6},
> +    {6,4,5,3,2,2,3,3,6},
> +    {6,6,4,2,2,3,2,5},
> +    {5,5,3,2,2,2,4},
> +    {4,4,3,3,1,3},
> +    {4,4,2,1,3},
> +    {3,3,1,2},
> +    {2,2,1},
> +    {1,1},
> +};
> +
> +static const uint8_t total_zeros_bits[16][16] = {
> +    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
> +    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
> +    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
> +    {3,7,5,4,6,5,4,3,3,2,2,1,0},
> +    {5,4,3,7,6,5,4,3,2,1,1,0},
> +    {1,1,7,6,5,4,3,2,1,1,0},
> +    {1,1,5,4,3,3,2,1,1,0},
> +    {1,1,1,3,3,2,2,1,0},
> +    {1,0,1,3,2,1,1,1},
> +    {1,0,1,3,2,1,1},
> +    {0,1,1,2,1,3},
> +    {0,1,1,1,1},
> +    {0,1,1,1},
> +    {0,1,1},
> +    {0,1},
> +};
> +
> +static const uint8_t run_len[7][16] = {
> +    {1,1},
> +    {1,2,2},
> +    {2,2,2,2},
> +    {2,2,2,3,3},
> +    {2,2,3,3,3,3},
> +    {2,3,3,3,3,3,3},
> +    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
> +};
> +
> +static const uint8_t run_bits[7][16] = {
> +    {1,0},
> +    {1,1,0},
> +    {3,2,1,0},
> +    {3,2,1,1,0},
> +    {3,2,3,2,1,0},
> +    {3,0,1,3,2,5,4},
> +    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
> +};
> +
> +typedef struct MVec {
> +    int x, y;
> +} MVec;
> +
> +typedef struct ActimagineContext {
> +    AVFrame *cur_frame;
> +    AVFrame *ref_frames[3];
> +    int      ref_frame_count;
> +
> +    int version;
> +    int quantizer;
> +    int avi;
> +
> +    GetBitContext gb;
> +
> +    uint8_t *bitstream;
> +    int bitstream_size;
> +
> +    int qtab[2][4];
> +
> +    uint8_t pred4_cache[5][5];
> +
> +    MVec *vectors;
> +    int   vectors_stride;
> +
> +    uint8_t *total_coeff_y;
> +    int      total_coeff_y_stride;
> +
> +    uint8_t *total_coeff_uv;
> +    int      total_coeff_uv_stride;
> +
> +    BswapDSPContext bdsp;
> +} ActimagineContext;
> +
> +static VLC coeff_token_vlc[4];
> +static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
> +static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
> +
> +static VLC total_zeros_vlc[15+1];
> +static VLC_TYPE total_zeros_vlc_tables[15][512][2];
> +static const int total_zeros_vlc_tables_size = 512;
> +
> +static VLC run_vlc[6+1];
> +static VLC_TYPE run_vlc_tables[6][8][2];
> +static const int run_vlc_tables_size = 8;
> +
> +static VLC run7_vlc;
> +static VLC_TYPE run7_vlc_table[96][2];
> +static const int run7_vlc_table_size = 96;
> +
> +#define COEFF_TOKEN_VLC_BITS    8
> +#define TOTAL_ZEROS_VLC_BITS    9
> +#define RUN_VLC_BITS            3
> +#define RUN7_VLC_BITS           6
> +
> +#define PIXEL_REF(s, ref, plane, x, y)\
> +    ((s)->ref_frames[(ref)]->data[(plane)]\
> +        [(y) * (s)->ref_frames[(ref)]->linesize[(plane)] + (x)])
> +
> +#define PIXEL_CUR(s, plane, x, y)\
> +    ((s)->cur_frame->data[(plane)]\
> +        [(y) * (s)->cur_frame->linesize[(plane)] + (x)])
> +
> +#define PREDICT2(a,b)       (((a) + (b) + 1) >> 1)
> +#define PREDICT3(a,b,c)     (((a) + 2 * (b) + (c) + 2) >> 2)
> +
> +#define VX_VERSION_INVALID  -1
> +#define VX_VERSION_OLD       0
> +#define VX_VERSION_NEW       1
> +
> +static av_cold void actimagine_init_static(void)
> +{
> +    // all these tables are equal to the ones used for h264 cavlc

Then please share them.

> +    int offset = 0;
> +    for (int i = 0; i < 4; i++) {
> +        coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
> +        coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
> +        init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4 * 17,
> +                 &coeff_token_len [i][0], 1, 1,
> +                 &coeff_token_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +        offset += coeff_token_vlc_tables_size[i];
> +    }
> +    /*
> +     * This is a one time safety check to make sure that
> +     * the packed static coeff_token_vlc table sizes
> +     * were initialized correctly.
> +     */
> +    av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables));
> +
> +    for (int i = 0; i < 15; i++) {
> +        total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i];
> +        total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
> +        init_vlc(&total_zeros_vlc[i + 1],
> +                 TOTAL_ZEROS_VLC_BITS, 16,
> +                 &total_zeros_len [i][0], 1, 1,
> +                 &total_zeros_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +    }
> +
> +    for (int i = 0; i < 6; i++) {
> +        run_vlc[i + 1].table = run_vlc_tables[i];
> +        run_vlc[i + 1].table_allocated = run_vlc_tables_size;
> +        init_vlc(&run_vlc[i + 1],
> +                 RUN_VLC_BITS, 7,
> +                 &run_len [i][0], 1, 1,
> +                 &run_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +    }
> +
> +    run7_vlc.table = run7_vlc_table,
> +    run7_vlc.table_allocated = run7_vlc_table_size;
> +    init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
> +             &run_len [6][0], 1, 1,
> +             &run_bits[6][0], 1, 1,
> +             INIT_VLC_USE_NEW_STATIC);
> +}
> +
> +static int setup_qtables(AVCodecContext *avctx, int quantizer)
> +{
> +    int qx, qy;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    if (quantizer < 12 || quantizer > 161)
> +        return AVERROR_INVALIDDATA;
> +
> +    s->quantizer = quantizer;
> +
> +    qx = quantizer % 6;
> +    qy = quantizer / 6;
> +
> +    for (int i = 0; i < 2; i++)
> +        for (int j = 0; j < 4; j++)
> +            s->qtab[i][j] = quant4x4_tab[qx][4 * i + j] << qy;
> +
> +    return 0;
> +}
> +
> +static av_cold int actimagine_init(AVCodecContext *avctx)
> +{
> +    int vectors_size;
> +    int total_coeff_y_size;
> +    int total_coeff_uv_size;
> +    static AVOnce init_static_once = AV_ONCE_INIT;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    if (avctx->width & 15 || avctx->height & 15) {
> +        av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    ff_bswapdsp_init(&s->bdsp);
> +
> +    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
> +    avctx->color_range = AVCOL_RANGE_JPEG;
> +    avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
> +    // the color space of this video format is not supported currently
> +    // it is a yuv approximation that can be converted back to rgb using bitshifts
> +    // r = y + (v << 1)
> +    // g = y - (u >> 1) - v
> +    // b = y + (u << 1)
> +    avctx->colorspace = AVCOL_SPC_NB;
> +
> +    // predict4 cache
> +    for (int i = 0; i < 5; i++)
> +        for (int j = 0; j < 5; j++)
> +            s->pred4_cache[i][j] = 9;
> +
> +    // motion vector cache
> +    s->vectors_stride = (avctx->width >> 4) + 2;
> +    vectors_size = ((avctx->height >> 4) + 1) * s->vectors_stride;
> +    s->vectors = av_calloc(vectors_size, sizeof(MVec));
> +    if (!s->vectors)
> +        return AVERROR(ENOMEM);
> +    memset(s->vectors, 0, vectors_size * sizeof(MVec));

Unnecessary. av_calloc already zeroes it.

> +
> +    // total dct coefficient cache for luma
> +    s->total_coeff_y_stride = (avctx->width >> 2) + 1;
> +    total_coeff_y_size = ((avctx->height >> 2) + 1) * s->total_coeff_y_stride;
> +    s->total_coeff_y = av_malloc(total_coeff_y_size);
> +    if (!s->total_coeff_y)
> +        return AVERROR(ENOMEM);
> +    memset(s->total_coeff_y, 0, total_coeff_y_size);

Unnecessary: Use av_mallocz.

> +
> +    // total dct coefficient cache for chroma
> +    s->total_coeff_uv_stride = (avctx->width >> 3) + 1;
> +    total_coeff_uv_size = ((avctx->height >> 3) + 1) * s->total_coeff_uv_stride;
> +    s->total_coeff_uv = av_malloc(total_coeff_uv_size);
> +    if (!s->total_coeff_uv)
> +        return AVERROR(ENOMEM);
> +    memset(s->total_coeff_uv, 0, total_coeff_uv_size);

Same as above.

> +
> +    s->ref_frame_count = 0;
> +    for (int i = 0; i < 3; i++) {
> +        s->ref_frames[i] = av_frame_alloc();
> +        if (!s->ref_frames[i])
> +            return AVERROR(ENOMEM);
> +    }
> +    s->cur_frame = av_frame_alloc();
> +    if (!s->cur_frame)
> +        return AVERROR(ENOMEM);
> +
> +    s->version = VX_VERSION_INVALID;
> +    s->quantizer = -1;
> +    s->avi = 0;
> +
> +    // when the source is an avi file, the quantizer is stored in the extradata
> +    if (avctx->extradata_size == 4)
> +        if (!setup_qtables(avctx, AV_RL32(avctx->extradata)))
> +            s->avi = 1;
> +
> +    ff_thread_once(&init_static_once, actimagine_init_static);
> +
> +    return 0;
> +}
> +
> +static void clear_total_coeff(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    // luma
> +    uint8_t *total_coeff = &s->total_coeff_y[
> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> +
> +    for (int y2 = 0; y2 < (h >> 2); y2++)
> +        for (int x2 = 0; x2 < (w >> 2); x2++)
> +            total_coeff[y2 * s->total_coeff_y_stride + x2] = 0;
> +
> +    // chroma
> +    total_coeff = &s->total_coeff_uv[
> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> +
> +    for (int y2 = 0; y2 < (h >> 3); y2++)
> +        for (int x2 = 0; x2 < (w >> 3); x2++)
> +            total_coeff[y2 * s->total_coeff_uv_stride + x2] = 0;
> +}
> +
> +static void predict_plane_intern(AVCodecContext *avctx, int x, int y,
> +                                 int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    if (w == 1 && h == 1)
> +        return;
> +    if (w == 1 && h != 1) {
> +        uint8_t top    = PIXEL_CUR(s, plane, x, y - 1);
> +        uint8_t bottom = PIXEL_CUR(s, plane, x, y + h - 1);
> +        PIXEL_CUR(s, plane, x, y + (h >> 1) - 1) = (top + bottom) >> 1;
> +        predict_plane_intern(avctx, x, y, 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x, y + (h >> 1), 1, h >> 1, plane);
> +    } else if (w != 1 && h == 1) {
> +        uint8_t left  = PIXEL_CUR(s, plane, x - 1, y);
> +        uint8_t right = PIXEL_CUR(s, plane, x + w - 1, y);
> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y) = (left + right) >> 1;
> +        predict_plane_intern(avctx, x, y, w >> 1, 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, 1, plane);
> +    } else {
> +        uint8_t bottom_left  = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> +        uint8_t top_right    = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> +        uint8_t bottom_right = PIXEL_CUR(s, plane, x + w - 1, y + h - 1);
> +        uint8_t bottom_center = (bottom_left + bottom_right) >> 1;
> +        uint8_t center_right = (top_right + bottom_right) >> 1;
> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + h - 1) = bottom_center;
> +        PIXEL_CUR(s, plane, x + w - 1, y + (h >> 1) - 1) = center_right;
> +        if ((w == 4 || w == 16) ^ (h == 4 || h == 16)) {
> +            uint8_t center_left = PIXEL_CUR(s, plane, x - 1, y + (h >> 1) - 1);
> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> +                = (center_left + center_right) >> 1;
> +        } else {
> +            uint8_t top_center = PIXEL_CUR(s, plane, x + (w >> 1) - 1, y - 1);
> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> +                = (top_center + bottom_center) >> 1;
> +        }
> +        predict_plane_intern(avctx, x, y, w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x, y + (h >> 1), w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y + (h >> 1), w >> 1, h >> 1,
> +                             plane);
> +    }
> +}
> +
> +static void predict_plane(AVCodecContext *avctx, int x, int y, int w, int h,
> +                          int plane, int param)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    uint8_t bottom_left = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> +    uint8_t top_right   = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> +    PIXEL_CUR(s, plane, x + w - 1, y + h - 1)
> +        = ((bottom_left + top_right + 1) >> 1) + param;
> +    predict_plane_intern(avctx, x, y, w, h, plane);
> +}
> +
> +static int predict_mb_plane(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    // y
> +    int param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x, y, w, h, 0, param << 1);
> +
> +    // u
> +    param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, param << 1);
> +
> +    // v
> +    param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, param << 1);
> +
> +    return 0;
> +}
> +
> +static void predict_horizontal(AVCodecContext *avctx, int x, int y,
> +                               int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    for (int y2 = 0; y2 < h; y2++) {
> +        uint8_t pixel = PIXEL_CUR(s, plane, x - 1, y + y2);
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2) = pixel;
> +    }
> +}
> +
> +static void predict_vertical(AVCodecContext *avctx, int x, int y,
> +                             int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2)
> +                = PIXEL_CUR(s, plane, x + x2, y - 1);
> +}
> +
> +static void predict_dc(AVCodecContext *avctx, int x, int y, int w, int h,
> +                       int plane)
> +{
> +    uint8_t dc;
> +    ActimagineContext *s = avctx->priv_data;
> +    if (x != 0 && y != 0) {
> +        int sum_h, sum_v;
> +        sum_h = w >> 1;
> +        for (int x2 = 0; x2 < w; x2++)
> +            sum_h += PIXEL_CUR(s, plane, x + x2, y - 1);
> +        sum_h >>= predict_dc_shift_tab[w >> 2];
> +
> +        sum_v = h >> 1;
> +        for (int y2 = 0; y2 < h; y2++)
> +            sum_v += PIXEL_CUR(s, plane, x - 1, y + y2);
> +        sum_v >>= predict_dc_shift_tab[h >> 2];
> +
> +        dc = (sum_h + sum_v + 1) >> 1;
> +    } else if (x == 0 && y != 0) {
> +        int sum = w >> 1;
> +        for (int x2 = 0; x2 < w; x2++)
> +            sum += PIXEL_CUR(s, plane, x + x2, y - 1);
> +        dc = sum >> predict_dc_shift_tab[w >> 2];
> +    } else if (x != 0 && y == 0) {
> +        int sum = h >> 1;
> +        for (int y2 = 0; y2 < h; y2++)
> +            sum += PIXEL_CUR(s, plane, x - 1, y + y2);
> +        dc = sum >> predict_dc_shift_tab[h >> 2];
> +    } else
> +        dc = 128;
> +
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2) = dc;
> +}
> +
> +static int predict_notile_uv(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int mode_uv = get_ue_golomb_31(gb);
> +    switch (mode_uv) {
> +    case 0:// dc
> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 1:// horizontal
> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 2:// vertical
> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 3:// plane
> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, 0);
> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, 0);
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile uv mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return 0;
> +}
> +
> +static int predict_notile(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int mode_y = get_ue_golomb_31(gb);
> +    switch (mode_y) {
> +    case 0:// vertical
> +        predict_vertical(avctx, x, y, w, h, 0);
> +        break;
> +    case 1:// horizontal
> +        predict_horizontal(avctx, x, y, w, h, 0);
> +        break;
> +    case 2:// dc
> +        predict_dc(avctx, x, y, w, h, 0);
> +        break;
> +    case 3:// plane
> +        predict_plane(avctx, x, y, w, h, 0, 0);
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile y mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return predict_notile_uv(avctx, x, y, w, h);
> +}
> +
> +// slightly different from the common dc prediction method
> +static void predict4_dc(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t dc;
> +    ActimagineContext *s = avctx->priv_data;
> +    if (x == 0 && y == 0)
> +        dc = 128;
> +    else {
> +        int sum   = 0;
> +        int shift = 1;
> +
> +        if (x != 0) {
> +            shift++;
> +            for (int y2 = 0; y2 < 4; y2++)
> +                sum += PIXEL_CUR(s, 0, x - 1, y + y2);
> +            sum += 2;
> +        }
> +
> +        if (y != 0) {
> +            shift++;
> +            for (int x2 = 0; x2 < 4; x2++)
> +                sum += PIXEL_CUR(s, 0, x + x2, y - 1);
> +            sum += 2;
> +        }
> +
> +        dc = sum >> shift;
> +    }
> +
> +    for (int y2 = 0; y2 < 4; y2++)
> +        for (int x2 = 0; x2 < 4; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2) = dc;
> +}
> +
> +static void predict4_diagonal_down_left(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> +    uint8_t h = PIXEL_CUR(s, 0, x + 7, y - 1);
> +
> +    PIXEL_CUR(s, 0, x, y) = PREDICT3(a, b, c);
> +
> +    val = PREDICT3(b, c, d);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +
> +    val = PREDICT3(c, d, e);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +
> +    val = PREDICT3(d, e, f);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> +
> +    val = PREDICT3(e, f, g);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT3(f, g, h);
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(g, h, h);
> +}
> +
> +static void predict4_diagonal_down_right(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(j, k, l);
> +
> +    val = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT3(m, i, j);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT3(i, m, a);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT3(m, a, b);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    val = PREDICT3(a, b, c);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(b, c, d);
> +}
> +
> +static void predict4_vertical_right(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT3(m, i, j);
> +
> +    val = PREDICT3(a, m, i);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT2(a, m);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +
> +    val = PREDICT3(b, a, m);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT2(b, a);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(c, b, a);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT2(c, b);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(d, c, b);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT2(d, c);
> +}
> +
> +static void predict4_horizontal_down(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = PREDICT3(j, k, l);
> +
> +    val = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT2(i, j);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(m, i, j);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    val = PREDICT2(i, m);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +
> +    val = PREDICT3(i, m, a);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT3(m, a, b);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(a, b, c);
> +}
> +
> +static void predict4_vertical_left(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(e, f, g);
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = PREDICT2(e, f);
> +
> +    val = PREDICT3(d, e, f);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT2(d, e);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(c, d, e);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT2(c, d);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +
> +    val = PREDICT3(b, c, d);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> +
> +    val = PREDICT2(b, c);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT3(a, b, c);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(a, b);
> +}
> +
> +static void predict4_horizontal_up(AVCodecContext *avctx, int x, int y)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(i, j);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(j, k, l);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = PREDICT3(j, k, l);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(k, l, l);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = PREDICT3(k, l, l);
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = l;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = l;
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = l;
> +}
> +
> +static int predict4(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    for (int y2 = 0; y2 < h >> 2; y2++) {
> +        for (int x2 = 0; x2 < w >> 2; x2++) {
> +            uint8_t mode = FFMIN(s->pred4_cache[1 + y2 - 1][1 + x2],
> +                                 s->pred4_cache[1 + y2][1 + x2 - 1]);
> +            if (mode == 9)// if invalid predict dc
> +                mode = 2;
> +
> +            if (!get_bits1(gb)) {
> +                uint8_t val = get_bits(gb, 3);
> +                if (val >= mode)
> +                    val++;
> +                mode = val;
> +            }
> +
> +            s->pred4_cache[1 + y2][1 + x2] = mode;
> +
> +            switch (mode) {
> +            case 0:// vertical
> +                predict_vertical(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> +                break;
> +            case 1:// horizontal
> +                predict_horizontal(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> +                break;
> +            case 2:// dc
> +                predict4_dc(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 3:// diagonal-down-left
> +                predict4_diagonal_down_left(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 4:// diagonal-down-right
> +                predict4_diagonal_down_right(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 5:// vertical-right
> +                predict4_vertical_right(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 6:// horizontal-down
> +                predict4_horizontal_down(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 7:// vertical-left
> +                predict4_vertical_left(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 8:// horizontal-up
> +                predict4_horizontal_up(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            default:
> +                av_log(avctx, AV_LOG_ERROR, "invalid predict4 mode\n");
> +                return AVERROR_INVALIDDATA;
> +            }
> +        }
> +    }
> +    return predict_notile_uv(avctx, x, y, w, h);
> +}
> +
> +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
> +                       const int* level)
> +{
> +    int a, b, c, d, e, f;
> +    int dct[16];
> +    int tmp[16];
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    // dezigzag
> +    for (int i = 0; i < 16; i++)
> +        dct[zigzag4x4_tab[i]] = level[i];
> +
> +    // dequantize
> +    for (int i = 0; i < 2; i++) {
> +        for (int j = 0; j < 4; j++) {
> +            dct[4 * j + i]     *= s->qtab[i][j];
> +            dct[4 * j + i + 2] *= s->qtab[i][j];
> +        }
> +    }
> +
> +    dct[0] += 32;// rounding
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = dct[i * 4 + 0];
> +        b = dct[i * 4 + 1];
> +        c = dct[i * 4 + 2];
> +        d = dct[i * 4 + 3];
> +        a += c;
> +        c = a - c * 2;
> +        e = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        tmp[ 0 + i] = a + f;
> +        tmp[ 4 + i] = c + e;
> +        tmp[ 8 + i] = c - e;
> +        tmp[12 + i] = a - f;
> +    }
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = tmp[i * 4 + 0];
> +        b = tmp[i * 4 + 1];
> +        c = tmp[i * 4 + 2];
> +        d = tmp[i * 4 + 3];
> +        a += c;
> +        c =  a - c * 2;
> +        e  = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        PIXEL_CUR(s, plane, x + 0, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
> +        PIXEL_CUR(s, plane, x + 1, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
> +        PIXEL_CUR(s, plane, x + 2, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
> +        PIXEL_CUR(s, plane, x + 3, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
> +    }
> +}
> +
> +static void decode_residu_cavlc(AVCodecContext *avctx, int x, int y, int plane,
> +                                int nc, uint8_t *out_total_coeff)
> +{
> +    int level[16];
> +    int coeff_token, total_coeff, trailing_ones, i, zeros_left, suffix_length;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +
> +    coeff_token = get_vlc2(gb, coeff_token_vlc[coeff_token_table_index[nc]].table,
> +                           COEFF_TOKEN_VLC_BITS, 2);
> +    trailing_ones = coeff_token & 3;
> +    total_coeff   = coeff_token >> 2;
> +
> +    *out_total_coeff = total_coeff;
> +    if (total_coeff == 0)
> +        return;
> +
> +    av_assert2(total_coeff <= 16);
> +
> +    i = 15;
> +    if (total_coeff != 16) {
> +        int trailing_zeros;
> +        zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff].table,
> +                              TOTAL_ZEROS_VLC_BITS, 1);
> +        trailing_zeros = 16 - (total_coeff + zeros_left);
> +        while(trailing_zeros-- > 0)
> +            level[i--] = 0;
> +    } else
> +        zeros_left = 0;
> +
> +    suffix_length = 0;
> +    while (1) {
> +        int level_suffix, level_code, run_before;
> +        if (trailing_ones > 0) {
> +            trailing_ones--;
> +            level[i--] = get_bits1(gb) ? -1 : 1;
> +        } else {
> +            int level_prefix = 0;
> +            while (!get_bits1(gb))
> +                level_prefix++;
> +
> +            if (level_prefix == 15)
> +                level_suffix = get_bits(gb, 11);
> +            else
> +                level_suffix = suffix_length == 0 ? 0 : get_bits(gb, suffix_length);
> +
> +            level_code = level_suffix + (level_prefix << suffix_length);
> +
> +            if (level_code > cavlc_suffix_len_update_tab[suffix_length])
> +                suffix_length++;
> +
> +            level_code++;
> +            if (get_bits1(gb))
> +                level_code = -level_code;
> +            level[i--] = level_code;
> +        }
> +
> +        if (--total_coeff == 0)
> +            break;
> +
> +        if (zeros_left == 0)
> +            continue;
> +
> +        if(zeros_left < 7)
> +            run_before = get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1);
> +        else
> +            run_before = get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2);
> +        zeros_left -= run_before;
> +        while(run_before-- > 0)
> +            level[i--] = 0;
> +    }
> +
> +    while(zeros_left-- > 0)
> +        level[i--] = 0;
> +
> +    decode_dct(avctx, x, y, plane, level);
> +}
> +
> +static int decode_residu_blocks(AVCodecContext *avctx, int x, int y,
> +                                int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    uint8_t *total_coeff_y = &s->total_coeff_y[
> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> +    uint8_t *total_coeff_uv = &s->total_coeff_uv[
> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> +    for (int y2 = 0; y2 < h >> 3; y2++) {
> +        for (int x2 = 0; x2 < w >> 3; x2++) {
> +            uint8_t residu_mask;
> +            int code = get_ue_golomb_31(gb);
> +            if (code > 0x1F) {
> +                av_log(avctx, AV_LOG_ERROR, "invalid residu mask code\n");
> +                return AVERROR_INVALIDDATA;
> +            }
> +            if (s->version == VX_VERSION_OLD)
> +                residu_mask = residu_mask_old_tab[code];
> +            else
> +                residu_mask = residu_mask_new_tab[code];
> +
> +            if (residu_mask & 1) {
> +                int nc = (total_coeff_y[-1] +
> +                          total_coeff_y[-s->total_coeff_y_stride] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8, 0, nc,
> +                                    &total_coeff_y[0]);
> +            } else
> +                total_coeff_y[0] = 0;
> +
> +            if (residu_mask & 2) {
> +                int nc = (total_coeff_y[0] +
> +                          total_coeff_y[-s->total_coeff_y_stride + 1] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8 + 4, y + y2 * 8, 0, nc,
> +                                    &total_coeff_y[1]);
> +            } else
> +                total_coeff_y[1] = 0;
> +
> +            if (residu_mask & 4) {
> +                int nc = (total_coeff_y[s->total_coeff_y_stride - 1] +
> +                          total_coeff_y[0] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8 + 4, 0, nc,
> +                                    &total_coeff_y[s->total_coeff_y_stride]);
> +            } else
> +                total_coeff_y[s->total_coeff_y_stride] = 0;
> +
> +            if (residu_mask & 8) {
> +                int nc = (total_coeff_y[s->total_coeff_y_stride] +
> +                          total_coeff_y[1] + 1) >> 1;
> +                decode_residu_cavlc(
> +                    avctx, x + x2 * 8 + 4, y + y2 * 8 + 4, 0, nc,
> +                    &total_coeff_y[s->total_coeff_y_stride + 1]);
> +            } else
> +                total_coeff_y[s->total_coeff_y_stride + 1] = 0;
> +
> +            if (residu_mask & 16) {
> +                uint8_t total_coeff_u, total_coeff_v;
> +                int nc = (total_coeff_uv[-1] +
> +                          total_coeff_uv[-s->total_coeff_uv_stride] + 1) >> 1;
> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> +                                    1, nc, &total_coeff_u);
> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> +                                    2, nc, &total_coeff_v);
> +                total_coeff_uv[0] = (total_coeff_u + total_coeff_v + 1) >> 1;
> +            } else
> +                total_coeff_uv[0] = 0;
> +
> +            total_coeff_y += 2;
> +            total_coeff_uv++;
> +        }
> +        total_coeff_y  += (s->total_coeff_y_stride << 1) - (w >> 2);
> +        total_coeff_uv += s->total_coeff_uv_stride - (w >> 3);
> +    }
> +    return 0;
> +}
> +
> +static int predict_inter(AVCodecContext *avctx, int x, int y, int w, int h,
> +                          const MVec *predVec, int has_delta, int ref_frame)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    MVec vec = *predVec;
> +
> +    if (ref_frame >= s->ref_frame_count) {
> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> +    s->cur_frame->key_frame = 0;
> +
> +    if (has_delta) {
> +        vec.x += (unsigned)get_se_golomb(gb);
> +        vec.y += (unsigned)get_se_golomb(gb);
> +    }
> +
> +    if (vec.x >= INT_MAX || vec.y >= INT_MAX)
> +        return AVERROR_INVALIDDATA;
> +
> +    s->vectors[(1 + (y >> 4)) * s->vectors_stride + 1 + (x >> 4)] = vec;
> +
> +    if (x + vec.x < 0 || x + vec.x + w > avctx->width ||
> +        y + vec.y < 0 || y + vec.y + h > avctx->height) {
> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    // luma
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2)
> +                = PIXEL_REF(s, ref_frame, 0, x + x2 + vec.x, y + y2 + vec.y);
> +
> +    // chroma
> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> +            // u
> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2)
> +                = PIXEL_REF(s, ref_frame, 1, (x >> 1) + x2 + (vec.x >> 1),
> +                            (y >> 1) + y2 + (vec.y >> 1));
> +            // v
> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2)
> +                = PIXEL_REF(s, ref_frame, 2, (x >> 1) + x2 + (vec.x >> 1),
> +                            (y >> 1) + y2 + (vec.y >> 1));
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int predict_inter_dc(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    int dx, dy, dc_y, dc_u, dc_v;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +
> +    if (s->ref_frame_count == 0) {
> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    dx   = get_se_golomb(gb);
> +    dy   = get_se_golomb(gb);
> +
> +    if (x + dx < 0 || x + dx + w > avctx->width ||
> +        y + dy < 0 || y + dy + h > avctx->height) {
> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    dc_y = get_se_golomb(gb);
> +    if (dc_y < -(1<<16) || dc_y >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_y <<= 1;
> +
> +    dc_u = get_se_golomb(gb);
> +    if (dc_u < -(1<<16) || dc_u >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_u <<= 1;
> +
> +    dc_v = get_se_golomb(gb);
> +    if (dc_v < -(1<<16) || dc_v >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_v <<= 1;
> +
> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> +    s->cur_frame->key_frame = 0;
> +
> +    // luma
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 0, x + x2 + dx, y + y2 + dy) + dc_y);
> +
> +    // chroma
> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 1, (x >> 1) + x2 + (dx >> 1),
> +                          (y >> 1) + y2 + (dy >> 1)) + dc_u);
> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 2, (x >> 1) + x2 + (dx >> 1),
> +                          (y >> 1) + y2 + (dy >> 1)) + dc_v);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_mb(AVCodecContext *avctx, int x, int y, int w, int h,
> +                     const MVec *predVec)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int ret = 0;
> +
> +    int mode = get_ue_golomb_31(gb);
> +    if (s->version == VX_VERSION_OLD)
> +        mode = old_mb_mode_remap_tab[mode];
> +
> +    switch (mode) {
> +    case 0:// v-split, no residu
> +        if (w == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if (w == 8 && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 1:// no delta, no residu, ref 0
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 2:// h-split, no residu
> +        if (h == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && h == 8)
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 3:// unpredicted delta ref0 + dc offset, no residu
> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 4:// delta, no residu, ref 0
> +    case 5:// delta, no residu, ref 1
> +    case 6:// delta, no residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 4)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 7:// plane, no residu
> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 8:// v-split, residu
> +        if (w == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 9:// no delta, no residu, ref 1
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 1)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 10:// unpredicted delta ref0 + dc offset, residu
> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 11:// predict notile, no residu
> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 12:// no delta, residu, ref 0
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 13:// h-split, residu
> +        if (h == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 14:// no delta, no residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 2)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 15:// predict4, no residu
> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 16:// delta, residu, ref 0
> +    case 17:// delta, residu, ref 1
> +    case 18:// delta, residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 16)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 19:// predict4, residu
> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 20:// no delta, residu, ref 1
> +    case 21:// no delta, residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, mode - 20 + 1)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 22:// predict notile, residu
> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 23:// plane, residu
> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return 0;
> +}
> +
> +static int detect_format(AVCodecContext *avctx)
> +{
> +    // assume the new format, if any incorrect decisions are made for the
> +    // first macroblock of a keyframe (ref, non-dc prediction) then it must be
> +    // the old format
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int w = 16;
> +    int h = 16;
> +    while (1) {
> +        int mode = get_ue_golomb_31(gb);
> +        if (mode == 0 || mode == 8) { // v-split
> +            if (w == 2) // too many splits
> +                return VX_VERSION_OLD;
> +            w >>= 1;
> +            continue;
> +        } else if (mode == 2 || mode == 13) { // h-split
> +            if (h == 2) // too many splits
> +                return VX_VERSION_OLD;
> +            h >>= 1;
> +            continue;
> +        } else if (mode == 11 || mode == 22) { // predict notile            
> +            if (get_ue_golomb_31(gb) != 2) // mode_y != dc
> +                return VX_VERSION_OLD;
> +            if (get_ue_golomb_31(gb) != 0) // mode_uv != dc
> +                return VX_VERSION_OLD;
> +            break; //we should have enough evidence now
> +        } else if (mode == 15 || mode == 19) { // predict4
> +            // initial prediction is always dc
> +            // we don't expect that prediction to be wrong
> +            if (!get_bits1(gb))
> +                return VX_VERSION_OLD;
> +            break; //we should have enough evidence now
> +        } else // inter prediction, plane or any other value
> +            return VX_VERSION_OLD;
> +    }
> +    return VX_VERSION_NEW;
> +}
> +
> +static int actimagine_decode(AVCodecContext *avctx, void *data,
> +                            int *got_frame, AVPacket *pkt)
> +{
> +    MVec *vectors;
> +    int ret;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    AVFrame *frame = s->cur_frame;
> +
> +    // in avi files the frames start with a 32 bit number that seems to
> +    // indicate the total number of bits
> +    int offset = s->avi ? 4 : 0;
> +
> +    av_fast_padded_malloc(&s->bitstream, &s->bitstream_size,
> +                          pkt->size);
> +
> +    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
> +        return ret;
> +
> +    s->bdsp.bswap16_buf((uint16_t *)s->bitstream, (uint16_t *)pkt->data,
> +                        (pkt->size + 1) >> 1);
> +
> +    ret = init_get_bits8(gb, s->bitstream + offset,
> +                         FFALIGN(pkt->size - offset, 2));
> +    if (ret < 0)
> +        return ret;
> +
> +    // determine the bitstream version if this was not done yet
> +    if (s->version == VX_VERSION_INVALID) {
> +        if (s->ref_frame_count != 0) {
> +            av_log(avctx, AV_LOG_ERROR, "can't determine version on p frame\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        s->version = detect_format(avctx);
> +
> +        // reinit bitreader
> +        ret = init_get_bits8(gb, s->bitstream + offset,
> +                             FFALIGN(pkt->size - offset, 2));
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    vectors = s->vectors + s->vectors_stride + 1;
> +
> +    frame->pict_type = AV_PICTURE_TYPE_I;
> +    frame->key_frame = 1;
> +
> +    if (s->quantizer == -1) {
> +        av_log(avctx, AV_LOG_ERROR, "no quantizer setup\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    for (int y = 0; y < avctx->height; y += 16) {
> +        MVec *vec_cur = vectors;
> +        for (int x = 0; x < avctx->width; x += 16) {
> +            MVec predVec;
> +            vec_cur[0].x = 0;
> +            vec_cur[0].y = 0;
> +            predVec.x = mid_pred(vec_cur[-1].x, vec_cur[-s->vectors_stride].x,
> +                                 vec_cur[-s->vectors_stride + 1].x);
> +            predVec.y = mid_pred(vec_cur[-1].y, vec_cur[-s->vectors_stride].y,
> +                                 vec_cur[-s->vectors_stride + 1].y);
> +            if ((ret = decode_mb(avctx, x, y, 16, 16, &predVec)) < 0)
> +                return ret;
> +            vec_cur++;
> +        }
> +        vectors += s->vectors_stride;
> +    }
> +
> +    if (s->ref_frame_count == 3)
> +        av_frame_unref(s->ref_frames[2]);
> +
> +    s->cur_frame = s->ref_frames[2];
> +    s->ref_frames[2] = s->ref_frames[1];
> +    s->ref_frames[1] = s->ref_frames[0];
> +    s->ref_frames[0] = frame;
> +
> +    if (s->ref_frame_count < 3)
> +        s->ref_frame_count++;
> +
> +    if ((ret = av_frame_ref(data, frame)) < 0)
> +        return ret;
> +    *got_frame = 1;
> +
> +    return 0;
> +}
> +
> +static void actimagine_flush(AVCodecContext *avctx)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    for (int i = 0; i < 3; i++)
> +        av_frame_unref(s->ref_frames[i]);
> +
> +    s->ref_frame_count = 0;
> +}
> +
> +static av_cold int actimagine_close(AVCodecContext *avctx)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    av_freep(&s->vectors);
> +    s->vectors_stride = 0;
> +    av_freep(&s->total_coeff_y);
> +    s->total_coeff_y_stride = 0;
> +    av_freep(&s->total_coeff_uv);
> +    s->total_coeff_uv_stride = 0;
> +
> +    av_freep(&s->bitstream);
> +    s->bitstream_size = 0;
> +
> +    for (int i = 0; i < 3; i++)
> +        av_frame_free(&s->ref_frames[i]);
> +    av_frame_free(&s->cur_frame);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_actimagine_decoder = {
> +    .name           = "actimagine",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_ACTIMAGINE,
> +    .priv_data_size = sizeof(ActimagineContext),
> +    .init           = actimagine_init,
> +    .decode         = actimagine_decode,
> +    .flush          = actimagine_flush,
> +    .close          = actimagine_close,
> +    .capabilities   = AV_CODEC_CAP_DR1,
> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2e9a3581de..10809f3492 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -32,6 +32,7 @@
>  extern AVCodec ff_a64multi_encoder;
>  extern AVCodec ff_a64multi5_encoder;
>  extern AVCodec ff_aasc_decoder;
> +extern AVCodec ff_actimagine_decoder;
>  extern AVCodec ff_aic_decoder;
>  extern AVCodec ff_alias_pix_encoder;
>  extern AVCodec ff_alias_pix_decoder;
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 17f8a14044..65d96c21af 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>          .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
>          .props     = AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_ACTIMAGINE,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "actimagine",
> +        .long_name = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> +        .props     = AV_CODEC_PROP_LOSSY,
> +    },
>  
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index ab7bc68ee2..a4b3f3955d 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -307,6 +307,7 @@ enum AVCodecID {
>      AV_CODEC_ID_CRI,
>      AV_CODEC_ID_SIMBIOSIS_IMX,
>      AV_CODEC_ID_SGA_VIDEO,
> +    AV_CODEC_ID_ACTIMAGINE,
>  
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 4299ad4239..f992e1b36e 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -28,7 +28,7 @@
>  #include "libavutil/version.h"
>  
>  #define LIBAVCODEC_VERSION_MAJOR  58
> -#define LIBAVCODEC_VERSION_MINOR 131
> +#define LIBAVCODEC_VERSION_MINOR 132
>  #define LIBAVCODEC_VERSION_MICRO 100
>  
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index 270ff7c024..5d5cfe16b0 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -496,6 +496,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { AV_CODEC_ID_MVHA,         MKTAG('M', 'V', 'H', 'A') },
>      { AV_CODEC_ID_MV30,         MKTAG('M', 'V', '3', '0') },
>      { AV_CODEC_ID_NOTCHLC,      MKTAG('n', 'l', 'c', '1') },
> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('V', 'X', 'S', '1') },
> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('v', 'x', 's', '1') },
>      { AV_CODEC_ID_NONE,         0 }
>  };
>  
>
Florian Nouwt March 15, 2021, 2:10 p.m. UTC | #2
I have uploaded an example video here:
https://mega.nz/file/PVwGAbbB#9tL6p3uE-Ej1DP7ngovAPqYghkQckTpW26XlrpAop9w
There's both an .avi file which can be read by ffmpeg and a vx file
containing the exact same video data, for which I will write a demuxer
soon (note that the vx is smaller because it contains compressed
audio). I assumed it would be appropriate to have the demuxer as a
separate patch after this one was accepted.

If I make changes to my patch, do I have to amend the changes to my
commit and then regenerate the patch and send it to this thread? This
patch and mailing list stuff is kinda new to me.

Op ma 15 mrt. 2021 om 14:47 schreef Andreas Rheinhardt
<andreas.rheinhardt@gmail.com>:
>
> Florian Nouwt:
> > This video format is mainly used in older Nintendo DS games in a .vx container. It is the predecessor of the Mobiclip format.
> >
> > Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
> > ---
> >  Changelog                 |    1 +
> >  configure                 |    1 +
> >  doc/general_contents.texi |    2 +
> >  libavcodec/Makefile       |    1 +
> >  libavcodec/actimagine.c   | 1644 +++++++++++++++++++++++++++++++++++++
> >  libavcodec/allcodecs.c    |    1 +
> >  libavcodec/codec_desc.c   |    7 +
> >  libavcodec/codec_id.h     |    1 +
> >  libavcodec/version.h      |    2 +-
> >  libavformat/riff.c        |    2 +
> >  10 files changed, 1661 insertions(+), 1 deletion(-)
> >  create mode 100644 libavcodec/actimagine.c
> >
> > diff --git a/Changelog b/Changelog
> > index a96e350e09..8807f3dcb3 100644
> > --- a/Changelog
> > +++ b/Changelog
> > @@ -83,6 +83,7 @@ version <next>:
> >  - msad video filter
> >  - gophers protocol
> >  - RIST protocol via librist
> > +- Actimagine VX video decoder
> >
> >
> >  version 4.3:
> > diff --git a/configure b/configure
> > index f0ac719d2d..10a07da95f 100755
> > --- a/configure
> > +++ b/configure
> > @@ -2662,6 +2662,7 @@ ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct"
> >  ac3_encoder_select="ac3dsp audiodsp mdct me_cmp"
> >  ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp"
> >  acelp_kelvin_decoder_select="audiodsp"
> > +actimagine_decoder_select="bswapdsp golomb"
> >  adpcm_g722_decoder_select="g722dsp"
> >  adpcm_g722_encoder_select="g722dsp"
> >  aic_decoder_select="golomb idctdsp"
> > diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> > index 33ece6e884..d4261386fc 100644
> > --- a/doc/general_contents.texi
> > +++ b/doc/general_contents.texi
> > @@ -807,6 +807,8 @@ following image formats are supported:
> >  @item 8088flex TMV           @tab     @tab  X
> >  @item A64 multicolor         @tab  X  @tab
> >      @tab Creates video suitable to be played on a commodore 64 (multicolor mode).
> > +@item Actimagine VX Video    @tab     @tab  X
> > +    @tab fourcc: vxs1, VXS1
> >  @item Amazing Studio PAF Video @tab     @tab  X
> >  @item American Laser Games MM  @tab    @tab X
> >      @tab Used in games like Mad Dog McCree.
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> > index 81cc16471b..39b3bc968d 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -182,6 +182,7 @@ OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
> >  OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o
> >  OBJS-$(CONFIG_AC3_MF_ENCODER)          += mfenc.o mf_utils.o
> >  OBJS-$(CONFIG_ACELP_KELVIN_DECODER)    += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o
> > +OBJS-$(CONFIG_ACTIMAGINE_DECODER)      += actimagine.o
> >  OBJS-$(CONFIG_AGM_DECODER)             += agm.o
> >  OBJS-$(CONFIG_AIC_DECODER)             += aic.o
> >  OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
> > diff --git a/libavcodec/actimagine.c b/libavcodec/actimagine.c
> > new file mode 100644
> > index 0000000000..6bb5126b05
> > --- /dev/null
> > +++ b/libavcodec/actimagine.c
> > @@ -0,0 +1,1644 @@
> > +/*
> > + * Actimagine VX Video decoder
> > + * Copyright (c) 2021 Florian Nouwt
> > + *
> > + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
> > + */
> > +
> > +#include <inttypes.h>
> > +
> > +#include "libavutil/avassert.h"
> > +#include "libavutil/thread.h"
> > +
> > +#include "avcodec.h"
> > +#include "bytestream.h"
> > +#include "bswapdsp.h"
> > +#include "get_bits.h"
> > +#include "golomb.h"
> > +#include "internal.h"
> > +
> > +static const uint8_t predict_dc_shift_tab[] = {1, 2, 3, 0, 4};
> > +
> > +static const uint8_t zigzag4x4_tab[] =
> > +{
> > +    0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09,
> > +    0x06, 0x03, 0x07, 0x0A, 0x0D, 0x0E, 0x0B, 0x0F
> > +};
> > +
> > +static const uint8_t quant4x4_tab[][8] =
> > +{
> > +    { 0x0A, 0x0D, 0x0A, 0x0D, 0x0D, 0x10, 0x0D, 0x10 },
> > +    { 0x0B, 0x0E, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12 },
> > +    { 0x0D, 0x10, 0x0D, 0x10, 0x10, 0x14, 0x10, 0x14 },
> > +    { 0x0E, 0x12, 0x0E, 0x12, 0x12, 0x17, 0x12, 0x17 },
> > +    { 0x10, 0x14, 0x10, 0x14, 0x14, 0x19, 0x14, 0x19 },
> > +    { 0x12, 0x17, 0x12, 0x17, 0x17, 0x1D, 0x17, 0x1D }
> > +};
> > +
> > +static const uint8_t old_mb_mode_remap_tab[] =
> > +{
> > +     1,  2,  0,  4,  7,  3, 11, 15,  9,  5, 14,  6,
> > +    12, 13,  8, 16, 23, 10, 22, 19, 20, 17, 21, 18
> > +};
> > +
> > +static const uint8_t residu_mask_old_tab[] =
> > +{
> > +    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x03, 0x05,
> > +    0x0A, 0x0C, 0x0F, 0x1F, 0x07, 0x0B, 0x0D, 0x0E,
> > +    0x06, 0x09, 0x13, 0x15, 0x1A, 0x1C, 0x11, 0x12,
> > +    0x14, 0x18, 0x17, 0x1B, 0x1D, 0x1E, 0x16, 0x19
> > +};
> > +
> > +static const uint8_t residu_mask_new_tab[] =
> > +{
> > +    0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
> > +    0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
> > +    0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
> > +    0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
> > +};
> > +
> > +static const int cavlc_suffix_len_update_tab[] =
> > +{
> > +    2, 5, 11, 23, 47, 32768
> > +};
> > +
> > +// same tables as h264 cavlc
> > +static const uint8_t coeff_token_table_index[17] =
> > +{
> > +    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3
> > +};
> > +
> > +static const uint8_t coeff_token_len[4][4 * 17] = {
> > +{
> > +     1, 0, 0, 0,
> > +     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
> > +    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
> > +    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
> > +    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
> > +},
> > +{
> > +     2, 0, 0, 0,
> > +     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
> > +     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
> > +    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
> > +    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
> > +},
> > +{
> > +     4, 0, 0, 0,
> > +     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
> > +     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
> > +     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
> > +    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
> > +},
> > +{
> > +     6, 0, 0, 0,
> > +     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
> > +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> > +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> > +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> > +}
> > +};
> > +
> > +static const uint8_t coeff_token_bits[4][4 * 17] = {
> > +{
> > +     1, 0, 0, 0,
> > +     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
> > +     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
> > +    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
> > +    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
> > +},
> > +{
> > +     3, 0, 0, 0,
> > +    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
> > +     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
> > +    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
> > +    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
> > +},
> > +{
> > +    15, 0, 0, 0,
> > +    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
> > +    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
> > +    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
> > +    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
> > +},
> > +{
> > +     3, 0, 0, 0,
> > +     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
> > +    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
> > +    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
> > +    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
> > +}
> > +};
> > +
> > +static const uint8_t total_zeros_len[16][16] = {
> > +    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
> > +    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
> > +    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
> > +    {5,3,4,4,3,3,3,4,3,4,5,5,5},
> > +    {4,4,4,3,3,3,3,3,4,5,4,5},
> > +    {6,5,3,3,3,3,3,3,4,3,6},
> > +    {6,5,3,3,3,2,3,4,3,6},
> > +    {6,4,5,3,2,2,3,3,6},
> > +    {6,6,4,2,2,3,2,5},
> > +    {5,5,3,2,2,2,4},
> > +    {4,4,3,3,1,3},
> > +    {4,4,2,1,3},
> > +    {3,3,1,2},
> > +    {2,2,1},
> > +    {1,1},
> > +};
> > +
> > +static const uint8_t total_zeros_bits[16][16] = {
> > +    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
> > +    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
> > +    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
> > +    {3,7,5,4,6,5,4,3,3,2,2,1,0},
> > +    {5,4,3,7,6,5,4,3,2,1,1,0},
> > +    {1,1,7,6,5,4,3,2,1,1,0},
> > +    {1,1,5,4,3,3,2,1,1,0},
> > +    {1,1,1,3,3,2,2,1,0},
> > +    {1,0,1,3,2,1,1,1},
> > +    {1,0,1,3,2,1,1},
> > +    {0,1,1,2,1,3},
> > +    {0,1,1,1,1},
> > +    {0,1,1,1},
> > +    {0,1,1},
> > +    {0,1},
> > +};
> > +
> > +static const uint8_t run_len[7][16] = {
> > +    {1,1},
> > +    {1,2,2},
> > +    {2,2,2,2},
> > +    {2,2,2,3,3},
> > +    {2,2,3,3,3,3},
> > +    {2,3,3,3,3,3,3},
> > +    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
> > +};
> > +
> > +static const uint8_t run_bits[7][16] = {
> > +    {1,0},
> > +    {1,1,0},
> > +    {3,2,1,0},
> > +    {3,2,1,1,0},
> > +    {3,2,3,2,1,0},
> > +    {3,0,1,3,2,5,4},
> > +    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
> > +};
> > +
> > +typedef struct MVec {
> > +    int x, y;
> > +} MVec;
> > +
> > +typedef struct ActimagineContext {
> > +    AVFrame *cur_frame;
> > +    AVFrame *ref_frames[3];
> > +    int      ref_frame_count;
> > +
> > +    int version;
> > +    int quantizer;
> > +    int avi;
> > +
> > +    GetBitContext gb;
> > +
> > +    uint8_t *bitstream;
> > +    int bitstream_size;
> > +
> > +    int qtab[2][4];
> > +
> > +    uint8_t pred4_cache[5][5];
> > +
> > +    MVec *vectors;
> > +    int   vectors_stride;
> > +
> > +    uint8_t *total_coeff_y;
> > +    int      total_coeff_y_stride;
> > +
> > +    uint8_t *total_coeff_uv;
> > +    int      total_coeff_uv_stride;
> > +
> > +    BswapDSPContext bdsp;
> > +} ActimagineContext;
> > +
> > +static VLC coeff_token_vlc[4];
> > +static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
> > +static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
> > +
> > +static VLC total_zeros_vlc[15+1];
> > +static VLC_TYPE total_zeros_vlc_tables[15][512][2];
> > +static const int total_zeros_vlc_tables_size = 512;
> > +
> > +static VLC run_vlc[6+1];
> > +static VLC_TYPE run_vlc_tables[6][8][2];
> > +static const int run_vlc_tables_size = 8;
> > +
> > +static VLC run7_vlc;
> > +static VLC_TYPE run7_vlc_table[96][2];
> > +static const int run7_vlc_table_size = 96;
> > +
> > +#define COEFF_TOKEN_VLC_BITS    8
> > +#define TOTAL_ZEROS_VLC_BITS    9
> > +#define RUN_VLC_BITS            3
> > +#define RUN7_VLC_BITS           6
> > +
> > +#define PIXEL_REF(s, ref, plane, x, y)\
> > +    ((s)->ref_frames[(ref)]->data[(plane)]\
> > +        [(y) * (s)->ref_frames[(ref)]->linesize[(plane)] + (x)])
> > +
> > +#define PIXEL_CUR(s, plane, x, y)\
> > +    ((s)->cur_frame->data[(plane)]\
> > +        [(y) * (s)->cur_frame->linesize[(plane)] + (x)])
> > +
> > +#define PREDICT2(a,b)       (((a) + (b) + 1) >> 1)
> > +#define PREDICT3(a,b,c)     (((a) + 2 * (b) + (c) + 2) >> 2)
> > +
> > +#define VX_VERSION_INVALID  -1
> > +#define VX_VERSION_OLD       0
> > +#define VX_VERSION_NEW       1
> > +
> > +static av_cold void actimagine_init_static(void)
> > +{
> > +    // all these tables are equal to the ones used for h264 cavlc
>
> Then please share them.
>
> > +    int offset = 0;
> > +    for (int i = 0; i < 4; i++) {
> > +        coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
> > +        coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
> > +        init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4 * 17,
> > +                 &coeff_token_len [i][0], 1, 1,
> > +                 &coeff_token_bits[i][0], 1, 1,
> > +                 INIT_VLC_USE_NEW_STATIC);
> > +        offset += coeff_token_vlc_tables_size[i];
> > +    }
> > +    /*
> > +     * This is a one time safety check to make sure that
> > +     * the packed static coeff_token_vlc table sizes
> > +     * were initialized correctly.
> > +     */
> > +    av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables));
> > +
> > +    for (int i = 0; i < 15; i++) {
> > +        total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i];
> > +        total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
> > +        init_vlc(&total_zeros_vlc[i + 1],
> > +                 TOTAL_ZEROS_VLC_BITS, 16,
> > +                 &total_zeros_len [i][0], 1, 1,
> > +                 &total_zeros_bits[i][0], 1, 1,
> > +                 INIT_VLC_USE_NEW_STATIC);
> > +    }
> > +
> > +    for (int i = 0; i < 6; i++) {
> > +        run_vlc[i + 1].table = run_vlc_tables[i];
> > +        run_vlc[i + 1].table_allocated = run_vlc_tables_size;
> > +        init_vlc(&run_vlc[i + 1],
> > +                 RUN_VLC_BITS, 7,
> > +                 &run_len [i][0], 1, 1,
> > +                 &run_bits[i][0], 1, 1,
> > +                 INIT_VLC_USE_NEW_STATIC);
> > +    }
> > +
> > +    run7_vlc.table = run7_vlc_table,
> > +    run7_vlc.table_allocated = run7_vlc_table_size;
> > +    init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
> > +             &run_len [6][0], 1, 1,
> > +             &run_bits[6][0], 1, 1,
> > +             INIT_VLC_USE_NEW_STATIC);
> > +}
> > +
> > +static int setup_qtables(AVCodecContext *avctx, int quantizer)
> > +{
> > +    int qx, qy;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    if (quantizer < 12 || quantizer > 161)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    s->quantizer = quantizer;
> > +
> > +    qx = quantizer % 6;
> > +    qy = quantizer / 6;
> > +
> > +    for (int i = 0; i < 2; i++)
> > +        for (int j = 0; j < 4; j++)
> > +            s->qtab[i][j] = quant4x4_tab[qx][4 * i + j] << qy;
> > +
> > +    return 0;
> > +}
> > +
> > +static av_cold int actimagine_init(AVCodecContext *avctx)
> > +{
> > +    int vectors_size;
> > +    int total_coeff_y_size;
> > +    int total_coeff_uv_size;
> > +    static AVOnce init_static_once = AV_ONCE_INIT;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    if (avctx->width & 15 || avctx->height & 15) {
> > +        av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    ff_bswapdsp_init(&s->bdsp);
> > +
> > +    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
> > +    avctx->color_range = AVCOL_RANGE_JPEG;
> > +    avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
> > +    // the color space of this video format is not supported currently
> > +    // it is a yuv approximation that can be converted back to rgb using bitshifts
> > +    // r = y + (v << 1)
> > +    // g = y - (u >> 1) - v
> > +    // b = y + (u << 1)
> > +    avctx->colorspace = AVCOL_SPC_NB;
> > +
> > +    // predict4 cache
> > +    for (int i = 0; i < 5; i++)
> > +        for (int j = 0; j < 5; j++)
> > +            s->pred4_cache[i][j] = 9;
> > +
> > +    // motion vector cache
> > +    s->vectors_stride = (avctx->width >> 4) + 2;
> > +    vectors_size = ((avctx->height >> 4) + 1) * s->vectors_stride;
> > +    s->vectors = av_calloc(vectors_size, sizeof(MVec));
> > +    if (!s->vectors)
> > +        return AVERROR(ENOMEM);
> > +    memset(s->vectors, 0, vectors_size * sizeof(MVec));
>
> Unnecessary. av_calloc already zeroes it.
>
> > +
> > +    // total dct coefficient cache for luma
> > +    s->total_coeff_y_stride = (avctx->width >> 2) + 1;
> > +    total_coeff_y_size = ((avctx->height >> 2) + 1) * s->total_coeff_y_stride;
> > +    s->total_coeff_y = av_malloc(total_coeff_y_size);
> > +    if (!s->total_coeff_y)
> > +        return AVERROR(ENOMEM);
> > +    memset(s->total_coeff_y, 0, total_coeff_y_size);
>
> Unnecessary: Use av_mallocz.
>
> > +
> > +    // total dct coefficient cache for chroma
> > +    s->total_coeff_uv_stride = (avctx->width >> 3) + 1;
> > +    total_coeff_uv_size = ((avctx->height >> 3) + 1) * s->total_coeff_uv_stride;
> > +    s->total_coeff_uv = av_malloc(total_coeff_uv_size);
> > +    if (!s->total_coeff_uv)
> > +        return AVERROR(ENOMEM);
> > +    memset(s->total_coeff_uv, 0, total_coeff_uv_size);
>
> Same as above.
>
> > +
> > +    s->ref_frame_count = 0;
> > +    for (int i = 0; i < 3; i++) {
> > +        s->ref_frames[i] = av_frame_alloc();
> > +        if (!s->ref_frames[i])
> > +            return AVERROR(ENOMEM);
> > +    }
> > +    s->cur_frame = av_frame_alloc();
> > +    if (!s->cur_frame)
> > +        return AVERROR(ENOMEM);
> > +
> > +    s->version = VX_VERSION_INVALID;
> > +    s->quantizer = -1;
> > +    s->avi = 0;
> > +
> > +    // when the source is an avi file, the quantizer is stored in the extradata
> > +    if (avctx->extradata_size == 4)
> > +        if (!setup_qtables(avctx, AV_RL32(avctx->extradata)))
> > +            s->avi = 1;
> > +
> > +    ff_thread_once(&init_static_once, actimagine_init_static);
> > +
> > +    return 0;
> > +}
> > +
> > +static void clear_total_coeff(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    // luma
> > +    uint8_t *total_coeff = &s->total_coeff_y[
> > +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> > +
> > +    for (int y2 = 0; y2 < (h >> 2); y2++)
> > +        for (int x2 = 0; x2 < (w >> 2); x2++)
> > +            total_coeff[y2 * s->total_coeff_y_stride + x2] = 0;
> > +
> > +    // chroma
> > +    total_coeff = &s->total_coeff_uv[
> > +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> > +
> > +    for (int y2 = 0; y2 < (h >> 3); y2++)
> > +        for (int x2 = 0; x2 < (w >> 3); x2++)
> > +            total_coeff[y2 * s->total_coeff_uv_stride + x2] = 0;
> > +}
> > +
> > +static void predict_plane_intern(AVCodecContext *avctx, int x, int y,
> > +                                 int w, int h, int plane)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    if (w == 1 && h == 1)
> > +        return;
> > +    if (w == 1 && h != 1) {
> > +        uint8_t top    = PIXEL_CUR(s, plane, x, y - 1);
> > +        uint8_t bottom = PIXEL_CUR(s, plane, x, y + h - 1);
> > +        PIXEL_CUR(s, plane, x, y + (h >> 1) - 1) = (top + bottom) >> 1;
> > +        predict_plane_intern(avctx, x, y, 1, h >> 1, plane);
> > +        predict_plane_intern(avctx, x, y + (h >> 1), 1, h >> 1, plane);
> > +    } else if (w != 1 && h == 1) {
> > +        uint8_t left  = PIXEL_CUR(s, plane, x - 1, y);
> > +        uint8_t right = PIXEL_CUR(s, plane, x + w - 1, y);
> > +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y) = (left + right) >> 1;
> > +        predict_plane_intern(avctx, x, y, w >> 1, 1, plane);
> > +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, 1, plane);
> > +    } else {
> > +        uint8_t bottom_left  = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> > +        uint8_t top_right    = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> > +        uint8_t bottom_right = PIXEL_CUR(s, plane, x + w - 1, y + h - 1);
> > +        uint8_t bottom_center = (bottom_left + bottom_right) >> 1;
> > +        uint8_t center_right = (top_right + bottom_right) >> 1;
> > +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + h - 1) = bottom_center;
> > +        PIXEL_CUR(s, plane, x + w - 1, y + (h >> 1) - 1) = center_right;
> > +        if ((w == 4 || w == 16) ^ (h == 4 || h == 16)) {
> > +            uint8_t center_left = PIXEL_CUR(s, plane, x - 1, y + (h >> 1) - 1);
> > +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> > +                = (center_left + center_right) >> 1;
> > +        } else {
> > +            uint8_t top_center = PIXEL_CUR(s, plane, x + (w >> 1) - 1, y - 1);
> > +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> > +                = (top_center + bottom_center) >> 1;
> > +        }
> > +        predict_plane_intern(avctx, x, y, w >> 1, h >> 1, plane);
> > +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, h >> 1, plane);
> > +        predict_plane_intern(avctx, x, y + (h >> 1), w >> 1, h >> 1, plane);
> > +        predict_plane_intern(avctx, x + (w >> 1), y + (h >> 1), w >> 1, h >> 1,
> > +                             plane);
> > +    }
> > +}
> > +
> > +static void predict_plane(AVCodecContext *avctx, int x, int y, int w, int h,
> > +                          int plane, int param)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    uint8_t bottom_left = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> > +    uint8_t top_right   = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> > +    PIXEL_CUR(s, plane, x + w - 1, y + h - 1)
> > +        = ((bottom_left + top_right + 1) >> 1) + param;
> > +    predict_plane_intern(avctx, x, y, w, h, plane);
> > +}
> > +
> > +static int predict_mb_plane(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    // y
> > +    int param = get_se_golomb(gb);
> > +    if (param < -(1 << 16) || param >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    predict_plane(avctx, x, y, w, h, 0, param << 1);
> > +
> > +    // u
> > +    param = get_se_golomb(gb);
> > +    if (param < -(1 << 16) || param >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, param << 1);
> > +
> > +    // v
> > +    param = get_se_golomb(gb);
> > +    if (param < -(1 << 16) || param >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, param << 1);
> > +
> > +    return 0;
> > +}
> > +
> > +static void predict_horizontal(AVCodecContext *avctx, int x, int y,
> > +                               int w, int h, int plane)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    for (int y2 = 0; y2 < h; y2++) {
> > +        uint8_t pixel = PIXEL_CUR(s, plane, x - 1, y + y2);
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            PIXEL_CUR(s, plane, x + x2, y + y2) = pixel;
> > +    }
> > +}
> > +
> > +static void predict_vertical(AVCodecContext *avctx, int x, int y,
> > +                             int w, int h, int plane)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    for (int y2 = 0; y2 < h; y2++)
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            PIXEL_CUR(s, plane, x + x2, y + y2)
> > +                = PIXEL_CUR(s, plane, x + x2, y - 1);
> > +}
> > +
> > +static void predict_dc(AVCodecContext *avctx, int x, int y, int w, int h,
> > +                       int plane)
> > +{
> > +    uint8_t dc;
> > +    ActimagineContext *s = avctx->priv_data;
> > +    if (x != 0 && y != 0) {
> > +        int sum_h, sum_v;
> > +        sum_h = w >> 1;
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            sum_h += PIXEL_CUR(s, plane, x + x2, y - 1);
> > +        sum_h >>= predict_dc_shift_tab[w >> 2];
> > +
> > +        sum_v = h >> 1;
> > +        for (int y2 = 0; y2 < h; y2++)
> > +            sum_v += PIXEL_CUR(s, plane, x - 1, y + y2);
> > +        sum_v >>= predict_dc_shift_tab[h >> 2];
> > +
> > +        dc = (sum_h + sum_v + 1) >> 1;
> > +    } else if (x == 0 && y != 0) {
> > +        int sum = w >> 1;
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            sum += PIXEL_CUR(s, plane, x + x2, y - 1);
> > +        dc = sum >> predict_dc_shift_tab[w >> 2];
> > +    } else if (x != 0 && y == 0) {
> > +        int sum = h >> 1;
> > +        for (int y2 = 0; y2 < h; y2++)
> > +            sum += PIXEL_CUR(s, plane, x - 1, y + y2);
> > +        dc = sum >> predict_dc_shift_tab[h >> 2];
> > +    } else
> > +        dc = 128;
> > +
> > +    for (int y2 = 0; y2 < h; y2++)
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            PIXEL_CUR(s, plane, x + x2, y + y2) = dc;
> > +}
> > +
> > +static int predict_notile_uv(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    int mode_uv = get_ue_golomb_31(gb);
> > +    switch (mode_uv) {
> > +    case 0:// dc
> > +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> > +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> > +        break;
> > +    case 1:// horizontal
> > +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> > +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> > +        break;
> > +    case 2:// vertical
> > +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> > +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> > +        break;
> > +    case 3:// plane
> > +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, 0);
> > +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, 0);
> > +        break;
> > +    default:
> > +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile uv mode\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static int predict_notile(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    int mode_y = get_ue_golomb_31(gb);
> > +    switch (mode_y) {
> > +    case 0:// vertical
> > +        predict_vertical(avctx, x, y, w, h, 0);
> > +        break;
> > +    case 1:// horizontal
> > +        predict_horizontal(avctx, x, y, w, h, 0);
> > +        break;
> > +    case 2:// dc
> > +        predict_dc(avctx, x, y, w, h, 0);
> > +        break;
> > +    case 3:// plane
> > +        predict_plane(avctx, x, y, w, h, 0, 0);
> > +        break;
> > +    default:
> > +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile y mode\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    return predict_notile_uv(avctx, x, y, w, h);
> > +}
> > +
> > +// slightly different from the common dc prediction method
> > +static void predict4_dc(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t dc;
> > +    ActimagineContext *s = avctx->priv_data;
> > +    if (x == 0 && y == 0)
> > +        dc = 128;
> > +    else {
> > +        int sum   = 0;
> > +        int shift = 1;
> > +
> > +        if (x != 0) {
> > +            shift++;
> > +            for (int y2 = 0; y2 < 4; y2++)
> > +                sum += PIXEL_CUR(s, 0, x - 1, y + y2);
> > +            sum += 2;
> > +        }
> > +
> > +        if (y != 0) {
> > +            shift++;
> > +            for (int x2 = 0; x2 < 4; x2++)
> > +                sum += PIXEL_CUR(s, 0, x + x2, y - 1);
> > +            sum += 2;
> > +        }
> > +
> > +        dc = sum >> shift;
> > +    }
> > +
> > +    for (int y2 = 0; y2 < 4; y2++)
> > +        for (int x2 = 0; x2 < 4; x2++)
> > +            PIXEL_CUR(s, 0, x + x2, y + y2) = dc;
> > +}
> > +
> > +static void predict4_diagonal_down_left(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t val;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> > +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> > +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> > +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> > +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> > +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> > +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> > +    uint8_t h = PIXEL_CUR(s, 0, x + 7, y - 1);
> > +
> > +    PIXEL_CUR(s, 0, x, y) = PREDICT3(a, b, c);
> > +
> > +    val = PREDICT3(b, c, d);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> > +
> > +    val = PREDICT3(c, d, e);
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> > +
> > +    val = PREDICT3(d, e, f);
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> > +
> > +    val = PREDICT3(e, f, g);
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> > +
> > +    val = PREDICT3(f, g, h);
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> > +
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(g, h, h);
> > +}
> > +
> > +static void predict4_diagonal_down_right(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t val;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> > +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> > +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> > +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> > +
> > +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> > +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> > +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> > +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> > +
> > +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(j, k, l);
> > +
> > +    val = PREDICT3(i, j, k);
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> > +
> > +    val = PREDICT3(m, i, j);
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> > +
> > +    val = PREDICT3(i, m, a);
> > +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> > +
> > +    val = PREDICT3(m, a, b);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> > +
> > +    val = PREDICT3(a, b, c);
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> > +
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(b, c, d);
> > +}
> > +
> > +static void predict4_vertical_right(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t val;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> > +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> > +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> > +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> > +
> > +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> > +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> > +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> > +
> > +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(i, j, k);
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT3(m, i, j);
> > +
> > +    val = PREDICT3(a, m, i);
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> > +
> > +    val = PREDICT2(a, m);
> > +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> > +
> > +    val = PREDICT3(b, a, m);
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> > +
> > +    val = PREDICT2(b, a);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> > +
> > +    val = PREDICT3(c, b, a);
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> > +
> > +    val = PREDICT2(c, b);
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> > +
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(d, c, b);
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT2(d, c);
> > +}
> > +
> > +static void predict4_horizontal_down(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t val;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> > +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> > +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> > +
> > +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> > +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> > +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> > +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> > +
> > +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT2(k, l);
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = PREDICT3(j, k, l);
> > +
> > +    val = PREDICT2(j, k);
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> > +
> > +    val = PREDICT3(i, j, k);
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> > +
> > +    val = PREDICT2(i, j);
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> > +
> > +    val = PREDICT3(m, i, j);
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> > +
> > +    val = PREDICT2(i, m);
> > +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> > +
> > +    val = PREDICT3(i, m, a);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> > +
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT3(m, a, b);
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(a, b, c);
> > +}
> > +
> > +static void predict4_vertical_left(AVCodecContext *avctx, int x, int y)
> > +{
> > +    uint8_t val;
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> > +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> > +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> > +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> > +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> > +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> > +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> > +
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(e, f, g);
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = PREDICT2(e, f);
> > +
> > +    val = PREDICT3(d, e, f);
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> > +
> > +    val = PREDICT2(d, e);
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> > +
> > +    val = PREDICT3(c, d, e);
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> > +
> > +    val = PREDICT2(c, d);
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> > +
> > +    val = PREDICT3(b, c, d);
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> > +
> > +    val = PREDICT2(b, c);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT3(a, b, c);
> > +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(a, b);
> > +}
> > +
> > +static void predict4_horizontal_up(AVCodecContext *avctx, int x, int y)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> > +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> > +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> > +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(i, j);
> > +    PIXEL_CUR(s, 0, x + 1, y + 0) = PREDICT3(i, j, k);
> > +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT2(j, k);
> > +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(j, k, l);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT2(j, k);
> > +    PIXEL_CUR(s, 0, x + 1, y + 1) = PREDICT3(j, k, l);
> > +    PIXEL_CUR(s, 0, x + 2, y + 1) = PREDICT2(k, l);
> > +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(k, l, l);
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT2(k, l);
> > +    PIXEL_CUR(s, 0, x + 1, y + 2) = PREDICT3(k, l, l);
> > +    PIXEL_CUR(s, 0, x + 2, y + 2) = l;
> > +    PIXEL_CUR(s, 0, x + 3, y + 2) = l;
> > +
> > +    PIXEL_CUR(s, 0, x + 0, y + 3) = l;
> > +    PIXEL_CUR(s, 0, x + 1, y + 3) = l;
> > +    PIXEL_CUR(s, 0, x + 2, y + 3) = l;
> > +    PIXEL_CUR(s, 0, x + 3, y + 3) = l;
> > +}
> > +
> > +static int predict4(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    for (int y2 = 0; y2 < h >> 2; y2++) {
> > +        for (int x2 = 0; x2 < w >> 2; x2++) {
> > +            uint8_t mode = FFMIN(s->pred4_cache[1 + y2 - 1][1 + x2],
> > +                                 s->pred4_cache[1 + y2][1 + x2 - 1]);
> > +            if (mode == 9)// if invalid predict dc
> > +                mode = 2;
> > +
> > +            if (!get_bits1(gb)) {
> > +                uint8_t val = get_bits(gb, 3);
> > +                if (val >= mode)
> > +                    val++;
> > +                mode = val;
> > +            }
> > +
> > +            s->pred4_cache[1 + y2][1 + x2] = mode;
> > +
> > +            switch (mode) {
> > +            case 0:// vertical
> > +                predict_vertical(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> > +                break;
> > +            case 1:// horizontal
> > +                predict_horizontal(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> > +                break;
> > +            case 2:// dc
> > +                predict4_dc(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 3:// diagonal-down-left
> > +                predict4_diagonal_down_left(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 4:// diagonal-down-right
> > +                predict4_diagonal_down_right(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 5:// vertical-right
> > +                predict4_vertical_right(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 6:// horizontal-down
> > +                predict4_horizontal_down(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 7:// vertical-left
> > +                predict4_vertical_left(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            case 8:// horizontal-up
> > +                predict4_horizontal_up(avctx, x + x2 * 4, y + y2 * 4);
> > +                break;
> > +            default:
> > +                av_log(avctx, AV_LOG_ERROR, "invalid predict4 mode\n");
> > +                return AVERROR_INVALIDDATA;
> > +            }
> > +        }
> > +    }
> > +    return predict_notile_uv(avctx, x, y, w, h);
> > +}
> > +
> > +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
> > +                       const int* level)
> > +{
> > +    int a, b, c, d, e, f;
> > +    int dct[16];
> > +    int tmp[16];
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    // dezigzag
> > +    for (int i = 0; i < 16; i++)
> > +        dct[zigzag4x4_tab[i]] = level[i];
> > +
> > +    // dequantize
> > +    for (int i = 0; i < 2; i++) {
> > +        for (int j = 0; j < 4; j++) {
> > +            dct[4 * j + i]     *= s->qtab[i][j];
> > +            dct[4 * j + i + 2] *= s->qtab[i][j];
> > +        }
> > +    }
> > +
> > +    dct[0] += 32;// rounding
> > +
> > +    for (int i = 0; i < 4; i++) {
> > +        a = dct[i * 4 + 0];
> > +        b = dct[i * 4 + 1];
> > +        c = dct[i * 4 + 2];
> > +        d = dct[i * 4 + 3];
> > +        a += c;
> > +        c = a - c * 2;
> > +        e = (b >> 1) - d;
> > +        f = b + (d >> 1);
> > +        tmp[ 0 + i] = a + f;
> > +        tmp[ 4 + i] = c + e;
> > +        tmp[ 8 + i] = c - e;
> > +        tmp[12 + i] = a - f;
> > +    }
> > +
> > +    for (int i = 0; i < 4; i++) {
> > +        a = tmp[i * 4 + 0];
> > +        b = tmp[i * 4 + 1];
> > +        c = tmp[i * 4 + 2];
> > +        d = tmp[i * 4 + 3];
> > +        a += c;
> > +        c =  a - c * 2;
> > +        e  = (b >> 1) - d;
> > +        f = b + (d >> 1);
> > +        PIXEL_CUR(s, plane, x + 0, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
> > +        PIXEL_CUR(s, plane, x + 1, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
> > +        PIXEL_CUR(s, plane, x + 2, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
> > +        PIXEL_CUR(s, plane, x + 3, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
> > +    }
> > +}
> > +
> > +static void decode_residu_cavlc(AVCodecContext *avctx, int x, int y, int plane,
> > +                                int nc, uint8_t *out_total_coeff)
> > +{
> > +    int level[16];
> > +    int coeff_token, total_coeff, trailing_ones, i, zeros_left, suffix_length;
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +
> > +    coeff_token = get_vlc2(gb, coeff_token_vlc[coeff_token_table_index[nc]].table,
> > +                           COEFF_TOKEN_VLC_BITS, 2);
> > +    trailing_ones = coeff_token & 3;
> > +    total_coeff   = coeff_token >> 2;
> > +
> > +    *out_total_coeff = total_coeff;
> > +    if (total_coeff == 0)
> > +        return;
> > +
> > +    av_assert2(total_coeff <= 16);
> > +
> > +    i = 15;
> > +    if (total_coeff != 16) {
> > +        int trailing_zeros;
> > +        zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff].table,
> > +                              TOTAL_ZEROS_VLC_BITS, 1);
> > +        trailing_zeros = 16 - (total_coeff + zeros_left);
> > +        while(trailing_zeros-- > 0)
> > +            level[i--] = 0;
> > +    } else
> > +        zeros_left = 0;
> > +
> > +    suffix_length = 0;
> > +    while (1) {
> > +        int level_suffix, level_code, run_before;
> > +        if (trailing_ones > 0) {
> > +            trailing_ones--;
> > +            level[i--] = get_bits1(gb) ? -1 : 1;
> > +        } else {
> > +            int level_prefix = 0;
> > +            while (!get_bits1(gb))
> > +                level_prefix++;
> > +
> > +            if (level_prefix == 15)
> > +                level_suffix = get_bits(gb, 11);
> > +            else
> > +                level_suffix = suffix_length == 0 ? 0 : get_bits(gb, suffix_length);
> > +
> > +            level_code = level_suffix + (level_prefix << suffix_length);
> > +
> > +            if (level_code > cavlc_suffix_len_update_tab[suffix_length])
> > +                suffix_length++;
> > +
> > +            level_code++;
> > +            if (get_bits1(gb))
> > +                level_code = -level_code;
> > +            level[i--] = level_code;
> > +        }
> > +
> > +        if (--total_coeff == 0)
> > +            break;
> > +
> > +        if (zeros_left == 0)
> > +            continue;
> > +
> > +        if(zeros_left < 7)
> > +            run_before = get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1);
> > +        else
> > +            run_before = get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2);
> > +        zeros_left -= run_before;
> > +        while(run_before-- > 0)
> > +            level[i--] = 0;
> > +    }
> > +
> > +    while(zeros_left-- > 0)
> > +        level[i--] = 0;
> > +
> > +    decode_dct(avctx, x, y, plane, level);
> > +}
> > +
> > +static int decode_residu_blocks(AVCodecContext *avctx, int x, int y,
> > +                                int w, int h)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    uint8_t *total_coeff_y = &s->total_coeff_y[
> > +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> > +    uint8_t *total_coeff_uv = &s->total_coeff_uv[
> > +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> > +    for (int y2 = 0; y2 < h >> 3; y2++) {
> > +        for (int x2 = 0; x2 < w >> 3; x2++) {
> > +            uint8_t residu_mask;
> > +            int code = get_ue_golomb_31(gb);
> > +            if (code > 0x1F) {
> > +                av_log(avctx, AV_LOG_ERROR, "invalid residu mask code\n");
> > +                return AVERROR_INVALIDDATA;
> > +            }
> > +            if (s->version == VX_VERSION_OLD)
> > +                residu_mask = residu_mask_old_tab[code];
> > +            else
> > +                residu_mask = residu_mask_new_tab[code];
> > +
> > +            if (residu_mask & 1) {
> > +                int nc = (total_coeff_y[-1] +
> > +                          total_coeff_y[-s->total_coeff_y_stride] + 1) >> 1;
> > +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8, 0, nc,
> > +                                    &total_coeff_y[0]);
> > +            } else
> > +                total_coeff_y[0] = 0;
> > +
> > +            if (residu_mask & 2) {
> > +                int nc = (total_coeff_y[0] +
> > +                          total_coeff_y[-s->total_coeff_y_stride + 1] + 1) >> 1;
> > +                decode_residu_cavlc(avctx, x + x2 * 8 + 4, y + y2 * 8, 0, nc,
> > +                                    &total_coeff_y[1]);
> > +            } else
> > +                total_coeff_y[1] = 0;
> > +
> > +            if (residu_mask & 4) {
> > +                int nc = (total_coeff_y[s->total_coeff_y_stride - 1] +
> > +                          total_coeff_y[0] + 1) >> 1;
> > +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8 + 4, 0, nc,
> > +                                    &total_coeff_y[s->total_coeff_y_stride]);
> > +            } else
> > +                total_coeff_y[s->total_coeff_y_stride] = 0;
> > +
> > +            if (residu_mask & 8) {
> > +                int nc = (total_coeff_y[s->total_coeff_y_stride] +
> > +                          total_coeff_y[1] + 1) >> 1;
> > +                decode_residu_cavlc(
> > +                    avctx, x + x2 * 8 + 4, y + y2 * 8 + 4, 0, nc,
> > +                    &total_coeff_y[s->total_coeff_y_stride + 1]);
> > +            } else
> > +                total_coeff_y[s->total_coeff_y_stride + 1] = 0;
> > +
> > +            if (residu_mask & 16) {
> > +                uint8_t total_coeff_u, total_coeff_v;
> > +                int nc = (total_coeff_uv[-1] +
> > +                          total_coeff_uv[-s->total_coeff_uv_stride] + 1) >> 1;
> > +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> > +                                    1, nc, &total_coeff_u);
> > +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> > +                                    2, nc, &total_coeff_v);
> > +                total_coeff_uv[0] = (total_coeff_u + total_coeff_v + 1) >> 1;
> > +            } else
> > +                total_coeff_uv[0] = 0;
> > +
> > +            total_coeff_y += 2;
> > +            total_coeff_uv++;
> > +        }
> > +        total_coeff_y  += (s->total_coeff_y_stride << 1) - (w >> 2);
> > +        total_coeff_uv += s->total_coeff_uv_stride - (w >> 3);
> > +    }
> > +    return 0;
> > +}
> > +
> > +static int predict_inter(AVCodecContext *avctx, int x, int y, int w, int h,
> > +                          const MVec *predVec, int has_delta, int ref_frame)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    MVec vec = *predVec;
> > +
> > +    if (ref_frame >= s->ref_frame_count) {
> > +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> > +    s->cur_frame->key_frame = 0;
> > +
> > +    if (has_delta) {
> > +        vec.x += (unsigned)get_se_golomb(gb);
> > +        vec.y += (unsigned)get_se_golomb(gb);
> > +    }
> > +
> > +    if (vec.x >= INT_MAX || vec.y >= INT_MAX)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    s->vectors[(1 + (y >> 4)) * s->vectors_stride + 1 + (x >> 4)] = vec;
> > +
> > +    if (x + vec.x < 0 || x + vec.x + w > avctx->width ||
> > +        y + vec.y < 0 || y + vec.y + h > avctx->height) {
> > +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    // luma
> > +    for (int y2 = 0; y2 < h; y2++)
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            PIXEL_CUR(s, 0, x + x2, y + y2)
> > +                = PIXEL_REF(s, ref_frame, 0, x + x2 + vec.x, y + y2 + vec.y);
> > +
> > +    // chroma
> > +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> > +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> > +            // u
> > +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2)
> > +                = PIXEL_REF(s, ref_frame, 1, (x >> 1) + x2 + (vec.x >> 1),
> > +                            (y >> 1) + y2 + (vec.y >> 1));
> > +            // v
> > +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2)
> > +                = PIXEL_REF(s, ref_frame, 2, (x >> 1) + x2 + (vec.x >> 1),
> > +                            (y >> 1) + y2 + (vec.y >> 1));
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int predict_inter_dc(AVCodecContext *avctx, int x, int y, int w, int h)
> > +{
> > +    int dx, dy, dc_y, dc_u, dc_v;
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +
> > +    if (s->ref_frame_count == 0) {
> > +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    dx   = get_se_golomb(gb);
> > +    dy   = get_se_golomb(gb);
> > +
> > +    if (x + dx < 0 || x + dx + w > avctx->width ||
> > +        y + dy < 0 || y + dy + h > avctx->height) {
> > +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    dc_y = get_se_golomb(gb);
> > +    if (dc_y < -(1<<16) || dc_y >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    dc_y <<= 1;
> > +
> > +    dc_u = get_se_golomb(gb);
> > +    if (dc_u < -(1<<16) || dc_u >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    dc_u <<= 1;
> > +
> > +    dc_v = get_se_golomb(gb);
> > +    if (dc_v < -(1<<16) || dc_v >= (1 << 16)) {
> > +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    dc_v <<= 1;
> > +
> > +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> > +    s->cur_frame->key_frame = 0;
> > +
> > +    // luma
> > +    for (int y2 = 0; y2 < h; y2++)
> > +        for (int x2 = 0; x2 < w; x2++)
> > +            PIXEL_CUR(s, 0, x + x2, y + y2) = av_clip_uint8(
> > +                PIXEL_REF(s, 0, 0, x + x2 + dx, y + y2 + dy) + dc_y);
> > +
> > +    // chroma
> > +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> > +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> > +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> > +                PIXEL_REF(s, 0, 1, (x >> 1) + x2 + (dx >> 1),
> > +                          (y >> 1) + y2 + (dy >> 1)) + dc_u);
> > +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> > +                PIXEL_REF(s, 0, 2, (x >> 1) + x2 + (dx >> 1),
> > +                          (y >> 1) + y2 + (dy >> 1)) + dc_v);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int decode_mb(AVCodecContext *avctx, int x, int y, int w, int h,
> > +                     const MVec *predVec)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    int ret = 0;
> > +
> > +    int mode = get_ue_golomb_31(gb);
> > +    if (s->version == VX_VERSION_OLD)
> > +        mode = old_mb_mode_remap_tab[mode];
> > +
> > +    switch (mode) {
> > +    case 0:// v-split, no residu
> > +        if (w == 2) {
> > +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> > +            return ret;
> > +        if (w == 8 && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 1:// no delta, no residu, ref 0
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 2:// h-split, no residu
> > +        if (h == 2) {
> > +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && h == 8)
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 3:// unpredicted delta ref0 + dc offset, no residu
> > +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 4:// delta, no residu, ref 0
> > +    case 5:// delta, no residu, ref 1
> > +    case 6:// delta, no residu, ref 2
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 4)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 7:// plane, no residu
> > +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 8:// v-split, residu
> > +        if (w == 2) {
> > +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 9:// no delta, no residu, ref 1
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 1)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 10:// unpredicted delta ref0 + dc offset, residu
> > +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 11:// predict notile, no residu
> > +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 12:// no delta, residu, ref 0
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 13:// h-split, residu
> > +        if (h == 2) {
> > +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 14:// no delta, no residu, ref 2
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 2)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 15:// predict4, no residu
> > +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> > +            clear_total_coeff(avctx, x, y, w, h);
> > +        break;
> > +    case 16:// delta, residu, ref 0
> > +    case 17:// delta, residu, ref 1
> > +    case 18:// delta, residu, ref 2
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 16)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 19:// predict4, residu
> > +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 20:// no delta, residu, ref 1
> > +    case 21:// no delta, residu, ref 2
> > +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, mode - 20 + 1)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 22:// predict notile, residu
> > +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    case 23:// plane, residu
> > +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> > +            return ret;
> > +        break;
> > +    default:
> > +        av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static int detect_format(AVCodecContext *avctx)
> > +{
> > +    // assume the new format, if any incorrect decisions are made for the
> > +    // first macroblock of a keyframe (ref, non-dc prediction) then it must be
> > +    // the old format
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    int w = 16;
> > +    int h = 16;
> > +    while (1) {
> > +        int mode = get_ue_golomb_31(gb);
> > +        if (mode == 0 || mode == 8) { // v-split
> > +            if (w == 2) // too many splits
> > +                return VX_VERSION_OLD;
> > +            w >>= 1;
> > +            continue;
> > +        } else if (mode == 2 || mode == 13) { // h-split
> > +            if (h == 2) // too many splits
> > +                return VX_VERSION_OLD;
> > +            h >>= 1;
> > +            continue;
> > +        } else if (mode == 11 || mode == 22) { // predict notile
> > +            if (get_ue_golomb_31(gb) != 2) // mode_y != dc
> > +                return VX_VERSION_OLD;
> > +            if (get_ue_golomb_31(gb) != 0) // mode_uv != dc
> > +                return VX_VERSION_OLD;
> > +            break; //we should have enough evidence now
> > +        } else if (mode == 15 || mode == 19) { // predict4
> > +            // initial prediction is always dc
> > +            // we don't expect that prediction to be wrong
> > +            if (!get_bits1(gb))
> > +                return VX_VERSION_OLD;
> > +            break; //we should have enough evidence now
> > +        } else // inter prediction, plane or any other value
> > +            return VX_VERSION_OLD;
> > +    }
> > +    return VX_VERSION_NEW;
> > +}
> > +
> > +static int actimagine_decode(AVCodecContext *avctx, void *data,
> > +                            int *got_frame, AVPacket *pkt)
> > +{
> > +    MVec *vectors;
> > +    int ret;
> > +    ActimagineContext *s = avctx->priv_data;
> > +    GetBitContext *gb = &s->gb;
> > +    AVFrame *frame = s->cur_frame;
> > +
> > +    // in avi files the frames start with a 32 bit number that seems to
> > +    // indicate the total number of bits
> > +    int offset = s->avi ? 4 : 0;
> > +
> > +    av_fast_padded_malloc(&s->bitstream, &s->bitstream_size,
> > +                          pkt->size);
> > +
> > +    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
> > +        return ret;
> > +
> > +    s->bdsp.bswap16_buf((uint16_t *)s->bitstream, (uint16_t *)pkt->data,
> > +                        (pkt->size + 1) >> 1);
> > +
> > +    ret = init_get_bits8(gb, s->bitstream + offset,
> > +                         FFALIGN(pkt->size - offset, 2));
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    // determine the bitstream version if this was not done yet
> > +    if (s->version == VX_VERSION_INVALID) {
> > +        if (s->ref_frame_count != 0) {
> > +            av_log(avctx, AV_LOG_ERROR, "can't determine version on p frame\n");
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        s->version = detect_format(avctx);
> > +
> > +        // reinit bitreader
> > +        ret = init_get_bits8(gb, s->bitstream + offset,
> > +                             FFALIGN(pkt->size - offset, 2));
> > +        if (ret < 0)
> > +            return ret;
> > +    }
> > +
> > +    vectors = s->vectors + s->vectors_stride + 1;
> > +
> > +    frame->pict_type = AV_PICTURE_TYPE_I;
> > +    frame->key_frame = 1;
> > +
> > +    if (s->quantizer == -1) {
> > +        av_log(avctx, AV_LOG_ERROR, "no quantizer setup\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    for (int y = 0; y < avctx->height; y += 16) {
> > +        MVec *vec_cur = vectors;
> > +        for (int x = 0; x < avctx->width; x += 16) {
> > +            MVec predVec;
> > +            vec_cur[0].x = 0;
> > +            vec_cur[0].y = 0;
> > +            predVec.x = mid_pred(vec_cur[-1].x, vec_cur[-s->vectors_stride].x,
> > +                                 vec_cur[-s->vectors_stride + 1].x);
> > +            predVec.y = mid_pred(vec_cur[-1].y, vec_cur[-s->vectors_stride].y,
> > +                                 vec_cur[-s->vectors_stride + 1].y);
> > +            if ((ret = decode_mb(avctx, x, y, 16, 16, &predVec)) < 0)
> > +                return ret;
> > +            vec_cur++;
> > +        }
> > +        vectors += s->vectors_stride;
> > +    }
> > +
> > +    if (s->ref_frame_count == 3)
> > +        av_frame_unref(s->ref_frames[2]);
> > +
> > +    s->cur_frame = s->ref_frames[2];
> > +    s->ref_frames[2] = s->ref_frames[1];
> > +    s->ref_frames[1] = s->ref_frames[0];
> > +    s->ref_frames[0] = frame;
> > +
> > +    if (s->ref_frame_count < 3)
> > +        s->ref_frame_count++;
> > +
> > +    if ((ret = av_frame_ref(data, frame)) < 0)
> > +        return ret;
> > +    *got_frame = 1;
> > +
> > +    return 0;
> > +}
> > +
> > +static void actimagine_flush(AVCodecContext *avctx)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    for (int i = 0; i < 3; i++)
> > +        av_frame_unref(s->ref_frames[i]);
> > +
> > +    s->ref_frame_count = 0;
> > +}
> > +
> > +static av_cold int actimagine_close(AVCodecContext *avctx)
> > +{
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    av_freep(&s->vectors);
> > +    s->vectors_stride = 0;
> > +    av_freep(&s->total_coeff_y);
> > +    s->total_coeff_y_stride = 0;
> > +    av_freep(&s->total_coeff_uv);
> > +    s->total_coeff_uv_stride = 0;
> > +
> > +    av_freep(&s->bitstream);
> > +    s->bitstream_size = 0;
> > +
> > +    for (int i = 0; i < 3; i++)
> > +        av_frame_free(&s->ref_frames[i]);
> > +    av_frame_free(&s->cur_frame);
> > +
> > +    return 0;
> > +}
> > +
> > +AVCodec ff_actimagine_decoder = {
> > +    .name           = "actimagine",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> > +    .type           = AVMEDIA_TYPE_VIDEO,
> > +    .id             = AV_CODEC_ID_ACTIMAGINE,
> > +    .priv_data_size = sizeof(ActimagineContext),
> > +    .init           = actimagine_init,
> > +    .decode         = actimagine_decode,
> > +    .flush          = actimagine_flush,
> > +    .close          = actimagine_close,
> > +    .capabilities   = AV_CODEC_CAP_DR1,
> > +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
> > +};
> > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> > index 2e9a3581de..10809f3492 100644
> > --- a/libavcodec/allcodecs.c
> > +++ b/libavcodec/allcodecs.c
> > @@ -32,6 +32,7 @@
> >  extern AVCodec ff_a64multi_encoder;
> >  extern AVCodec ff_a64multi5_encoder;
> >  extern AVCodec ff_aasc_decoder;
> > +extern AVCodec ff_actimagine_decoder;
> >  extern AVCodec ff_aic_decoder;
> >  extern AVCodec ff_alias_pix_encoder;
> >  extern AVCodec ff_alias_pix_decoder;
> > diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> > index 17f8a14044..65d96c21af 100644
> > --- a/libavcodec/codec_desc.c
> > +++ b/libavcodec/codec_desc.c
> > @@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
> >          .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
> >          .props     = AV_CODEC_PROP_LOSSY,
> >      },
> > +    {
> > +        .id        = AV_CODEC_ID_ACTIMAGINE,
> > +        .type      = AVMEDIA_TYPE_VIDEO,
> > +        .name      = "actimagine",
> > +        .long_name = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> > +        .props     = AV_CODEC_PROP_LOSSY,
> > +    },
> >
> >      /* various PCM "codecs" */
> >      {
> > diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> > index ab7bc68ee2..a4b3f3955d 100644
> > --- a/libavcodec/codec_id.h
> > +++ b/libavcodec/codec_id.h
> > @@ -307,6 +307,7 @@ enum AVCodecID {
> >      AV_CODEC_ID_CRI,
> >      AV_CODEC_ID_SIMBIOSIS_IMX,
> >      AV_CODEC_ID_SGA_VIDEO,
> > +    AV_CODEC_ID_ACTIMAGINE,
> >
> >      /* various PCM "codecs" */
> >      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
> > diff --git a/libavcodec/version.h b/libavcodec/version.h
> > index 4299ad4239..f992e1b36e 100644
> > --- a/libavcodec/version.h
> > +++ b/libavcodec/version.h
> > @@ -28,7 +28,7 @@
> >  #include "libavutil/version.h"
> >
> >  #define LIBAVCODEC_VERSION_MAJOR  58
> > -#define LIBAVCODEC_VERSION_MINOR 131
> > +#define LIBAVCODEC_VERSION_MINOR 132
> >  #define LIBAVCODEC_VERSION_MICRO 100
> >
> >  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> > diff --git a/libavformat/riff.c b/libavformat/riff.c
> > index 270ff7c024..5d5cfe16b0 100644
> > --- a/libavformat/riff.c
> > +++ b/libavformat/riff.c
> > @@ -496,6 +496,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
> >      { AV_CODEC_ID_MVHA,         MKTAG('M', 'V', 'H', 'A') },
> >      { AV_CODEC_ID_MV30,         MKTAG('M', 'V', '3', '0') },
> >      { AV_CODEC_ID_NOTCHLC,      MKTAG('n', 'l', 'c', '1') },
> > +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('V', 'X', 'S', '1') },
> > +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('v', 'x', 's', '1') },
> >      { AV_CODEC_ID_NONE,         0 }
> >  };
> >
> >
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Florian Nouwt March 15, 2021, 3:13 p.m. UTC | #3
I applied the few changes you suggested. I moved the shared tables to
h264_cavlc_data.c. Am I supposed to share the actual vlc tables (such
as "static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];")
which are generated by actimagine_init_static respectively
ff_h264_decode_init_vlc as well?

Op ma 15 mrt. 2021 om 16:10 schreef Florian Nouwt <fnouwt2@gmail.com>:
>
> Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
> ---
>  Changelog                    |    1 +
>  configure                    |    1 +
>  doc/general_contents.texi    |    2 +
>  libavcodec/Makefile          |    3 +-
>  libavcodec/actimagine.c      | 1523 ++++++++++++++++++++++++++++++++++
>  libavcodec/allcodecs.c       |    1 +
>  libavcodec/codec_desc.c      |    7 +
>  libavcodec/codec_id.h        |    1 +
>  libavcodec/h264_cavlc.c      |  135 +--
>  libavcodec/h264_cavlc_data.c |  140 ++++
>  libavcodec/h264_cavlc_data.h |   33 +
>  libavcodec/version.h         |    2 +-
>  libavformat/riff.c           |    2 +
>  13 files changed, 1723 insertions(+), 128 deletions(-)
>  create mode 100644 libavcodec/actimagine.c
>  create mode 100644 libavcodec/h264_cavlc_data.c
>  create mode 100644 libavcodec/h264_cavlc_data.h
>
> diff --git a/Changelog b/Changelog
> index a96e350e09..8807f3dcb3 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -83,6 +83,7 @@ version <next>:
>  - msad video filter
>  - gophers protocol
>  - RIST protocol via librist
> +- Actimagine VX video decoder
>
>
>  version 4.3:
> diff --git a/configure b/configure
> index f0ac719d2d..10a07da95f 100755
> --- a/configure
> +++ b/configure
> @@ -2662,6 +2662,7 @@ ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct"
>  ac3_encoder_select="ac3dsp audiodsp mdct me_cmp"
>  ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp"
>  acelp_kelvin_decoder_select="audiodsp"
> +actimagine_decoder_select="bswapdsp golomb"
>  adpcm_g722_decoder_select="g722dsp"
>  adpcm_g722_encoder_select="g722dsp"
>  aic_decoder_select="golomb idctdsp"
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index 33ece6e884..d4261386fc 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -807,6 +807,8 @@ following image formats are supported:
>  @item 8088flex TMV           @tab     @tab  X
>  @item A64 multicolor         @tab  X  @tab
>      @tab Creates video suitable to be played on a commodore 64 (multicolor mode).
> +@item Actimagine VX Video    @tab     @tab  X
> +    @tab fourcc: vxs1, VXS1
>  @item Amazing Studio PAF Video @tab     @tab  X
>  @item American Laser Games MM  @tab    @tab X
>      @tab Used in games like Mad Dog McCree.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 81cc16471b..1b90c66925 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -182,6 +182,7 @@ OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
>  OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o
>  OBJS-$(CONFIG_AC3_MF_ENCODER)          += mfenc.o mf_utils.o
>  OBJS-$(CONFIG_ACELP_KELVIN_DECODER)    += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o
> +OBJS-$(CONFIG_ACTIMAGINE_DECODER)      += actimagine.o h264_cavlc_data.o
>  OBJS-$(CONFIG_AGM_DECODER)             += agm.o
>  OBJS-$(CONFIG_AIC_DECODER)             += aic.o
>  OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
> @@ -367,7 +368,7 @@ OBJS-$(CONFIG_H264_DECODER)            += h264dec.o h264_cabac.o h264_cavlc.o \
>                                            h264_direct.o h264_loopfilter.o  \
>                                            h264_mb.o h264_picture.o \
>                                            h264_refs.o h264_sei.o \
> -                                          h264_slice.o h264data.o
> +                                          h264_slice.o h264data.o h264_cavlc_data.o
>  OBJS-$(CONFIG_H264_AMF_ENCODER)        += amfenc_h264.o
>  OBJS-$(CONFIG_H264_CUVID_DECODER)      += cuviddec.o
>  OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o
> diff --git a/libavcodec/actimagine.c b/libavcodec/actimagine.c
> new file mode 100644
> index 0000000000..f495426869
> --- /dev/null
> +++ b/libavcodec/actimagine.c
> @@ -0,0 +1,1523 @@
> +/*
> + * Actimagine VX Video decoder
> + * Copyright (c) 2021 Florian Nouwt
> + *
> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <inttypes.h>
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/thread.h"
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "bswapdsp.h"
> +#include "get_bits.h"
> +#include "golomb.h"
> +#include "internal.h"
> +#include "h264_cavlc_data.h"
> +
> +static const uint8_t predict_dc_shift_tab[] = {1, 2, 3, 0, 4};
> +
> +static const uint8_t zigzag4x4_tab[] =
> +{
> +    0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09,
> +    0x06, 0x03, 0x07, 0x0A, 0x0D, 0x0E, 0x0B, 0x0F
> +};
> +
> +static const uint8_t quant4x4_tab[][8] =
> +{
> +    { 0x0A, 0x0D, 0x0A, 0x0D, 0x0D, 0x10, 0x0D, 0x10 },
> +    { 0x0B, 0x0E, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12 },
> +    { 0x0D, 0x10, 0x0D, 0x10, 0x10, 0x14, 0x10, 0x14 },
> +    { 0x0E, 0x12, 0x0E, 0x12, 0x12, 0x17, 0x12, 0x17 },
> +    { 0x10, 0x14, 0x10, 0x14, 0x14, 0x19, 0x14, 0x19 },
> +    { 0x12, 0x17, 0x12, 0x17, 0x17, 0x1D, 0x17, 0x1D }
> +};
> +
> +static const uint8_t old_mb_mode_remap_tab[] =
> +{
> +     1,  2,  0,  4,  7,  3, 11, 15,  9,  5, 14,  6,
> +    12, 13,  8, 16, 23, 10, 22, 19, 20, 17, 21, 18
> +};
> +
> +static const uint8_t residu_mask_old_tab[] =
> +{
> +    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x03, 0x05,
> +    0x0A, 0x0C, 0x0F, 0x1F, 0x07, 0x0B, 0x0D, 0x0E,
> +    0x06, 0x09, 0x13, 0x15, 0x1A, 0x1C, 0x11, 0x12,
> +    0x14, 0x18, 0x17, 0x1B, 0x1D, 0x1E, 0x16, 0x19
> +};
> +
> +static const uint8_t residu_mask_new_tab[] =
> +{
> +    0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
> +    0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
> +    0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
> +    0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
> +};
> +
> +static const int cavlc_suffix_len_update_tab[] =
> +{
> +    2, 5, 11, 23, 47, 32768
> +};
> +
> +static const uint8_t coeff_token_table_index[17] =
> +{
> +    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3
> +};
> +
> +typedef struct MVec {
> +    int x, y;
> +} MVec;
> +
> +typedef struct ActimagineContext {
> +    AVFrame *cur_frame;
> +    AVFrame *ref_frames[3];
> +    int      ref_frame_count;
> +
> +    int version;
> +    int quantizer;
> +    int avi;
> +
> +    GetBitContext gb;
> +
> +    uint8_t *bitstream;
> +    int bitstream_size;
> +
> +    int qtab[2][4];
> +
> +    uint8_t pred4_cache[5][5];
> +
> +    MVec *vectors;
> +    int   vectors_stride;
> +
> +    uint8_t *total_coeff_y;
> +    int      total_coeff_y_stride;
> +
> +    uint8_t *total_coeff_uv;
> +    int      total_coeff_uv_stride;
> +
> +    BswapDSPContext bdsp;
> +} ActimagineContext;
> +
> +static VLC coeff_token_vlc[4];
> +static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
> +static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
> +
> +static VLC total_zeros_vlc[15+1];
> +static VLC_TYPE total_zeros_vlc_tables[15][512][2];
> +static const int total_zeros_vlc_tables_size = 512;
> +
> +static VLC run_vlc[6+1];
> +static VLC_TYPE run_vlc_tables[6][8][2];
> +static const int run_vlc_tables_size = 8;
> +
> +static VLC run7_vlc;
> +static VLC_TYPE run7_vlc_table[96][2];
> +static const int run7_vlc_table_size = 96;
> +
> +#define COEFF_TOKEN_VLC_BITS    8
> +#define TOTAL_ZEROS_VLC_BITS    9
> +#define RUN_VLC_BITS            3
> +#define RUN7_VLC_BITS           6
> +
> +#define PIXEL_REF(s, ref, plane, x, y)\
> +    ((s)->ref_frames[(ref)]->data[(plane)]\
> +        [(y) * (s)->ref_frames[(ref)]->linesize[(plane)] + (x)])
> +
> +#define PIXEL_CUR(s, plane, x, y)\
> +    ((s)->cur_frame->data[(plane)]\
> +        [(y) * (s)->cur_frame->linesize[(plane)] + (x)])
> +
> +#define PREDICT2(a,b)       (((a) + (b) + 1) >> 1)
> +#define PREDICT3(a,b,c)     (((a) + 2 * (b) + (c) + 2) >> 2)
> +
> +#define VX_VERSION_INVALID  -1
> +#define VX_VERSION_OLD       0
> +#define VX_VERSION_NEW       1
> +
> +static av_cold void actimagine_init_static(void)
> +{
> +    // all these tables are equal to the ones used for h264 cavlc
> +    int offset = 0;
> +    for (int i = 0; i < 4; i++) {
> +        coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
> +        coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
> +        init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4 * 17,
> +                 &ff_h264_cavlc_coeff_token_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_coeff_token_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +        offset += coeff_token_vlc_tables_size[i];
> +    }
> +    /*
> +     * This is a one time safety check to make sure that
> +     * the packed static coeff_token_vlc table sizes
> +     * were initialized correctly.
> +     */
> +    av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables));
> +
> +    for (int i = 0; i < 15; i++) {
> +        total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i];
> +        total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
> +        init_vlc(&total_zeros_vlc[i + 1],
> +                 TOTAL_ZEROS_VLC_BITS, 16,
> +                 &ff_h264_cavlc_total_zeros_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_total_zeros_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +    }
> +
> +    for (int i = 0; i < 6; i++) {
> +        run_vlc[i + 1].table = run_vlc_tables[i];
> +        run_vlc[i + 1].table_allocated = run_vlc_tables_size;
> +        init_vlc(&run_vlc[i + 1],
> +                 RUN_VLC_BITS, 7,
> +                 &ff_h264_cavlc_run_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_run_bits[i][0], 1, 1,
> +                 INIT_VLC_USE_NEW_STATIC);
> +    }
> +
> +    run7_vlc.table = run7_vlc_table,
> +    run7_vlc.table_allocated = run7_vlc_table_size;
> +    init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
> +             &ff_h264_cavlc_run_len [6][0], 1, 1,
> +             &ff_h264_cavlc_run_bits[6][0], 1, 1,
> +             INIT_VLC_USE_NEW_STATIC);
> +}
> +
> +static int setup_qtables(AVCodecContext *avctx, int quantizer)
> +{
> +    int qx, qy;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    if (quantizer < 12 || quantizer > 161)
> +        return AVERROR_INVALIDDATA;
> +
> +    s->quantizer = quantizer;
> +
> +    qx = quantizer % 6;
> +    qy = quantizer / 6;
> +
> +    for (int i = 0; i < 2; i++)
> +        for (int j = 0; j < 4; j++)
> +            s->qtab[i][j] = quant4x4_tab[qx][4 * i + j] << qy;
> +
> +    return 0;
> +}
> +
> +static av_cold int actimagine_init(AVCodecContext *avctx)
> +{
> +    int vectors_size;
> +    int total_coeff_y_size;
> +    int total_coeff_uv_size;
> +    static AVOnce init_static_once = AV_ONCE_INIT;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    if (avctx->width & 15 || avctx->height & 15) {
> +        av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    ff_bswapdsp_init(&s->bdsp);
> +
> +    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
> +    avctx->color_range = AVCOL_RANGE_JPEG;
> +    avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
> +    // the color space of this video format is not supported currently
> +    // it is a yuv approximation that can be converted back to rgb using bitshifts
> +    // r = y + (v << 1)
> +    // g = y - (u >> 1) - v
> +    // b = y + (u << 1)
> +    avctx->colorspace = AVCOL_SPC_NB;
> +
> +    // predict4 cache
> +    for (int i = 0; i < 5; i++)
> +        for (int j = 0; j < 5; j++)
> +            s->pred4_cache[i][j] = 9;
> +
> +    // motion vector cache
> +    s->vectors_stride = (avctx->width >> 4) + 2;
> +    vectors_size = ((avctx->height >> 4) + 1) * s->vectors_stride;
> +    s->vectors = av_calloc(vectors_size, sizeof(MVec));
> +    if (!s->vectors)
> +        return AVERROR(ENOMEM);
> +
> +    // total dct coefficient cache for luma
> +    s->total_coeff_y_stride = (avctx->width >> 2) + 1;
> +    total_coeff_y_size = ((avctx->height >> 2) + 1) * s->total_coeff_y_stride;
> +    s->total_coeff_y = av_mallocz(total_coeff_y_size);
> +    if (!s->total_coeff_y)
> +        return AVERROR(ENOMEM);
> +
> +    // total dct coefficient cache for chroma
> +    s->total_coeff_uv_stride = (avctx->width >> 3) + 1;
> +    total_coeff_uv_size = ((avctx->height >> 3) + 1) * s->total_coeff_uv_stride;
> +    s->total_coeff_uv = av_mallocz(total_coeff_uv_size);
> +    if (!s->total_coeff_uv)
> +        return AVERROR(ENOMEM);
> +
> +    s->ref_frame_count = 0;
> +    for (int i = 0; i < 3; i++) {
> +        s->ref_frames[i] = av_frame_alloc();
> +        if (!s->ref_frames[i])
> +            return AVERROR(ENOMEM);
> +    }
> +    s->cur_frame = av_frame_alloc();
> +    if (!s->cur_frame)
> +        return AVERROR(ENOMEM);
> +
> +    s->version = VX_VERSION_INVALID;
> +    s->quantizer = -1;
> +    s->avi = 0;
> +
> +    // when the source is an avi file, the quantizer is stored in the extradata
> +    if (avctx->extradata_size == 4)
> +        if (!setup_qtables(avctx, AV_RL32(avctx->extradata)))
> +            s->avi = 1;
> +
> +    ff_thread_once(&init_static_once, actimagine_init_static);
> +
> +    return 0;
> +}
> +
> +static void clear_total_coeff(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    // luma
> +    uint8_t *total_coeff = &s->total_coeff_y[
> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> +
> +    for (int y2 = 0; y2 < (h >> 2); y2++)
> +        for (int x2 = 0; x2 < (w >> 2); x2++)
> +            total_coeff[y2 * s->total_coeff_y_stride + x2] = 0;
> +
> +    // chroma
> +    total_coeff = &s->total_coeff_uv[
> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> +
> +    for (int y2 = 0; y2 < (h >> 3); y2++)
> +        for (int x2 = 0; x2 < (w >> 3); x2++)
> +            total_coeff[y2 * s->total_coeff_uv_stride + x2] = 0;
> +}
> +
> +static void predict_plane_intern(AVCodecContext *avctx, int x, int y,
> +                                 int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    if (w == 1 && h == 1)
> +        return;
> +    if (w == 1 && h != 1) {
> +        uint8_t top    = PIXEL_CUR(s, plane, x, y - 1);
> +        uint8_t bottom = PIXEL_CUR(s, plane, x, y + h - 1);
> +        PIXEL_CUR(s, plane, x, y + (h >> 1) - 1) = (top + bottom) >> 1;
> +        predict_plane_intern(avctx, x, y, 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x, y + (h >> 1), 1, h >> 1, plane);
> +    } else if (w != 1 && h == 1) {
> +        uint8_t left  = PIXEL_CUR(s, plane, x - 1, y);
> +        uint8_t right = PIXEL_CUR(s, plane, x + w - 1, y);
> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y) = (left + right) >> 1;
> +        predict_plane_intern(avctx, x, y, w >> 1, 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, 1, plane);
> +    } else {
> +        uint8_t bottom_left  = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> +        uint8_t top_right    = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> +        uint8_t bottom_right = PIXEL_CUR(s, plane, x + w - 1, y + h - 1);
> +        uint8_t bottom_center = (bottom_left + bottom_right) >> 1;
> +        uint8_t center_right = (top_right + bottom_right) >> 1;
> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + h - 1) = bottom_center;
> +        PIXEL_CUR(s, plane, x + w - 1, y + (h >> 1) - 1) = center_right;
> +        if ((w == 4 || w == 16) ^ (h == 4 || h == 16)) {
> +            uint8_t center_left = PIXEL_CUR(s, plane, x - 1, y + (h >> 1) - 1);
> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> +                = (center_left + center_right) >> 1;
> +        } else {
> +            uint8_t top_center = PIXEL_CUR(s, plane, x + (w >> 1) - 1, y - 1);
> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
> +                = (top_center + bottom_center) >> 1;
> +        }
> +        predict_plane_intern(avctx, x, y, w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x, y + (h >> 1), w >> 1, h >> 1, plane);
> +        predict_plane_intern(avctx, x + (w >> 1), y + (h >> 1), w >> 1, h >> 1,
> +                             plane);
> +    }
> +}
> +
> +static void predict_plane(AVCodecContext *avctx, int x, int y, int w, int h,
> +                          int plane, int param)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    uint8_t bottom_left = PIXEL_CUR(s, plane, x - 1, y + h - 1);
> +    uint8_t top_right   = PIXEL_CUR(s, plane, x + w - 1, y - 1);
> +    PIXEL_CUR(s, plane, x + w - 1, y + h - 1)
> +        = ((bottom_left + top_right + 1) >> 1) + param;
> +    predict_plane_intern(avctx, x, y, w, h, plane);
> +}
> +
> +static int predict_mb_plane(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    // y
> +    int param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x, y, w, h, 0, param << 1);
> +
> +    // u
> +    param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, param << 1);
> +
> +    // v
> +    param = get_se_golomb(gb);
> +    if (param < -(1 << 16) || param >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, param << 1);
> +
> +    return 0;
> +}
> +
> +static void predict_horizontal(AVCodecContext *avctx, int x, int y,
> +                               int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    for (int y2 = 0; y2 < h; y2++) {
> +        uint8_t pixel = PIXEL_CUR(s, plane, x - 1, y + y2);
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2) = pixel;
> +    }
> +}
> +
> +static void predict_vertical(AVCodecContext *avctx, int x, int y,
> +                             int w, int h, int plane)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2)
> +                = PIXEL_CUR(s, plane, x + x2, y - 1);
> +}
> +
> +static void predict_dc(AVCodecContext *avctx, int x, int y, int w, int h,
> +                       int plane)
> +{
> +    uint8_t dc;
> +    ActimagineContext *s = avctx->priv_data;
> +    if (x != 0 && y != 0) {
> +        int sum_h, sum_v;
> +        sum_h = w >> 1;
> +        for (int x2 = 0; x2 < w; x2++)
> +            sum_h += PIXEL_CUR(s, plane, x + x2, y - 1);
> +        sum_h >>= predict_dc_shift_tab[w >> 2];
> +
> +        sum_v = h >> 1;
> +        for (int y2 = 0; y2 < h; y2++)
> +            sum_v += PIXEL_CUR(s, plane, x - 1, y + y2);
> +        sum_v >>= predict_dc_shift_tab[h >> 2];
> +
> +        dc = (sum_h + sum_v + 1) >> 1;
> +    } else if (x == 0 && y != 0) {
> +        int sum = w >> 1;
> +        for (int x2 = 0; x2 < w; x2++)
> +            sum += PIXEL_CUR(s, plane, x + x2, y - 1);
> +        dc = sum >> predict_dc_shift_tab[w >> 2];
> +    } else if (x != 0 && y == 0) {
> +        int sum = h >> 1;
> +        for (int y2 = 0; y2 < h; y2++)
> +            sum += PIXEL_CUR(s, plane, x - 1, y + y2);
> +        dc = sum >> predict_dc_shift_tab[h >> 2];
> +    } else
> +        dc = 128;
> +
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, plane, x + x2, y + y2) = dc;
> +}
> +
> +static int predict_notile_uv(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int mode_uv = get_ue_golomb_31(gb);
> +    switch (mode_uv) {
> +    case 0:// dc
> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 1:// horizontal
> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 2:// vertical
> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
> +        break;
> +    case 3:// plane
> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, 0);
> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, 0);
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile uv mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return 0;
> +}
> +
> +static int predict_notile(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int mode_y = get_ue_golomb_31(gb);
> +    switch (mode_y) {
> +    case 0:// vertical
> +        predict_vertical(avctx, x, y, w, h, 0);
> +        break;
> +    case 1:// horizontal
> +        predict_horizontal(avctx, x, y, w, h, 0);
> +        break;
> +    case 2:// dc
> +        predict_dc(avctx, x, y, w, h, 0);
> +        break;
> +    case 3:// plane
> +        predict_plane(avctx, x, y, w, h, 0, 0);
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile y mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return predict_notile_uv(avctx, x, y, w, h);
> +}
> +
> +// slightly different from the common dc prediction method
> +static void predict4_dc(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t dc;
> +    ActimagineContext *s = avctx->priv_data;
> +    if (x == 0 && y == 0)
> +        dc = 128;
> +    else {
> +        int sum   = 0;
> +        int shift = 1;
> +
> +        if (x != 0) {
> +            shift++;
> +            for (int y2 = 0; y2 < 4; y2++)
> +                sum += PIXEL_CUR(s, 0, x - 1, y + y2);
> +            sum += 2;
> +        }
> +
> +        if (y != 0) {
> +            shift++;
> +            for (int x2 = 0; x2 < 4; x2++)
> +                sum += PIXEL_CUR(s, 0, x + x2, y - 1);
> +            sum += 2;
> +        }
> +
> +        dc = sum >> shift;
> +    }
> +
> +    for (int y2 = 0; y2 < 4; y2++)
> +        for (int x2 = 0; x2 < 4; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2) = dc;
> +}
> +
> +static void predict4_diagonal_down_left(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> +    uint8_t h = PIXEL_CUR(s, 0, x + 7, y - 1);
> +
> +    PIXEL_CUR(s, 0, x, y) = PREDICT3(a, b, c);
> +
> +    val = PREDICT3(b, c, d);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +
> +    val = PREDICT3(c, d, e);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +
> +    val = PREDICT3(d, e, f);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> +
> +    val = PREDICT3(e, f, g);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT3(f, g, h);
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(g, h, h);
> +}
> +
> +static void predict4_diagonal_down_right(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(j, k, l);
> +
> +    val = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT3(m, i, j);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT3(i, m, a);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT3(m, a, b);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    val = PREDICT3(a, b, c);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(b, c, d);
> +}
> +
> +static void predict4_vertical_right(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT3(m, i, j);
> +
> +    val = PREDICT3(a, m, i);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT2(a, m);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +
> +    val = PREDICT3(b, a, m);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT2(b, a);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(c, b, a);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT2(c, b);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(d, c, b);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT2(d, c);
> +}
> +
> +static void predict4_horizontal_down(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = PREDICT3(j, k, l);
> +
> +    val = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
> +
> +    val = PREDICT2(i, j);
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(m, i, j);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
> +
> +    val = PREDICT2(i, m);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +
> +    val = PREDICT3(i, m, a);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT3(m, a, b);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(a, b, c);
> +}
> +
> +static void predict4_vertical_left(AVCodecContext *avctx, int x, int y)
> +{
> +    uint8_t val;
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
> +
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(e, f, g);
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = PREDICT2(e, f);
> +
> +    val = PREDICT3(d, e, f);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
> +
> +    val = PREDICT2(d, e);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
> +
> +    val = PREDICT3(c, d, e);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
> +
> +    val = PREDICT2(c, d);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
> +
> +    val = PREDICT3(b, c, d);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
> +
> +    val = PREDICT2(b, c);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT3(a, b, c);
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(a, b);
> +}
> +
> +static void predict4_horizontal_up(AVCodecContext *avctx, int x, int y)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(i, j);
> +    PIXEL_CUR(s, 0, x + 1, y + 0) = PREDICT3(i, j, k);
> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(j, k, l);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT2(j, k);
> +    PIXEL_CUR(s, 0, x + 1, y + 1) = PREDICT3(j, k, l);
> +    PIXEL_CUR(s, 0, x + 2, y + 1) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(k, l, l);
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT2(k, l);
> +    PIXEL_CUR(s, 0, x + 1, y + 2) = PREDICT3(k, l, l);
> +    PIXEL_CUR(s, 0, x + 2, y + 2) = l;
> +    PIXEL_CUR(s, 0, x + 3, y + 2) = l;
> +
> +    PIXEL_CUR(s, 0, x + 0, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 1, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 2, y + 3) = l;
> +    PIXEL_CUR(s, 0, x + 3, y + 3) = l;
> +}
> +
> +static int predict4(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    for (int y2 = 0; y2 < h >> 2; y2++) {
> +        for (int x2 = 0; x2 < w >> 2; x2++) {
> +            uint8_t mode = FFMIN(s->pred4_cache[1 + y2 - 1][1 + x2],
> +                                 s->pred4_cache[1 + y2][1 + x2 - 1]);
> +            if (mode == 9)// if invalid predict dc
> +                mode = 2;
> +
> +            if (!get_bits1(gb)) {
> +                uint8_t val = get_bits(gb, 3);
> +                if (val >= mode)
> +                    val++;
> +                mode = val;
> +            }
> +
> +            s->pred4_cache[1 + y2][1 + x2] = mode;
> +
> +            switch (mode) {
> +            case 0:// vertical
> +                predict_vertical(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> +                break;
> +            case 1:// horizontal
> +                predict_horizontal(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
> +                break;
> +            case 2:// dc
> +                predict4_dc(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 3:// diagonal-down-left
> +                predict4_diagonal_down_left(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 4:// diagonal-down-right
> +                predict4_diagonal_down_right(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 5:// vertical-right
> +                predict4_vertical_right(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 6:// horizontal-down
> +                predict4_horizontal_down(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 7:// vertical-left
> +                predict4_vertical_left(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            case 8:// horizontal-up
> +                predict4_horizontal_up(avctx, x + x2 * 4, y + y2 * 4);
> +                break;
> +            default:
> +                av_log(avctx, AV_LOG_ERROR, "invalid predict4 mode\n");
> +                return AVERROR_INVALIDDATA;
> +            }
> +        }
> +    }
> +    return predict_notile_uv(avctx, x, y, w, h);
> +}
> +
> +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
> +                       const int* level)
> +{
> +    int a, b, c, d, e, f;
> +    int dct[16];
> +    int tmp[16];
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    // dezigzag
> +    for (int i = 0; i < 16; i++)
> +        dct[zigzag4x4_tab[i]] = level[i];
> +
> +    // dequantize
> +    for (int i = 0; i < 2; i++) {
> +        for (int j = 0; j < 4; j++) {
> +            dct[4 * j + i]     *= s->qtab[i][j];
> +            dct[4 * j + i + 2] *= s->qtab[i][j];
> +        }
> +    }
> +
> +    dct[0] += 32;// rounding
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = dct[i * 4 + 0];
> +        b = dct[i * 4 + 1];
> +        c = dct[i * 4 + 2];
> +        d = dct[i * 4 + 3];
> +        a += c;
> +        c = a - c * 2;
> +        e = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        tmp[ 0 + i] = a + f;
> +        tmp[ 4 + i] = c + e;
> +        tmp[ 8 + i] = c - e;
> +        tmp[12 + i] = a - f;
> +    }
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = tmp[i * 4 + 0];
> +        b = tmp[i * 4 + 1];
> +        c = tmp[i * 4 + 2];
> +        d = tmp[i * 4 + 3];
> +        a += c;
> +        c =  a - c * 2;
> +        e  = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        PIXEL_CUR(s, plane, x + 0, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
> +        PIXEL_CUR(s, plane, x + 1, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
> +        PIXEL_CUR(s, plane, x + 2, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
> +        PIXEL_CUR(s, plane, x + 3, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
> +    }
> +}
> +
> +static void decode_residu_cavlc(AVCodecContext *avctx, int x, int y, int plane,
> +                                int nc, uint8_t *out_total_coeff)
> +{
> +    int level[16];
> +    int coeff_token, total_coeff, trailing_ones, i, zeros_left, suffix_length;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +
> +    coeff_token = get_vlc2(gb, coeff_token_vlc[coeff_token_table_index[nc]].table,
> +                           COEFF_TOKEN_VLC_BITS, 2);
> +    trailing_ones = coeff_token & 3;
> +    total_coeff   = coeff_token >> 2;
> +
> +    *out_total_coeff = total_coeff;
> +    if (total_coeff == 0)
> +        return;
> +
> +    av_assert2(total_coeff <= 16);
> +
> +    i = 15;
> +    if (total_coeff != 16) {
> +        int trailing_zeros;
> +        zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff].table,
> +                              TOTAL_ZEROS_VLC_BITS, 1);
> +        trailing_zeros = 16 - (total_coeff + zeros_left);
> +        while(trailing_zeros-- > 0)
> +            level[i--] = 0;
> +    } else
> +        zeros_left = 0;
> +
> +    suffix_length = 0;
> +    while (1) {
> +        int level_suffix, level_code, run_before;
> +        if (trailing_ones > 0) {
> +            trailing_ones--;
> +            level[i--] = get_bits1(gb) ? -1 : 1;
> +        } else {
> +            int level_prefix = 0;
> +            while (!get_bits1(gb))
> +                level_prefix++;
> +
> +            if (level_prefix == 15)
> +                level_suffix = get_bits(gb, 11);
> +            else
> +                level_suffix = suffix_length == 0 ? 0 : get_bits(gb, suffix_length);
> +
> +            level_code = level_suffix + (level_prefix << suffix_length);
> +
> +            if (level_code > cavlc_suffix_len_update_tab[suffix_length])
> +                suffix_length++;
> +
> +            level_code++;
> +            if (get_bits1(gb))
> +                level_code = -level_code;
> +            level[i--] = level_code;
> +        }
> +
> +        if (--total_coeff == 0)
> +            break;
> +
> +        if (zeros_left == 0)
> +            continue;
> +
> +        if(zeros_left < 7)
> +            run_before = get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1);
> +        else
> +            run_before = get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2);
> +        zeros_left -= run_before;
> +        while(run_before-- > 0)
> +            level[i--] = 0;
> +    }
> +
> +    while(zeros_left-- > 0)
> +        level[i--] = 0;
> +
> +    decode_dct(avctx, x, y, plane, level);
> +}
> +
> +static int decode_residu_blocks(AVCodecContext *avctx, int x, int y,
> +                                int w, int h)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    uint8_t *total_coeff_y = &s->total_coeff_y[
> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
> +    uint8_t *total_coeff_uv = &s->total_coeff_uv[
> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
> +    for (int y2 = 0; y2 < h >> 3; y2++) {
> +        for (int x2 = 0; x2 < w >> 3; x2++) {
> +            uint8_t residu_mask;
> +            int code = get_ue_golomb_31(gb);
> +            if (code > 0x1F) {
> +                av_log(avctx, AV_LOG_ERROR, "invalid residu mask code\n");
> +                return AVERROR_INVALIDDATA;
> +            }
> +            if (s->version == VX_VERSION_OLD)
> +                residu_mask = residu_mask_old_tab[code];
> +            else
> +                residu_mask = residu_mask_new_tab[code];
> +
> +            if (residu_mask & 1) {
> +                int nc = (total_coeff_y[-1] +
> +                          total_coeff_y[-s->total_coeff_y_stride] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8, 0, nc,
> +                                    &total_coeff_y[0]);
> +            } else
> +                total_coeff_y[0] = 0;
> +
> +            if (residu_mask & 2) {
> +                int nc = (total_coeff_y[0] +
> +                          total_coeff_y[-s->total_coeff_y_stride + 1] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8 + 4, y + y2 * 8, 0, nc,
> +                                    &total_coeff_y[1]);
> +            } else
> +                total_coeff_y[1] = 0;
> +
> +            if (residu_mask & 4) {
> +                int nc = (total_coeff_y[s->total_coeff_y_stride - 1] +
> +                          total_coeff_y[0] + 1) >> 1;
> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8 + 4, 0, nc,
> +                                    &total_coeff_y[s->total_coeff_y_stride]);
> +            } else
> +                total_coeff_y[s->total_coeff_y_stride] = 0;
> +
> +            if (residu_mask & 8) {
> +                int nc = (total_coeff_y[s->total_coeff_y_stride] +
> +                          total_coeff_y[1] + 1) >> 1;
> +                decode_residu_cavlc(
> +                    avctx, x + x2 * 8 + 4, y + y2 * 8 + 4, 0, nc,
> +                    &total_coeff_y[s->total_coeff_y_stride + 1]);
> +            } else
> +                total_coeff_y[s->total_coeff_y_stride + 1] = 0;
> +
> +            if (residu_mask & 16) {
> +                uint8_t total_coeff_u, total_coeff_v;
> +                int nc = (total_coeff_uv[-1] +
> +                          total_coeff_uv[-s->total_coeff_uv_stride] + 1) >> 1;
> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> +                                    1, nc, &total_coeff_u);
> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
> +                                    2, nc, &total_coeff_v);
> +                total_coeff_uv[0] = (total_coeff_u + total_coeff_v + 1) >> 1;
> +            } else
> +                total_coeff_uv[0] = 0;
> +
> +            total_coeff_y += 2;
> +            total_coeff_uv++;
> +        }
> +        total_coeff_y  += (s->total_coeff_y_stride << 1) - (w >> 2);
> +        total_coeff_uv += s->total_coeff_uv_stride - (w >> 3);
> +    }
> +    return 0;
> +}
> +
> +static int predict_inter(AVCodecContext *avctx, int x, int y, int w, int h,
> +                          const MVec *predVec, int has_delta, int ref_frame)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    MVec vec = *predVec;
> +
> +    if (ref_frame >= s->ref_frame_count) {
> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> +    s->cur_frame->key_frame = 0;
> +
> +    if (has_delta) {
> +        vec.x += (unsigned)get_se_golomb(gb);
> +        vec.y += (unsigned)get_se_golomb(gb);
> +    }
> +
> +    if (vec.x >= INT_MAX || vec.y >= INT_MAX)
> +        return AVERROR_INVALIDDATA;
> +
> +    s->vectors[(1 + (y >> 4)) * s->vectors_stride + 1 + (x >> 4)] = vec;
> +
> +    if (x + vec.x < 0 || x + vec.x + w > avctx->width ||
> +        y + vec.y < 0 || y + vec.y + h > avctx->height) {
> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    // luma
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2)
> +                = PIXEL_REF(s, ref_frame, 0, x + x2 + vec.x, y + y2 + vec.y);
> +
> +    // chroma
> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> +            // u
> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2)
> +                = PIXEL_REF(s, ref_frame, 1, (x >> 1) + x2 + (vec.x >> 1),
> +                            (y >> 1) + y2 + (vec.y >> 1));
> +            // v
> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2)
> +                = PIXEL_REF(s, ref_frame, 2, (x >> 1) + x2 + (vec.x >> 1),
> +                            (y >> 1) + y2 + (vec.y >> 1));
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int predict_inter_dc(AVCodecContext *avctx, int x, int y, int w, int h)
> +{
> +    int dx, dy, dc_y, dc_u, dc_v;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +
> +    if (s->ref_frame_count == 0) {
> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    dx   = get_se_golomb(gb);
> +    dy   = get_se_golomb(gb);
> +
> +    if (x + dx < 0 || x + dx + w > avctx->width ||
> +        y + dy < 0 || y + dy + h > avctx->height) {
> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    dc_y = get_se_golomb(gb);
> +    if (dc_y < -(1<<16) || dc_y >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_y <<= 1;
> +
> +    dc_u = get_se_golomb(gb);
> +    if (dc_u < -(1<<16) || dc_u >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_u <<= 1;
> +
> +    dc_v = get_se_golomb(gb);
> +    if (dc_v < -(1<<16) || dc_v >= (1 << 16)) {
> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    dc_v <<= 1;
> +
> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
> +    s->cur_frame->key_frame = 0;
> +
> +    // luma
> +    for (int y2 = 0; y2 < h; y2++)
> +        for (int x2 = 0; x2 < w; x2++)
> +            PIXEL_CUR(s, 0, x + x2, y + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 0, x + x2 + dx, y + y2 + dy) + dc_y);
> +
> +    // chroma
> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 1, (x >> 1) + x2 + (dx >> 1),
> +                          (y >> 1) + y2 + (dy >> 1)) + dc_u);
> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
> +                PIXEL_REF(s, 0, 2, (x >> 1) + x2 + (dx >> 1),
> +                          (y >> 1) + y2 + (dy >> 1)) + dc_v);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_mb(AVCodecContext *avctx, int x, int y, int w, int h,
> +                     const MVec *predVec)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int ret = 0;
> +
> +    int mode = get_ue_golomb_31(gb);
> +    if (s->version == VX_VERSION_OLD)
> +        mode = old_mb_mode_remap_tab[mode];
> +
> +    switch (mode) {
> +    case 0:// v-split, no residu
> +        if (w == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if (w == 8 && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 1:// no delta, no residu, ref 0
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 2:// h-split, no residu
> +        if (h == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && h == 8)
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 3:// unpredicted delta ref0 + dc offset, no residu
> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 4:// delta, no residu, ref 0
> +    case 5:// delta, no residu, ref 1
> +    case 6:// delta, no residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 4)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 7:// plane, no residu
> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 8:// v-split, residu
> +        if (w == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 9:// no delta, no residu, ref 1
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 1)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 10:// unpredicted delta ref0 + dc offset, residu
> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 11:// predict notile, no residu
> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 12:// no delta, residu, ref 0
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 13:// h-split, residu
> +        if (h == 2) {
> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 14:// no delta, no residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 2)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 15:// predict4, no residu
> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
> +            clear_total_coeff(avctx, x, y, w, h);
> +        break;
> +    case 16:// delta, residu, ref 0
> +    case 17:// delta, residu, ref 1
> +    case 18:// delta, residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 16)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 19:// predict4, residu
> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 20:// no delta, residu, ref 1
> +    case 21:// no delta, residu, ref 2
> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, mode - 20 + 1)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 22:// predict notile, residu
> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    case 23:// plane, residu
> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
> +            return ret;
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    return 0;
> +}
> +
> +static int detect_format(AVCodecContext *avctx)
> +{
> +    // assume the new format, if any incorrect decisions are made for the
> +    // first macroblock of a keyframe (ref, non-dc prediction) then it must be
> +    // the old format
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    int w = 16;
> +    int h = 16;
> +    while (1) {
> +        int mode = get_ue_golomb_31(gb);
> +        if (mode == 0 || mode == 8) { // v-split
> +            if (w == 2) // too many splits
> +                return VX_VERSION_OLD;
> +            w >>= 1;
> +            continue;
> +        } else if (mode == 2 || mode == 13) { // h-split
> +            if (h == 2) // too many splits
> +                return VX_VERSION_OLD;
> +            h >>= 1;
> +            continue;
> +        } else if (mode == 11 || mode == 22) { // predict notile
> +            if (get_ue_golomb_31(gb) != 2) // mode_y != dc
> +                return VX_VERSION_OLD;
> +            if (get_ue_golomb_31(gb) != 0) // mode_uv != dc
> +                return VX_VERSION_OLD;
> +            break; //we should have enough evidence now
> +        } else if (mode == 15 || mode == 19) { // predict4
> +            // initial prediction is always dc
> +            // we don't expect that prediction to be wrong
> +            if (!get_bits1(gb))
> +                return VX_VERSION_OLD;
> +            break; //we should have enough evidence now
> +        } else // inter prediction, plane or any other value
> +            return VX_VERSION_OLD;
> +    }
> +    return VX_VERSION_NEW;
> +}
> +
> +static int actimagine_decode(AVCodecContext *avctx, void *data,
> +                            int *got_frame, AVPacket *pkt)
> +{
> +    MVec *vectors;
> +    int ret;
> +    ActimagineContext *s = avctx->priv_data;
> +    GetBitContext *gb = &s->gb;
> +    AVFrame *frame = s->cur_frame;
> +
> +    // in avi files the frames start with a 32 bit number that seems to
> +    // indicate the total number of bits
> +    int offset = s->avi ? 4 : 0;
> +
> +    av_fast_padded_malloc(&s->bitstream, &s->bitstream_size,
> +                          pkt->size);
> +
> +    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
> +        return ret;
> +
> +    s->bdsp.bswap16_buf((uint16_t *)s->bitstream, (uint16_t *)pkt->data,
> +                        (pkt->size + 1) >> 1);
> +
> +    ret = init_get_bits8(gb, s->bitstream + offset,
> +                         FFALIGN(pkt->size - offset, 2));
> +    if (ret < 0)
> +        return ret;
> +
> +    // determine the bitstream version if this was not done yet
> +    if (s->version == VX_VERSION_INVALID) {
> +        if (s->ref_frame_count != 0) {
> +            av_log(avctx, AV_LOG_ERROR, "can't determine version on p frame\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +        s->version = detect_format(avctx);
> +
> +        // reinit bitreader
> +        ret = init_get_bits8(gb, s->bitstream + offset,
> +                             FFALIGN(pkt->size - offset, 2));
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    vectors = s->vectors + s->vectors_stride + 1;
> +
> +    frame->pict_type = AV_PICTURE_TYPE_I;
> +    frame->key_frame = 1;
> +
> +    if (s->quantizer == -1) {
> +        av_log(avctx, AV_LOG_ERROR, "no quantizer setup\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    for (int y = 0; y < avctx->height; y += 16) {
> +        MVec *vec_cur = vectors;
> +        for (int x = 0; x < avctx->width; x += 16) {
> +            MVec predVec;
> +            vec_cur[0].x = 0;
> +            vec_cur[0].y = 0;
> +            predVec.x = mid_pred(vec_cur[-1].x, vec_cur[-s->vectors_stride].x,
> +                                 vec_cur[-s->vectors_stride + 1].x);
> +            predVec.y = mid_pred(vec_cur[-1].y, vec_cur[-s->vectors_stride].y,
> +                                 vec_cur[-s->vectors_stride + 1].y);
> +            if ((ret = decode_mb(avctx, x, y, 16, 16, &predVec)) < 0)
> +                return ret;
> +            vec_cur++;
> +        }
> +        vectors += s->vectors_stride;
> +    }
> +
> +    if (s->ref_frame_count == 3)
> +        av_frame_unref(s->ref_frames[2]);
> +
> +    s->cur_frame = s->ref_frames[2];
> +    s->ref_frames[2] = s->ref_frames[1];
> +    s->ref_frames[1] = s->ref_frames[0];
> +    s->ref_frames[0] = frame;
> +
> +    if (s->ref_frame_count < 3)
> +        s->ref_frame_count++;
> +
> +    if ((ret = av_frame_ref(data, frame)) < 0)
> +        return ret;
> +    *got_frame = 1;
> +
> +    return 0;
> +}
> +
> +static void actimagine_flush(AVCodecContext *avctx)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    for (int i = 0; i < 3; i++)
> +        av_frame_unref(s->ref_frames[i]);
> +
> +    s->ref_frame_count = 0;
> +}
> +
> +static av_cold int actimagine_close(AVCodecContext *avctx)
> +{
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    av_freep(&s->vectors);
> +    s->vectors_stride = 0;
> +    av_freep(&s->total_coeff_y);
> +    s->total_coeff_y_stride = 0;
> +    av_freep(&s->total_coeff_uv);
> +    s->total_coeff_uv_stride = 0;
> +
> +    av_freep(&s->bitstream);
> +    s->bitstream_size = 0;
> +
> +    for (int i = 0; i < 3; i++)
> +        av_frame_free(&s->ref_frames[i]);
> +    av_frame_free(&s->cur_frame);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_actimagine_decoder = {
> +    .name           = "actimagine",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_ACTIMAGINE,
> +    .priv_data_size = sizeof(ActimagineContext),
> +    .init           = actimagine_init,
> +    .decode         = actimagine_decode,
> +    .flush          = actimagine_flush,
> +    .close          = actimagine_close,
> +    .capabilities   = AV_CODEC_CAP_DR1,
> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2e9a3581de..10809f3492 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -32,6 +32,7 @@
>  extern AVCodec ff_a64multi_encoder;
>  extern AVCodec ff_a64multi5_encoder;
>  extern AVCodec ff_aasc_decoder;
> +extern AVCodec ff_actimagine_decoder;
>  extern AVCodec ff_aic_decoder;
>  extern AVCodec ff_alias_pix_encoder;
>  extern AVCodec ff_alias_pix_decoder;
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 17f8a14044..65d96c21af 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>          .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
>          .props     = AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_ACTIMAGINE,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "actimagine",
> +        .long_name = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
> +        .props     = AV_CODEC_PROP_LOSSY,
> +    },
>
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index ab7bc68ee2..a4b3f3955d 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -307,6 +307,7 @@ enum AVCodecID {
>      AV_CODEC_ID_CRI,
>      AV_CODEC_ID_SIMBIOSIS_IMX,
>      AV_CODEC_ID_SGA_VIDEO,
> +    AV_CODEC_ID_ACTIMAGINE,
>
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
> diff --git a/libavcodec/h264_cavlc.c b/libavcodec/h264_cavlc.c
> index 9f5f692331..642fb2659d 100644
> --- a/libavcodec/h264_cavlc.c
> +++ b/libavcodec/h264_cavlc.c
> @@ -36,6 +36,7 @@
>  #include "golomb.h"
>  #include "mpegutils.h"
>  #include "libavutil/avassert.h"
> +#include "h264_cavlc_data.h"
>
>
>  static const uint8_t golomb_to_inter_cbp_gray[16]={
> @@ -86,104 +87,6 @@ static const uint8_t chroma422_dc_coeff_token_bits[4*9]={
>    7,   5,  4, 4,
>  };
>
> -static const uint8_t coeff_token_len[4][4*17]={
> -{
> -     1, 0, 0, 0,
> -     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
> -    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
> -    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
> -    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
> -},
> -{
> -     2, 0, 0, 0,
> -     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
> -     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
> -    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
> -    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
> -},
> -{
> -     4, 0, 0, 0,
> -     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
> -     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
> -     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
> -    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
> -},
> -{
> -     6, 0, 0, 0,
> -     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> -}
> -};
> -
> -static const uint8_t coeff_token_bits[4][4*17]={
> -{
> -     1, 0, 0, 0,
> -     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
> -     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
> -    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
> -    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
> -},
> -{
> -     3, 0, 0, 0,
> -    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
> -     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
> -    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
> -    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
> -},
> -{
> -    15, 0, 0, 0,
> -    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
> -    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
> -    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
> -    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
> -},
> -{
> -     3, 0, 0, 0,
> -     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
> -    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
> -    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
> -    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
> -}
> -};
> -
> -static const uint8_t total_zeros_len[16][16]= {
> -    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
> -    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
> -    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
> -    {5,3,4,4,3,3,3,4,3,4,5,5,5},
> -    {4,4,4,3,3,3,3,3,4,5,4,5},
> -    {6,5,3,3,3,3,3,3,4,3,6},
> -    {6,5,3,3,3,2,3,4,3,6},
> -    {6,4,5,3,2,2,3,3,6},
> -    {6,6,4,2,2,3,2,5},
> -    {5,5,3,2,2,2,4},
> -    {4,4,3,3,1,3},
> -    {4,4,2,1,3},
> -    {3,3,1,2},
> -    {2,2,1},
> -    {1,1},
> -};
> -
> -static const uint8_t total_zeros_bits[16][16]= {
> -    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
> -    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
> -    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
> -    {3,7,5,4,6,5,4,3,3,2,2,1,0},
> -    {5,4,3,7,6,5,4,3,2,1,1,0},
> -    {1,1,7,6,5,4,3,2,1,1,0},
> -    {1,1,5,4,3,3,2,1,1,0},
> -    {1,1,1,3,3,2,2,1,0},
> -    {1,0,1,3,2,1,1,1},
> -    {1,0,1,3,2,1,1},
> -    {0,1,1,2,1,3},
> -    {0,1,1,1,1},
> -    {0,1,1,1},
> -    {0,1,1},
> -    {0,1},
> -};
> -
>  static const uint8_t chroma_dc_total_zeros_len[3][4]= {
>      { 1, 2, 3, 3,},
>      { 1, 2, 2, 0,},
> @@ -216,26 +119,6 @@ static const uint8_t chroma422_dc_total_zeros_bits[7][8]= {
>      { 0, 1 },
>  };
>
> -static const uint8_t run_len[7][16]={
> -    {1,1},
> -    {1,2,2},
> -    {2,2,2,2},
> -    {2,2,2,3,3},
> -    {2,2,3,3,3,3},
> -    {2,3,3,3,3,3,3},
> -    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
> -};
> -
> -static const uint8_t run_bits[7][16]={
> -    {1,0},
> -    {1,1,0},
> -    {3,2,1,0},
> -    {3,2,1,1,0},
> -    {3,2,3,2,1,0},
> -    {3,0,1,3,2,5,4},
> -    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
> -};
> -
>  static VLC coeff_token_vlc[4];
>  static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
>  static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
> @@ -347,8 +230,8 @@ av_cold void ff_h264_decode_init_vlc(void)
>          coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
>          coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
>          init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4*17,
> -                 &coeff_token_len [i][0], 1, 1,
> -                 &coeff_token_bits[i][0], 1, 1,
> +                 &ff_h264_cavlc_coeff_token_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_coeff_token_bits[i][0], 1, 1,
>                   INIT_VLC_USE_NEW_STATIC);
>          offset += coeff_token_vlc_tables_size[i];
>      }
> @@ -384,8 +267,8 @@ av_cold void ff_h264_decode_init_vlc(void)
>          total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
>          init_vlc(&total_zeros_vlc[i + 1],
>                   TOTAL_ZEROS_VLC_BITS, 16,
> -                 &total_zeros_len [i][0], 1, 1,
> -                 &total_zeros_bits[i][0], 1, 1,
> +                 &ff_h264_cavlc_total_zeros_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_total_zeros_bits[i][0], 1, 1,
>                   INIT_VLC_USE_NEW_STATIC);
>      }
>
> @@ -394,15 +277,15 @@ av_cold void ff_h264_decode_init_vlc(void)
>          run_vlc[i + 1].table_allocated = run_vlc_tables_size;
>          init_vlc(&run_vlc[i + 1],
>                   RUN_VLC_BITS, 7,
> -                 &run_len [i][0], 1, 1,
> -                 &run_bits[i][0], 1, 1,
> +                 &ff_h264_cavlc_run_len [i][0], 1, 1,
> +                 &ff_h264_cavlc_run_bits[i][0], 1, 1,
>                   INIT_VLC_USE_NEW_STATIC);
>      }
>      run7_vlc.table = run7_vlc_table;
>      run7_vlc.table_allocated = run7_vlc_table_size;
>      init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
> -             &run_len [6][0], 1, 1,
> -             &run_bits[6][0], 1, 1,
> +             &ff_h264_cavlc_run_len [6][0], 1, 1,
> +             &ff_h264_cavlc_run_bits[6][0], 1, 1,
>               INIT_VLC_USE_NEW_STATIC);
>
>      init_cavlc_level_tab();
> diff --git a/libavcodec/h264_cavlc_data.c b/libavcodec/h264_cavlc_data.c
> new file mode 100644
> index 0000000000..d18ab1954c
> --- /dev/null
> +++ b/libavcodec/h264_cavlc_data.c
> @@ -0,0 +1,140 @@
> +/*
> + * H.264 cavlc tables
> + * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
> + *
> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "h264_cavlc_data.h"
> +
> +const uint8_t ff_h264_cavlc_coeff_token_len[4][4*17]={
> +{
> +     1, 0, 0, 0,
> +     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
> +    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
> +    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
> +    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
> +},
> +{
> +     2, 0, 0, 0,
> +     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
> +     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
> +    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
> +    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
> +},
> +{
> +     4, 0, 0, 0,
> +     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
> +     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
> +     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
> +    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
> +},
> +{
> +     6, 0, 0, 0,
> +     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
> +}
> +};
> +
> +const uint8_t ff_h264_cavlc_coeff_token_bits[4][4*17]={
> +{
> +     1, 0, 0, 0,
> +     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
> +     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
> +    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
> +    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
> +},
> +{
> +     3, 0, 0, 0,
> +    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
> +     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
> +    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
> +    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
> +},
> +{
> +    15, 0, 0, 0,
> +    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
> +    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
> +    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
> +    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
> +},
> +{
> +     3, 0, 0, 0,
> +     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
> +    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
> +    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
> +    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
> +}
> +};
> +
> +const uint8_t ff_h264_cavlc_total_zeros_len[16][16]= {
> +    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
> +    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
> +    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
> +    {5,3,4,4,3,3,3,4,3,4,5,5,5},
> +    {4,4,4,3,3,3,3,3,4,5,4,5},
> +    {6,5,3,3,3,3,3,3,4,3,6},
> +    {6,5,3,3,3,2,3,4,3,6},
> +    {6,4,5,3,2,2,3,3,6},
> +    {6,6,4,2,2,3,2,5},
> +    {5,5,3,2,2,2,4},
> +    {4,4,3,3,1,3},
> +    {4,4,2,1,3},
> +    {3,3,1,2},
> +    {2,2,1},
> +    {1,1},
> +};
> +
> +const uint8_t ff_h264_cavlc_total_zeros_bits[16][16]= {
> +    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
> +    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
> +    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
> +    {3,7,5,4,6,5,4,3,3,2,2,1,0},
> +    {5,4,3,7,6,5,4,3,2,1,1,0},
> +    {1,1,7,6,5,4,3,2,1,1,0},
> +    {1,1,5,4,3,3,2,1,1,0},
> +    {1,1,1,3,3,2,2,1,0},
> +    {1,0,1,3,2,1,1,1},
> +    {1,0,1,3,2,1,1},
> +    {0,1,1,2,1,3},
> +    {0,1,1,1,1},
> +    {0,1,1,1},
> +    {0,1,1},
> +    {0,1},
> +};
> +
> +const uint8_t ff_h264_cavlc_run_len[7][16]={
> +    {1,1},
> +    {1,2,2},
> +    {2,2,2,2},
> +    {2,2,2,3,3},
> +    {2,2,3,3,3,3},
> +    {2,3,3,3,3,3,3},
> +    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
> +};
> +
> +const uint8_t ff_h264_cavlc_run_bits[7][16]={
> +    {1,0},
> +    {1,1,0},
> +    {3,2,1,0},
> +    {3,2,1,1,0},
> +    {3,2,3,2,1,0},
> +    {3,0,1,3,2,5,4},
> +    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
> +};
> diff --git a/libavcodec/h264_cavlc_data.h b/libavcodec/h264_cavlc_data.h
> new file mode 100644
> index 0000000000..f20453fa26
> --- /dev/null
> +++ b/libavcodec/h264_cavlc_data.h
> @@ -0,0 +1,33 @@
> +/*
> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVCODEC_H264_CAVLC_DATA_H
> +#define AVCODEC_H264_CAVLC_DATA_H
> +
> +#include <stdint.h>
> +
> +extern const uint8_t ff_h264_cavlc_coeff_token_len[4][4*17];
> +extern const uint8_t ff_h264_cavlc_coeff_token_bits[4][4*17];
> +
> +extern const uint8_t ff_h264_cavlc_total_zeros_len[16][16];
> +extern const uint8_t ff_h264_cavlc_total_zeros_bits[16][16];
> +
> +extern const uint8_t ff_h264_cavlc_run_len[7][16];
> +extern const uint8_t ff_h264_cavlc_run_bits[7][16];
> +
> +#endif /* AVCODEC_H264_CAVLC_DATA_H */
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 4299ad4239..f992e1b36e 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -28,7 +28,7 @@
>  #include "libavutil/version.h"
>
>  #define LIBAVCODEC_VERSION_MAJOR  58
> -#define LIBAVCODEC_VERSION_MINOR 131
> +#define LIBAVCODEC_VERSION_MINOR 132
>  #define LIBAVCODEC_VERSION_MICRO 100
>
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index 270ff7c024..5d5cfe16b0 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -496,6 +496,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { AV_CODEC_ID_MVHA,         MKTAG('M', 'V', 'H', 'A') },
>      { AV_CODEC_ID_MV30,         MKTAG('M', 'V', '3', '0') },
>      { AV_CODEC_ID_NOTCHLC,      MKTAG('n', 'l', 'c', '1') },
> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('V', 'X', 'S', '1') },
> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('v', 'x', 's', '1') },
>      { AV_CODEC_ID_NONE,         0 }
>  };
>
> --
> 2.17.1
>
Lynne March 15, 2021, 5:34 p.m. UTC | #4
Mar 15, 2021, 16:09 by fnouwt2@gmail.com:

> Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
> ---
>  Changelog                    |    1 +
>  configure                    |    1 +
>  doc/general_contents.texi    |    2 +
>  libavcodec/Makefile          |    3 +-
>  libavcodec/actimagine.c      | 1523 ++++++++++++++++++++++++++++++++++
>  libavcodec/allcodecs.c       |    1 +
>  libavcodec/codec_desc.c      |    7 +
>  libavcodec/codec_id.h        |    1 +
>  libavcodec/h264_cavlc.c      |  135 +--
>  libavcodec/h264_cavlc_data.c |  140 ++++
>  libavcodec/h264_cavlc_data.h |   33 +
>  libavcodec/version.h         |    2 +-
>  libavformat/riff.c           |    2 +
>  13 files changed, 1723 insertions(+), 128 deletions(-)
>  create mode 100644 libavcodec/actimagine.c
>  create mode 100644 libavcodec/h264_cavlc_data.c
>  create mode 100644 libavcodec/h264_cavlc_data.h
>
> +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
> +                       const int* level)
> +{
> +    int a, b, c, d, e, f;
> +    int dct[16];
> +    int tmp[16];
> +    ActimagineContext *s = avctx->priv_data;
> +
> +    // dezigzag
> +    for (int i = 0; i < 16; i++)
> +        dct[zigzag4x4_tab[i]] = level[i];
> +
> +    // dequantize
> +    for (int i = 0; i < 2; i++) {
> +        for (int j = 0; j < 4; j++) {
> +            dct[4 * j + i]     *= s->qtab[i][j];
> +            dct[4 * j + i + 2] *= s->qtab[i][j];
> +        }
> +    }
> +
> +    dct[0] += 32;// rounding
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = dct[i * 4 + 0];
> +        b = dct[i * 4 + 1];
> +        c = dct[i * 4 + 2];
> +        d = dct[i * 4 + 3];
> +        a += c;
> +        c = a - c * 2;
> +        e = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        tmp[ 0 + i] = a + f;
> +        tmp[ 4 + i] = c + e;
> +        tmp[ 8 + i] = c - e;
> +        tmp[12 + i] = a - f;
> +    }
> +
> +    for (int i = 0; i < 4; i++) {
> +        a = tmp[i * 4 + 0];
> +        b = tmp[i * 4 + 1];
> +        c = tmp[i * 4 + 2];
> +        d = tmp[i * 4 + 3];
> +        a += c;
> +        c =  a - c * 2;
> +        e  = (b >> 1) - d;
> +        f = b + (d >> 1);
> +        PIXEL_CUR(s, plane, x + 0, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
> +        PIXEL_CUR(s, plane, x + 1, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
> +        PIXEL_CUR(s, plane, x + 2, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
> +        PIXEL_CUR(s, plane, x + 3, y + i)
> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
> +    }
> +}
>

This isn't really a DCT but a WHT approximation of one. We have some
DSP functions with assembly written for this. Some have suggested that
it's similar to H264's. The predictors also look similiar to it.
Could you take a look whether you can reuse them (iwht4_1d in
libavcodec/vp9dsp_template.c), or if not, maybe rename the function
to decode_wht?
Florian Nouwt March 15, 2021, 6:37 p.m. UTC | #5
I'll have a look if functions can be reused. If it's the case, the same
could likely be done to the mobiclip decoder too since that one does a lot
of stuff in a similar way.
The "idct" looks very similar to "ff_h264_idct_add" in h264idct_template.c.
Which would make sense since the codec is largely based on techniques used
in h264. In that function the order seems to be reversed though
(column->row, row->row instead of row->column, row->row) and I wonder if
that can have any impact on the output? (Rounding errors or so)
I think there's a good chance that h264 prediction functions can also be
used. I will check if they are 100% equal.

I have a question about the color space btw. This format uses a yuv
approximation based on bit shifting as I described in the init function.
Currently it is not supported by ffmpeg. Would there be a way to add it or
so? The regular yuv coefficients do look alright, but it is not correct.
Everything is a bit less saturated or so.

Op ma 15 mrt. 2021 18:34 schreef Lynne <dev@lynne.ee>:

> Mar 15, 2021, 16:09 by fnouwt2@gmail.com:
>
> > Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
> > ---
> >  Changelog                    |    1 +
> >  configure                    |    1 +
> >  doc/general_contents.texi    |    2 +
> >  libavcodec/Makefile          |    3 +-
> >  libavcodec/actimagine.c      | 1523 ++++++++++++++++++++++++++++++++++
> >  libavcodec/allcodecs.c       |    1 +
> >  libavcodec/codec_desc.c      |    7 +
> >  libavcodec/codec_id.h        |    1 +
> >  libavcodec/h264_cavlc.c      |  135 +--
> >  libavcodec/h264_cavlc_data.c |  140 ++++
> >  libavcodec/h264_cavlc_data.h |   33 +
> >  libavcodec/version.h         |    2 +-
> >  libavformat/riff.c           |    2 +
> >  13 files changed, 1723 insertions(+), 128 deletions(-)
> >  create mode 100644 libavcodec/actimagine.c
> >  create mode 100644 libavcodec/h264_cavlc_data.c
> >  create mode 100644 libavcodec/h264_cavlc_data.h
> >
> > +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
> > +                       const int* level)
> > +{
> > +    int a, b, c, d, e, f;
> > +    int dct[16];
> > +    int tmp[16];
> > +    ActimagineContext *s = avctx->priv_data;
> > +
> > +    // dezigzag
> > +    for (int i = 0; i < 16; i++)
> > +        dct[zigzag4x4_tab[i]] = level[i];
> > +
> > +    // dequantize
> > +    for (int i = 0; i < 2; i++) {
> > +        for (int j = 0; j < 4; j++) {
> > +            dct[4 * j + i]     *= s->qtab[i][j];
> > +            dct[4 * j + i + 2] *= s->qtab[i][j];
> > +        }
> > +    }
> > +
> > +    dct[0] += 32;// rounding
> > +
> > +    for (int i = 0; i < 4; i++) {
> > +        a = dct[i * 4 + 0];
> > +        b = dct[i * 4 + 1];
> > +        c = dct[i * 4 + 2];
> > +        d = dct[i * 4 + 3];
> > +        a += c;
> > +        c = a - c * 2;
> > +        e = (b >> 1) - d;
> > +        f = b + (d >> 1);
> > +        tmp[ 0 + i] = a + f;
> > +        tmp[ 4 + i] = c + e;
> > +        tmp[ 8 + i] = c - e;
> > +        tmp[12 + i] = a - f;
> > +    }
> > +
> > +    for (int i = 0; i < 4; i++) {
> > +        a = tmp[i * 4 + 0];
> > +        b = tmp[i * 4 + 1];
> > +        c = tmp[i * 4 + 2];
> > +        d = tmp[i * 4 + 3];
> > +        a += c;
> > +        c =  a - c * 2;
> > +        e  = (b >> 1) - d;
> > +        f = b + (d >> 1);
> > +        PIXEL_CUR(s, plane, x + 0, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a +
> f) >> 6));
> > +        PIXEL_CUR(s, plane, x + 1, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c +
> e) >> 6));
> > +        PIXEL_CUR(s, plane, x + 2, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c -
> e) >> 6));
> > +        PIXEL_CUR(s, plane, x + 3, y + i)
> > +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a -
> f) >> 6));
> > +    }
> > +}
> >
>
> This isn't really a DCT but a WHT approximation of one. We have some
> DSP functions with assembly written for this. Some have suggested that
> it's similar to H264's. The predictors also look similiar to it.
> Could you take a look whether you can reuse them (iwht4_1d in
> libavcodec/vp9dsp_template.c), or if not, maybe rename the function
> to decode_wht?
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Andreas Rheinhardt March 15, 2021, 6:47 p.m. UTC | #6
Florian Nouwt:
> I applied the few changes you suggested. I moved the shared tables to
> h264_cavlc_data.c. Am I supposed to share the actual vlc tables (such
> as "static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];")
> which are generated by actimagine_init_static respectively
> ff_h264_decode_init_vlc as well?
> 
Yes, please.


> Op ma 15 mrt. 2021 om 16:10 schreef Florian Nouwt <fnouwt2@gmail.com>:
>>
>> Signed-off-by: Florian Nouwt <fnouwt2@gmail.com>
>> ---
>>  Changelog                    |    1 +
>>  configure                    |    1 +
>>  doc/general_contents.texi    |    2 +
>>  libavcodec/Makefile          |    3 +-
>>  libavcodec/actimagine.c      | 1523 ++++++++++++++++++++++++++++++++++
>>  libavcodec/allcodecs.c       |    1 +
>>  libavcodec/codec_desc.c      |    7 +
>>  libavcodec/codec_id.h        |    1 +
>>  libavcodec/h264_cavlc.c      |  135 +--
>>  libavcodec/h264_cavlc_data.c |  140 ++++
>>  libavcodec/h264_cavlc_data.h |   33 +
>>  libavcodec/version.h         |    2 +-
>>  libavformat/riff.c           |    2 +
>>  13 files changed, 1723 insertions(+), 128 deletions(-)
>>  create mode 100644 libavcodec/actimagine.c
>>  create mode 100644 libavcodec/h264_cavlc_data.c
>>  create mode 100644 libavcodec/h264_cavlc_data.h
>>
>> diff --git a/Changelog b/Changelog
>> index a96e350e09..8807f3dcb3 100644
>> --- a/Changelog
>> +++ b/Changelog
>> @@ -83,6 +83,7 @@ version <next>:
>>  - msad video filter
>>  - gophers protocol
>>  - RIST protocol via librist
>> +- Actimagine VX video decoder
>>
>>
>>  version 4.3:
>> diff --git a/configure b/configure
>> index f0ac719d2d..10a07da95f 100755
>> --- a/configure
>> +++ b/configure
>> @@ -2662,6 +2662,7 @@ ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct"
>>  ac3_encoder_select="ac3dsp audiodsp mdct me_cmp"
>>  ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp"
>>  acelp_kelvin_decoder_select="audiodsp"
>> +actimagine_decoder_select="bswapdsp golomb"
>>  adpcm_g722_decoder_select="g722dsp"
>>  adpcm_g722_encoder_select="g722dsp"
>>  aic_decoder_select="golomb idctdsp"
>> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
>> index 33ece6e884..d4261386fc 100644
>> --- a/doc/general_contents.texi
>> +++ b/doc/general_contents.texi
>> @@ -807,6 +807,8 @@ following image formats are supported:
>>  @item 8088flex TMV           @tab     @tab  X
>>  @item A64 multicolor         @tab  X  @tab
>>      @tab Creates video suitable to be played on a commodore 64 (multicolor mode).
>> +@item Actimagine VX Video    @tab     @tab  X
>> +    @tab fourcc: vxs1, VXS1
>>  @item Amazing Studio PAF Video @tab     @tab  X
>>  @item American Laser Games MM  @tab    @tab X
>>      @tab Used in games like Mad Dog McCree.
>> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
>> index 81cc16471b..1b90c66925 100644
>> --- a/libavcodec/Makefile
>> +++ b/libavcodec/Makefile
>> @@ -182,6 +182,7 @@ OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
>>  OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o
>>  OBJS-$(CONFIG_AC3_MF_ENCODER)          += mfenc.o mf_utils.o
>>  OBJS-$(CONFIG_ACELP_KELVIN_DECODER)    += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o
>> +OBJS-$(CONFIG_ACTIMAGINE_DECODER)      += actimagine.o h264_cavlc_data.o
>>  OBJS-$(CONFIG_AGM_DECODER)             += agm.o
>>  OBJS-$(CONFIG_AIC_DECODER)             += aic.o
>>  OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
>> @@ -367,7 +368,7 @@ OBJS-$(CONFIG_H264_DECODER)            += h264dec.o h264_cabac.o h264_cavlc.o \
>>                                            h264_direct.o h264_loopfilter.o  \
>>                                            h264_mb.o h264_picture.o \
>>                                            h264_refs.o h264_sei.o \
>> -                                          h264_slice.o h264data.o
>> +                                          h264_slice.o h264data.o h264_cavlc_data.o
>>  OBJS-$(CONFIG_H264_AMF_ENCODER)        += amfenc_h264.o
>>  OBJS-$(CONFIG_H264_CUVID_DECODER)      += cuviddec.o
>>  OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o
>> diff --git a/libavcodec/actimagine.c b/libavcodec/actimagine.c
>> new file mode 100644
>> index 0000000000..f495426869
>> --- /dev/null
>> +++ b/libavcodec/actimagine.c
>> @@ -0,0 +1,1523 @@
>> +/*
>> + * Actimagine VX Video decoder
>> + * Copyright (c) 2021 Florian Nouwt
>> + *
>> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include <inttypes.h>
>> +
>> +#include "libavutil/avassert.h"
>> +#include "libavutil/thread.h"
>> +
>> +#include "avcodec.h"
>> +#include "bytestream.h"
>> +#include "bswapdsp.h"
>> +#include "get_bits.h"
>> +#include "golomb.h"
>> +#include "internal.h"
>> +#include "h264_cavlc_data.h"
>> +
>> +static const uint8_t predict_dc_shift_tab[] = {1, 2, 3, 0, 4};
>> +
>> +static const uint8_t zigzag4x4_tab[] =
>> +{
>> +    0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09,
>> +    0x06, 0x03, 0x07, 0x0A, 0x0D, 0x0E, 0x0B, 0x0F
>> +};
>> +
>> +static const uint8_t quant4x4_tab[][8] =
>> +{
>> +    { 0x0A, 0x0D, 0x0A, 0x0D, 0x0D, 0x10, 0x0D, 0x10 },
>> +    { 0x0B, 0x0E, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12 },
>> +    { 0x0D, 0x10, 0x0D, 0x10, 0x10, 0x14, 0x10, 0x14 },
>> +    { 0x0E, 0x12, 0x0E, 0x12, 0x12, 0x17, 0x12, 0x17 },
>> +    { 0x10, 0x14, 0x10, 0x14, 0x14, 0x19, 0x14, 0x19 },
>> +    { 0x12, 0x17, 0x12, 0x17, 0x17, 0x1D, 0x17, 0x1D }
>> +};
>> +
>> +static const uint8_t old_mb_mode_remap_tab[] =
>> +{
>> +     1,  2,  0,  4,  7,  3, 11, 15,  9,  5, 14,  6,
>> +    12, 13,  8, 16, 23, 10, 22, 19, 20, 17, 21, 18
>> +};
>> +
>> +static const uint8_t residu_mask_old_tab[] =
>> +{
>> +    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x03, 0x05,
>> +    0x0A, 0x0C, 0x0F, 0x1F, 0x07, 0x0B, 0x0D, 0x0E,
>> +    0x06, 0x09, 0x13, 0x15, 0x1A, 0x1C, 0x11, 0x12,
>> +    0x14, 0x18, 0x17, 0x1B, 0x1D, 0x1E, 0x16, 0x19
>> +};
>> +
>> +static const uint8_t residu_mask_new_tab[] =
>> +{
>> +    0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
>> +    0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
>> +    0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
>> +    0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
>> +};
>> +
>> +static const int cavlc_suffix_len_update_tab[] =
>> +{
>> +    2, 5, 11, 23, 47, 32768
>> +};
>> +
>> +static const uint8_t coeff_token_table_index[17] =
>> +{
>> +    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3
>> +};
>> +
>> +typedef struct MVec {
>> +    int x, y;
>> +} MVec;
>> +
>> +typedef struct ActimagineContext {
>> +    AVFrame *cur_frame;
>> +    AVFrame *ref_frames[3];
>> +    int      ref_frame_count;
>> +
>> +    int version;
>> +    int quantizer;
>> +    int avi;
>> +
>> +    GetBitContext gb;
>> +
>> +    uint8_t *bitstream;
>> +    int bitstream_size;
>> +
>> +    int qtab[2][4];
>> +
>> +    uint8_t pred4_cache[5][5];
>> +
>> +    MVec *vectors;
>> +    int   vectors_stride;
>> +
>> +    uint8_t *total_coeff_y;
>> +    int      total_coeff_y_stride;
>> +
>> +    uint8_t *total_coeff_uv;
>> +    int      total_coeff_uv_stride;
>> +
>> +    BswapDSPContext bdsp;
>> +} ActimagineContext;
>> +
>> +static VLC coeff_token_vlc[4];
>> +static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
>> +static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
>> +
>> +static VLC total_zeros_vlc[15+1];
>> +static VLC_TYPE total_zeros_vlc_tables[15][512][2];
>> +static const int total_zeros_vlc_tables_size = 512;
>> +
>> +static VLC run_vlc[6+1];
>> +static VLC_TYPE run_vlc_tables[6][8][2];
>> +static const int run_vlc_tables_size = 8;
>> +
>> +static VLC run7_vlc;
>> +static VLC_TYPE run7_vlc_table[96][2];
>> +static const int run7_vlc_table_size = 96;
>> +
>> +#define COEFF_TOKEN_VLC_BITS    8
>> +#define TOTAL_ZEROS_VLC_BITS    9
>> +#define RUN_VLC_BITS            3
>> +#define RUN7_VLC_BITS           6
>> +
>> +#define PIXEL_REF(s, ref, plane, x, y)\
>> +    ((s)->ref_frames[(ref)]->data[(plane)]\
>> +        [(y) * (s)->ref_frames[(ref)]->linesize[(plane)] + (x)])
>> +
>> +#define PIXEL_CUR(s, plane, x, y)\
>> +    ((s)->cur_frame->data[(plane)]\
>> +        [(y) * (s)->cur_frame->linesize[(plane)] + (x)])
>> +
>> +#define PREDICT2(a,b)       (((a) + (b) + 1) >> 1)
>> +#define PREDICT3(a,b,c)     (((a) + 2 * (b) + (c) + 2) >> 2)
>> +
>> +#define VX_VERSION_INVALID  -1
>> +#define VX_VERSION_OLD       0
>> +#define VX_VERSION_NEW       1
>> +
>> +static av_cold void actimagine_init_static(void)
>> +{
>> +    // all these tables are equal to the ones used for h264 cavlc
>> +    int offset = 0;
>> +    for (int i = 0; i < 4; i++) {
>> +        coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
>> +        coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
>> +        init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4 * 17,
>> +                 &ff_h264_cavlc_coeff_token_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_coeff_token_bits[i][0], 1, 1,
>> +                 INIT_VLC_USE_NEW_STATIC);
>> +        offset += coeff_token_vlc_tables_size[i];
>> +    }
>> +    /*
>> +     * This is a one time safety check to make sure that
>> +     * the packed static coeff_token_vlc table sizes
>> +     * were initialized correctly.
>> +     */
>> +    av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables));
>> +
>> +    for (int i = 0; i < 15; i++) {
>> +        total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i];
>> +        total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
>> +        init_vlc(&total_zeros_vlc[i + 1],
>> +                 TOTAL_ZEROS_VLC_BITS, 16,
>> +                 &ff_h264_cavlc_total_zeros_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_total_zeros_bits[i][0], 1, 1,
>> +                 INIT_VLC_USE_NEW_STATIC);
>> +    }
>> +
>> +    for (int i = 0; i < 6; i++) {
>> +        run_vlc[i + 1].table = run_vlc_tables[i];
>> +        run_vlc[i + 1].table_allocated = run_vlc_tables_size;
>> +        init_vlc(&run_vlc[i + 1],
>> +                 RUN_VLC_BITS, 7,
>> +                 &ff_h264_cavlc_run_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_run_bits[i][0], 1, 1,
>> +                 INIT_VLC_USE_NEW_STATIC);
>> +    }
>> +
>> +    run7_vlc.table = run7_vlc_table,
>> +    run7_vlc.table_allocated = run7_vlc_table_size;
>> +    init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
>> +             &ff_h264_cavlc_run_len [6][0], 1, 1,
>> +             &ff_h264_cavlc_run_bits[6][0], 1, 1,
>> +             INIT_VLC_USE_NEW_STATIC);
>> +}
>> +
>> +static int setup_qtables(AVCodecContext *avctx, int quantizer)
>> +{
>> +    int qx, qy;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    if (quantizer < 12 || quantizer > 161)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    s->quantizer = quantizer;
>> +
>> +    qx = quantizer % 6;
>> +    qy = quantizer / 6;
>> +
>> +    for (int i = 0; i < 2; i++)
>> +        for (int j = 0; j < 4; j++)
>> +            s->qtab[i][j] = quant4x4_tab[qx][4 * i + j] << qy;
>> +
>> +    return 0;
>> +}
>> +
>> +static av_cold int actimagine_init(AVCodecContext *avctx)
>> +{
>> +    int vectors_size;
>> +    int total_coeff_y_size;
>> +    int total_coeff_uv_size;
>> +    static AVOnce init_static_once = AV_ONCE_INIT;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    if (avctx->width & 15 || avctx->height & 15) {
>> +        av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    ff_bswapdsp_init(&s->bdsp);
>> +
>> +    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
>> +    avctx->color_range = AVCOL_RANGE_JPEG;
>> +    avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
>> +    // the color space of this video format is not supported currently
>> +    // it is a yuv approximation that can be converted back to rgb using bitshifts
>> +    // r = y + (v << 1)
>> +    // g = y - (u >> 1) - v
>> +    // b = y + (u << 1)
>> +    avctx->colorspace = AVCOL_SPC_NB;
>> +
>> +    // predict4 cache
>> +    for (int i = 0; i < 5; i++)
>> +        for (int j = 0; j < 5; j++)
>> +            s->pred4_cache[i][j] = 9;
>> +
>> +    // motion vector cache
>> +    s->vectors_stride = (avctx->width >> 4) + 2;
>> +    vectors_size = ((avctx->height >> 4) + 1) * s->vectors_stride;
>> +    s->vectors = av_calloc(vectors_size, sizeof(MVec));
>> +    if (!s->vectors)
>> +        return AVERROR(ENOMEM);
>> +
>> +    // total dct coefficient cache for luma
>> +    s->total_coeff_y_stride = (avctx->width >> 2) + 1;
>> +    total_coeff_y_size = ((avctx->height >> 2) + 1) * s->total_coeff_y_stride;
>> +    s->total_coeff_y = av_mallocz(total_coeff_y_size);
>> +    if (!s->total_coeff_y)
>> +        return AVERROR(ENOMEM);
>> +
>> +    // total dct coefficient cache for chroma
>> +    s->total_coeff_uv_stride = (avctx->width >> 3) + 1;
>> +    total_coeff_uv_size = ((avctx->height >> 3) + 1) * s->total_coeff_uv_stride;
>> +    s->total_coeff_uv = av_mallocz(total_coeff_uv_size);
>> +    if (!s->total_coeff_uv)
>> +        return AVERROR(ENOMEM);
>> +
>> +    s->ref_frame_count = 0;
>> +    for (int i = 0; i < 3; i++) {
>> +        s->ref_frames[i] = av_frame_alloc();
>> +        if (!s->ref_frames[i])
>> +            return AVERROR(ENOMEM);
>> +    }
>> +    s->cur_frame = av_frame_alloc();
>> +    if (!s->cur_frame)
>> +        return AVERROR(ENOMEM);
>> +
>> +    s->version = VX_VERSION_INVALID;
>> +    s->quantizer = -1;
>> +    s->avi = 0;
>> +
>> +    // when the source is an avi file, the quantizer is stored in the extradata
>> +    if (avctx->extradata_size == 4)
>> +        if (!setup_qtables(avctx, AV_RL32(avctx->extradata)))
>> +            s->avi = 1;
>> +
>> +    ff_thread_once(&init_static_once, actimagine_init_static);
>> +
>> +    return 0;
>> +}
>> +
>> +static void clear_total_coeff(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    // luma
>> +    uint8_t *total_coeff = &s->total_coeff_y[
>> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
>> +
>> +    for (int y2 = 0; y2 < (h >> 2); y2++)
>> +        for (int x2 = 0; x2 < (w >> 2); x2++)
>> +            total_coeff[y2 * s->total_coeff_y_stride + x2] = 0;
>> +
>> +    // chroma
>> +    total_coeff = &s->total_coeff_uv[
>> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
>> +
>> +    for (int y2 = 0; y2 < (h >> 3); y2++)
>> +        for (int x2 = 0; x2 < (w >> 3); x2++)
>> +            total_coeff[y2 * s->total_coeff_uv_stride + x2] = 0;
>> +}
>> +
>> +static void predict_plane_intern(AVCodecContext *avctx, int x, int y,
>> +                                 int w, int h, int plane)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    if (w == 1 && h == 1)
>> +        return;
>> +    if (w == 1 && h != 1) {
>> +        uint8_t top    = PIXEL_CUR(s, plane, x, y - 1);
>> +        uint8_t bottom = PIXEL_CUR(s, plane, x, y + h - 1);
>> +        PIXEL_CUR(s, plane, x, y + (h >> 1) - 1) = (top + bottom) >> 1;
>> +        predict_plane_intern(avctx, x, y, 1, h >> 1, plane);
>> +        predict_plane_intern(avctx, x, y + (h >> 1), 1, h >> 1, plane);
>> +    } else if (w != 1 && h == 1) {
>> +        uint8_t left  = PIXEL_CUR(s, plane, x - 1, y);
>> +        uint8_t right = PIXEL_CUR(s, plane, x + w - 1, y);
>> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y) = (left + right) >> 1;
>> +        predict_plane_intern(avctx, x, y, w >> 1, 1, plane);
>> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, 1, plane);
>> +    } else {
>> +        uint8_t bottom_left  = PIXEL_CUR(s, plane, x - 1, y + h - 1);
>> +        uint8_t top_right    = PIXEL_CUR(s, plane, x + w - 1, y - 1);
>> +        uint8_t bottom_right = PIXEL_CUR(s, plane, x + w - 1, y + h - 1);
>> +        uint8_t bottom_center = (bottom_left + bottom_right) >> 1;
>> +        uint8_t center_right = (top_right + bottom_right) >> 1;
>> +        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + h - 1) = bottom_center;
>> +        PIXEL_CUR(s, plane, x + w - 1, y + (h >> 1) - 1) = center_right;
>> +        if ((w == 4 || w == 16) ^ (h == 4 || h == 16)) {
>> +            uint8_t center_left = PIXEL_CUR(s, plane, x - 1, y + (h >> 1) - 1);
>> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
>> +                = (center_left + center_right) >> 1;
>> +        } else {
>> +            uint8_t top_center = PIXEL_CUR(s, plane, x + (w >> 1) - 1, y - 1);
>> +            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
>> +                = (top_center + bottom_center) >> 1;
>> +        }
>> +        predict_plane_intern(avctx, x, y, w >> 1, h >> 1, plane);
>> +        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, h >> 1, plane);
>> +        predict_plane_intern(avctx, x, y + (h >> 1), w >> 1, h >> 1, plane);
>> +        predict_plane_intern(avctx, x + (w >> 1), y + (h >> 1), w >> 1, h >> 1,
>> +                             plane);
>> +    }
>> +}
>> +
>> +static void predict_plane(AVCodecContext *avctx, int x, int y, int w, int h,
>> +                          int plane, int param)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    uint8_t bottom_left = PIXEL_CUR(s, plane, x - 1, y + h - 1);
>> +    uint8_t top_right   = PIXEL_CUR(s, plane, x + w - 1, y - 1);
>> +    PIXEL_CUR(s, plane, x + w - 1, y + h - 1)
>> +        = ((bottom_left + top_right + 1) >> 1) + param;
>> +    predict_plane_intern(avctx, x, y, w, h, plane);
>> +}
>> +
>> +static int predict_mb_plane(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    // y
>> +    int param = get_se_golomb(gb);
>> +    if (param < -(1 << 16) || param >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    predict_plane(avctx, x, y, w, h, 0, param << 1);
>> +
>> +    // u
>> +    param = get_se_golomb(gb);
>> +    if (param < -(1 << 16) || param >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, param << 1);
>> +
>> +    // v
>> +    param = get_se_golomb(gb);
>> +    if (param < -(1 << 16) || param >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, param << 1);
>> +
>> +    return 0;
>> +}
>> +
>> +static void predict_horizontal(AVCodecContext *avctx, int x, int y,
>> +                               int w, int h, int plane)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    for (int y2 = 0; y2 < h; y2++) {
>> +        uint8_t pixel = PIXEL_CUR(s, plane, x - 1, y + y2);
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            PIXEL_CUR(s, plane, x + x2, y + y2) = pixel;
>> +    }
>> +}
>> +
>> +static void predict_vertical(AVCodecContext *avctx, int x, int y,
>> +                             int w, int h, int plane)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    for (int y2 = 0; y2 < h; y2++)
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            PIXEL_CUR(s, plane, x + x2, y + y2)
>> +                = PIXEL_CUR(s, plane, x + x2, y - 1);
>> +}
>> +
>> +static void predict_dc(AVCodecContext *avctx, int x, int y, int w, int h,
>> +                       int plane)
>> +{
>> +    uint8_t dc;
>> +    ActimagineContext *s = avctx->priv_data;
>> +    if (x != 0 && y != 0) {
>> +        int sum_h, sum_v;
>> +        sum_h = w >> 1;
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            sum_h += PIXEL_CUR(s, plane, x + x2, y - 1);
>> +        sum_h >>= predict_dc_shift_tab[w >> 2];
>> +
>> +        sum_v = h >> 1;
>> +        for (int y2 = 0; y2 < h; y2++)
>> +            sum_v += PIXEL_CUR(s, plane, x - 1, y + y2);
>> +        sum_v >>= predict_dc_shift_tab[h >> 2];
>> +
>> +        dc = (sum_h + sum_v + 1) >> 1;
>> +    } else if (x == 0 && y != 0) {
>> +        int sum = w >> 1;
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            sum += PIXEL_CUR(s, plane, x + x2, y - 1);
>> +        dc = sum >> predict_dc_shift_tab[w >> 2];
>> +    } else if (x != 0 && y == 0) {
>> +        int sum = h >> 1;
>> +        for (int y2 = 0; y2 < h; y2++)
>> +            sum += PIXEL_CUR(s, plane, x - 1, y + y2);
>> +        dc = sum >> predict_dc_shift_tab[h >> 2];
>> +    } else
>> +        dc = 128;
>> +
>> +    for (int y2 = 0; y2 < h; y2++)
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            PIXEL_CUR(s, plane, x + x2, y + y2) = dc;
>> +}
>> +
>> +static int predict_notile_uv(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    int mode_uv = get_ue_golomb_31(gb);
>> +    switch (mode_uv) {
>> +    case 0:// dc
>> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
>> +        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
>> +        break;
>> +    case 1:// horizontal
>> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
>> +        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
>> +        break;
>> +    case 2:// vertical
>> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
>> +        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
>> +        break;
>> +    case 3:// plane
>> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, 0);
>> +        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, 0);
>> +        break;
>> +    default:
>> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile uv mode\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int predict_notile(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    int mode_y = get_ue_golomb_31(gb);
>> +    switch (mode_y) {
>> +    case 0:// vertical
>> +        predict_vertical(avctx, x, y, w, h, 0);
>> +        break;
>> +    case 1:// horizontal
>> +        predict_horizontal(avctx, x, y, w, h, 0);
>> +        break;
>> +    case 2:// dc
>> +        predict_dc(avctx, x, y, w, h, 0);
>> +        break;
>> +    case 3:// plane
>> +        predict_plane(avctx, x, y, w, h, 0, 0);
>> +        break;
>> +    default:
>> +        av_log(avctx, AV_LOG_ERROR, "invalid predict notile y mode\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    return predict_notile_uv(avctx, x, y, w, h);
>> +}
>> +
>> +// slightly different from the common dc prediction method
>> +static void predict4_dc(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t dc;
>> +    ActimagineContext *s = avctx->priv_data;
>> +    if (x == 0 && y == 0)
>> +        dc = 128;
>> +    else {
>> +        int sum   = 0;
>> +        int shift = 1;
>> +
>> +        if (x != 0) {
>> +            shift++;
>> +            for (int y2 = 0; y2 < 4; y2++)
>> +                sum += PIXEL_CUR(s, 0, x - 1, y + y2);
>> +            sum += 2;
>> +        }
>> +
>> +        if (y != 0) {
>> +            shift++;
>> +            for (int x2 = 0; x2 < 4; x2++)
>> +                sum += PIXEL_CUR(s, 0, x + x2, y - 1);
>> +            sum += 2;
>> +        }
>> +
>> +        dc = sum >> shift;
>> +    }
>> +
>> +    for (int y2 = 0; y2 < 4; y2++)
>> +        for (int x2 = 0; x2 < 4; x2++)
>> +            PIXEL_CUR(s, 0, x + x2, y + y2) = dc;
>> +}
>> +
>> +static void predict4_diagonal_down_left(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t val;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
>> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
>> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
>> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
>> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
>> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
>> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
>> +    uint8_t h = PIXEL_CUR(s, 0, x + 7, y - 1);
>> +
>> +    PIXEL_CUR(s, 0, x, y) = PREDICT3(a, b, c);
>> +
>> +    val = PREDICT3(b, c, d);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
>> +
>> +    val = PREDICT3(c, d, e);
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
>> +
>> +    val = PREDICT3(d, e, f);
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
>> +
>> +    val = PREDICT3(e, f, g);
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
>> +
>> +    val = PREDICT3(f, g, h);
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
>> +
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(g, h, h);
>> +}
>> +
>> +static void predict4_diagonal_down_right(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t val;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
>> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
>> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
>> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
>> +
>> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
>> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
>> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
>> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
>> +
>> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(j, k, l);
>> +
>> +    val = PREDICT3(i, j, k);
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
>> +
>> +    val = PREDICT3(m, i, j);
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
>> +
>> +    val = PREDICT3(i, m, a);
>> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
>> +
>> +    val = PREDICT3(m, a, b);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
>> +
>> +    val = PREDICT3(a, b, c);
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
>> +
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(b, c, d);
>> +}
>> +
>> +static void predict4_vertical_right(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t val;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
>> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
>> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
>> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
>> +
>> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
>> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
>> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
>> +
>> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(i, j, k);
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT3(m, i, j);
>> +
>> +    val = PREDICT3(a, m, i);
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
>> +
>> +    val = PREDICT2(a, m);
>> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
>> +
>> +    val = PREDICT3(b, a, m);
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
>> +
>> +    val = PREDICT2(b, a);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
>> +
>> +    val = PREDICT3(c, b, a);
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
>> +
>> +    val = PREDICT2(c, b);
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
>> +
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(d, c, b);
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT2(d, c);
>> +}
>> +
>> +static void predict4_horizontal_down(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t val;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
>> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
>> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
>> +
>> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
>> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
>> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
>> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
>> +
>> +    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT2(k, l);
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = PREDICT3(j, k, l);
>> +
>> +    val = PREDICT2(j, k);
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
>> +
>> +    val = PREDICT3(i, j, k);
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
>> +
>> +    val = PREDICT2(i, j);
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
>> +
>> +    val = PREDICT3(m, i, j);
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
>> +
>> +    val = PREDICT2(i, m);
>> +    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
>> +
>> +    val = PREDICT3(i, m, a);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
>> +
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT3(m, a, b);
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(a, b, c);
>> +}
>> +
>> +static void predict4_vertical_left(AVCodecContext *avctx, int x, int y)
>> +{
>> +    uint8_t val;
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
>> +    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
>> +    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
>> +    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
>> +    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
>> +    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
>> +    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
>> +
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(e, f, g);
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = PREDICT2(e, f);
>> +
>> +    val = PREDICT3(d, e, f);
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
>> +
>> +    val = PREDICT2(d, e);
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
>> +
>> +    val = PREDICT3(c, d, e);
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
>> +
>> +    val = PREDICT2(c, d);
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
>> +
>> +    val = PREDICT3(b, c, d);
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
>> +
>> +    val = PREDICT2(b, c);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT3(a, b, c);
>> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(a, b);
>> +}
>> +
>> +static void predict4_horizontal_up(AVCodecContext *avctx, int x, int y)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
>> +    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
>> +    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
>> +    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(i, j);
>> +    PIXEL_CUR(s, 0, x + 1, y + 0) = PREDICT3(i, j, k);
>> +    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT2(j, k);
>> +    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(j, k, l);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT2(j, k);
>> +    PIXEL_CUR(s, 0, x + 1, y + 1) = PREDICT3(j, k, l);
>> +    PIXEL_CUR(s, 0, x + 2, y + 1) = PREDICT2(k, l);
>> +    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(k, l, l);
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT2(k, l);
>> +    PIXEL_CUR(s, 0, x + 1, y + 2) = PREDICT3(k, l, l);
>> +    PIXEL_CUR(s, 0, x + 2, y + 2) = l;
>> +    PIXEL_CUR(s, 0, x + 3, y + 2) = l;
>> +
>> +    PIXEL_CUR(s, 0, x + 0, y + 3) = l;
>> +    PIXEL_CUR(s, 0, x + 1, y + 3) = l;
>> +    PIXEL_CUR(s, 0, x + 2, y + 3) = l;
>> +    PIXEL_CUR(s, 0, x + 3, y + 3) = l;
>> +}
>> +
>> +static int predict4(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    for (int y2 = 0; y2 < h >> 2; y2++) {
>> +        for (int x2 = 0; x2 < w >> 2; x2++) {
>> +            uint8_t mode = FFMIN(s->pred4_cache[1 + y2 - 1][1 + x2],
>> +                                 s->pred4_cache[1 + y2][1 + x2 - 1]);
>> +            if (mode == 9)// if invalid predict dc
>> +                mode = 2;
>> +
>> +            if (!get_bits1(gb)) {
>> +                uint8_t val = get_bits(gb, 3);
>> +                if (val >= mode)
>> +                    val++;
>> +                mode = val;
>> +            }
>> +
>> +            s->pred4_cache[1 + y2][1 + x2] = mode;
>> +
>> +            switch (mode) {
>> +            case 0:// vertical
>> +                predict_vertical(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
>> +                break;
>> +            case 1:// horizontal
>> +                predict_horizontal(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
>> +                break;
>> +            case 2:// dc
>> +                predict4_dc(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 3:// diagonal-down-left
>> +                predict4_diagonal_down_left(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 4:// diagonal-down-right
>> +                predict4_diagonal_down_right(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 5:// vertical-right
>> +                predict4_vertical_right(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 6:// horizontal-down
>> +                predict4_horizontal_down(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 7:// vertical-left
>> +                predict4_vertical_left(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            case 8:// horizontal-up
>> +                predict4_horizontal_up(avctx, x + x2 * 4, y + y2 * 4);
>> +                break;
>> +            default:
>> +                av_log(avctx, AV_LOG_ERROR, "invalid predict4 mode\n");
>> +                return AVERROR_INVALIDDATA;
>> +            }
>> +        }
>> +    }
>> +    return predict_notile_uv(avctx, x, y, w, h);
>> +}
>> +
>> +static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
>> +                       const int* level)
>> +{
>> +    int a, b, c, d, e, f;
>> +    int dct[16];
>> +    int tmp[16];
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    // dezigzag
>> +    for (int i = 0; i < 16; i++)
>> +        dct[zigzag4x4_tab[i]] = level[i];
>> +
>> +    // dequantize
>> +    for (int i = 0; i < 2; i++) {
>> +        for (int j = 0; j < 4; j++) {
>> +            dct[4 * j + i]     *= s->qtab[i][j];
>> +            dct[4 * j + i + 2] *= s->qtab[i][j];
>> +        }
>> +    }
>> +
>> +    dct[0] += 32;// rounding
>> +
>> +    for (int i = 0; i < 4; i++) {
>> +        a = dct[i * 4 + 0];
>> +        b = dct[i * 4 + 1];
>> +        c = dct[i * 4 + 2];
>> +        d = dct[i * 4 + 3];
>> +        a += c;
>> +        c = a - c * 2;
>> +        e = (b >> 1) - d;
>> +        f = b + (d >> 1);
>> +        tmp[ 0 + i] = a + f;
>> +        tmp[ 4 + i] = c + e;
>> +        tmp[ 8 + i] = c - e;
>> +        tmp[12 + i] = a - f;
>> +    }
>> +
>> +    for (int i = 0; i < 4; i++) {
>> +        a = tmp[i * 4 + 0];
>> +        b = tmp[i * 4 + 1];
>> +        c = tmp[i * 4 + 2];
>> +        d = tmp[i * 4 + 3];
>> +        a += c;
>> +        c =  a - c * 2;
>> +        e  = (b >> 1) - d;
>> +        f = b + (d >> 1);
>> +        PIXEL_CUR(s, plane, x + 0, y + i)
>> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
>> +        PIXEL_CUR(s, plane, x + 1, y + i)
>> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
>> +        PIXEL_CUR(s, plane, x + 2, y + i)
>> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
>> +        PIXEL_CUR(s, plane, x + 3, y + i)
>> +            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
>> +    }
>> +}
>> +
>> +static void decode_residu_cavlc(AVCodecContext *avctx, int x, int y, int plane,
>> +                                int nc, uint8_t *out_total_coeff)
>> +{
>> +    int level[16];
>> +    int coeff_token, total_coeff, trailing_ones, i, zeros_left, suffix_length;
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +
>> +    coeff_token = get_vlc2(gb, coeff_token_vlc[coeff_token_table_index[nc]].table,
>> +                           COEFF_TOKEN_VLC_BITS, 2);
>> +    trailing_ones = coeff_token & 3;
>> +    total_coeff   = coeff_token >> 2;
>> +
>> +    *out_total_coeff = total_coeff;
>> +    if (total_coeff == 0)
>> +        return;
>> +
>> +    av_assert2(total_coeff <= 16);
>> +
>> +    i = 15;
>> +    if (total_coeff != 16) {
>> +        int trailing_zeros;
>> +        zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff].table,
>> +                              TOTAL_ZEROS_VLC_BITS, 1);
>> +        trailing_zeros = 16 - (total_coeff + zeros_left);
>> +        while(trailing_zeros-- > 0)
>> +            level[i--] = 0;
>> +    } else
>> +        zeros_left = 0;
>> +
>> +    suffix_length = 0;
>> +    while (1) {
>> +        int level_suffix, level_code, run_before;
>> +        if (trailing_ones > 0) {
>> +            trailing_ones--;
>> +            level[i--] = get_bits1(gb) ? -1 : 1;
>> +        } else {
>> +            int level_prefix = 0;
>> +            while (!get_bits1(gb))
>> +                level_prefix++;
>> +
>> +            if (level_prefix == 15)
>> +                level_suffix = get_bits(gb, 11);
>> +            else
>> +                level_suffix = suffix_length == 0 ? 0 : get_bits(gb, suffix_length);
>> +
>> +            level_code = level_suffix + (level_prefix << suffix_length);
>> +
>> +            if (level_code > cavlc_suffix_len_update_tab[suffix_length])
>> +                suffix_length++;
>> +
>> +            level_code++;
>> +            if (get_bits1(gb))
>> +                level_code = -level_code;
>> +            level[i--] = level_code;
>> +        }
>> +
>> +        if (--total_coeff == 0)
>> +            break;
>> +
>> +        if (zeros_left == 0)
>> +            continue;
>> +
>> +        if(zeros_left < 7)
>> +            run_before = get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1);
>> +        else
>> +            run_before = get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2);
>> +        zeros_left -= run_before;
>> +        while(run_before-- > 0)
>> +            level[i--] = 0;
>> +    }
>> +
>> +    while(zeros_left-- > 0)
>> +        level[i--] = 0;
>> +
>> +    decode_dct(avctx, x, y, plane, level);
>> +}
>> +
>> +static int decode_residu_blocks(AVCodecContext *avctx, int x, int y,
>> +                                int w, int h)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    uint8_t *total_coeff_y = &s->total_coeff_y[
>> +        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
>> +    uint8_t *total_coeff_uv = &s->total_coeff_uv[
>> +        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
>> +    for (int y2 = 0; y2 < h >> 3; y2++) {
>> +        for (int x2 = 0; x2 < w >> 3; x2++) {
>> +            uint8_t residu_mask;
>> +            int code = get_ue_golomb_31(gb);
>> +            if (code > 0x1F) {
>> +                av_log(avctx, AV_LOG_ERROR, "invalid residu mask code\n");
>> +                return AVERROR_INVALIDDATA;
>> +            }
>> +            if (s->version == VX_VERSION_OLD)
>> +                residu_mask = residu_mask_old_tab[code];
>> +            else
>> +                residu_mask = residu_mask_new_tab[code];
>> +
>> +            if (residu_mask & 1) {
>> +                int nc = (total_coeff_y[-1] +
>> +                          total_coeff_y[-s->total_coeff_y_stride] + 1) >> 1;
>> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8, 0, nc,
>> +                                    &total_coeff_y[0]);
>> +            } else
>> +                total_coeff_y[0] = 0;
>> +
>> +            if (residu_mask & 2) {
>> +                int nc = (total_coeff_y[0] +
>> +                          total_coeff_y[-s->total_coeff_y_stride + 1] + 1) >> 1;
>> +                decode_residu_cavlc(avctx, x + x2 * 8 + 4, y + y2 * 8, 0, nc,
>> +                                    &total_coeff_y[1]);
>> +            } else
>> +                total_coeff_y[1] = 0;
>> +
>> +            if (residu_mask & 4) {
>> +                int nc = (total_coeff_y[s->total_coeff_y_stride - 1] +
>> +                          total_coeff_y[0] + 1) >> 1;
>> +                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8 + 4, 0, nc,
>> +                                    &total_coeff_y[s->total_coeff_y_stride]);
>> +            } else
>> +                total_coeff_y[s->total_coeff_y_stride] = 0;
>> +
>> +            if (residu_mask & 8) {
>> +                int nc = (total_coeff_y[s->total_coeff_y_stride] +
>> +                          total_coeff_y[1] + 1) >> 1;
>> +                decode_residu_cavlc(
>> +                    avctx, x + x2 * 8 + 4, y + y2 * 8 + 4, 0, nc,
>> +                    &total_coeff_y[s->total_coeff_y_stride + 1]);
>> +            } else
>> +                total_coeff_y[s->total_coeff_y_stride + 1] = 0;
>> +
>> +            if (residu_mask & 16) {
>> +                uint8_t total_coeff_u, total_coeff_v;
>> +                int nc = (total_coeff_uv[-1] +
>> +                          total_coeff_uv[-s->total_coeff_uv_stride] + 1) >> 1;
>> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
>> +                                    1, nc, &total_coeff_u);
>> +                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
>> +                                    2, nc, &total_coeff_v);
>> +                total_coeff_uv[0] = (total_coeff_u + total_coeff_v + 1) >> 1;
>> +            } else
>> +                total_coeff_uv[0] = 0;
>> +
>> +            total_coeff_y += 2;
>> +            total_coeff_uv++;
>> +        }
>> +        total_coeff_y  += (s->total_coeff_y_stride << 1) - (w >> 2);
>> +        total_coeff_uv += s->total_coeff_uv_stride - (w >> 3);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int predict_inter(AVCodecContext *avctx, int x, int y, int w, int h,
>> +                          const MVec *predVec, int has_delta, int ref_frame)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    MVec vec = *predVec;
>> +
>> +    if (ref_frame >= s->ref_frame_count) {
>> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
>> +    s->cur_frame->key_frame = 0;
>> +
>> +    if (has_delta) {
>> +        vec.x += (unsigned)get_se_golomb(gb);
>> +        vec.y += (unsigned)get_se_golomb(gb);
>> +    }
>> +
>> +    if (vec.x >= INT_MAX || vec.y >= INT_MAX)
>> +        return AVERROR_INVALIDDATA;
>> +
>> +    s->vectors[(1 + (y >> 4)) * s->vectors_stride + 1 + (x >> 4)] = vec;
>> +
>> +    if (x + vec.x < 0 || x + vec.x + w > avctx->width ||
>> +        y + vec.y < 0 || y + vec.y + h > avctx->height) {
>> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    // luma
>> +    for (int y2 = 0; y2 < h; y2++)
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            PIXEL_CUR(s, 0, x + x2, y + y2)
>> +                = PIXEL_REF(s, ref_frame, 0, x + x2 + vec.x, y + y2 + vec.y);
>> +
>> +    // chroma
>> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
>> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
>> +            // u
>> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2)
>> +                = PIXEL_REF(s, ref_frame, 1, (x >> 1) + x2 + (vec.x >> 1),
>> +                            (y >> 1) + y2 + (vec.y >> 1));
>> +            // v
>> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2)
>> +                = PIXEL_REF(s, ref_frame, 2, (x >> 1) + x2 + (vec.x >> 1),
>> +                            (y >> 1) + y2 + (vec.y >> 1));
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int predict_inter_dc(AVCodecContext *avctx, int x, int y, int w, int h)
>> +{
>> +    int dx, dy, dc_y, dc_u, dc_v;
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +
>> +    if (s->ref_frame_count == 0) {
>> +        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    dx   = get_se_golomb(gb);
>> +    dy   = get_se_golomb(gb);
>> +
>> +    if (x + dx < 0 || x + dx + w > avctx->width ||
>> +        y + dy < 0 || y + dy + h > avctx->height) {
>> +        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    dc_y = get_se_golomb(gb);
>> +    if (dc_y < -(1<<16) || dc_y >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    dc_y <<= 1;
>> +
>> +    dc_u = get_se_golomb(gb);
>> +    if (dc_u < -(1<<16) || dc_u >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    dc_u <<= 1;
>> +
>> +    dc_v = get_se_golomb(gb);
>> +    if (dc_v < -(1<<16) || dc_v >= (1 << 16)) {
>> +        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    dc_v <<= 1;
>> +
>> +    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
>> +    s->cur_frame->key_frame = 0;
>> +
>> +    // luma
>> +    for (int y2 = 0; y2 < h; y2++)
>> +        for (int x2 = 0; x2 < w; x2++)
>> +            PIXEL_CUR(s, 0, x + x2, y + y2) = av_clip_uint8(
>> +                PIXEL_REF(s, 0, 0, x + x2 + dx, y + y2 + dy) + dc_y);
>> +
>> +    // chroma
>> +    for (int y2 = 0; y2 < (h >> 1); y2++) {
>> +        for (int x2 = 0; x2 < (w >> 1); x2++) {
>> +            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
>> +                PIXEL_REF(s, 0, 1, (x >> 1) + x2 + (dx >> 1),
>> +                          (y >> 1) + y2 + (dy >> 1)) + dc_u);
>> +            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
>> +                PIXEL_REF(s, 0, 2, (x >> 1) + x2 + (dx >> 1),
>> +                          (y >> 1) + y2 + (dy >> 1)) + dc_v);
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int decode_mb(AVCodecContext *avctx, int x, int y, int w, int h,
>> +                     const MVec *predVec)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    int ret = 0;
>> +
>> +    int mode = get_ue_golomb_31(gb);
>> +    if (s->version == VX_VERSION_OLD)
>> +        mode = old_mb_mode_remap_tab[mode];
>> +
>> +    switch (mode) {
>> +    case 0:// v-split, no residu
>> +        if (w == 2) {
>> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
>> +            return ret;
>> +        if (w == 8 && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 1:// no delta, no residu, ref 0
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 2:// h-split, no residu
>> +        if (h == 2) {
>> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && h == 8)
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 3:// unpredicted delta ref0 + dc offset, no residu
>> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 4:// delta, no residu, ref 0
>> +    case 5:// delta, no residu, ref 1
>> +    case 6:// delta, no residu, ref 2
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 4)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 7:// plane, no residu
>> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 8:// v-split, residu
>> +        if (w == 2) {
>> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 9:// no delta, no residu, ref 1
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 1)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 10:// unpredicted delta ref0 + dc offset, residu
>> +        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 11:// predict notile, no residu
>> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 12:// no delta, residu, ref 0
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 13:// h-split, residu
>> +        if (h == 2) {
>> +            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 14:// no delta, no residu, ref 2
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 2)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 15:// predict4, no residu
>> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((w == 8 || w == 16) && (h == 8 || h == 16))
>> +            clear_total_coeff(avctx, x, y, w, h);
>> +        break;
>> +    case 16:// delta, residu, ref 0
>> +    case 17:// delta, residu, ref 1
>> +    case 18:// delta, residu, ref 2
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 16)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 19:// predict4, residu
>> +        if ((ret = predict4(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 20:// no delta, residu, ref 1
>> +    case 21:// no delta, residu, ref 2
>> +        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, mode - 20 + 1)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 22:// predict notile, residu
>> +        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    case 23:// plane, residu
>> +        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
>> +            return ret;
>> +        break;
>> +    default:
>> +        av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int detect_format(AVCodecContext *avctx)
>> +{
>> +    // assume the new format, if any incorrect decisions are made for the
>> +    // first macroblock of a keyframe (ref, non-dc prediction) then it must be
>> +    // the old format
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    int w = 16;
>> +    int h = 16;
>> +    while (1) {
>> +        int mode = get_ue_golomb_31(gb);
>> +        if (mode == 0 || mode == 8) { // v-split
>> +            if (w == 2) // too many splits
>> +                return VX_VERSION_OLD;
>> +            w >>= 1;
>> +            continue;
>> +        } else if (mode == 2 || mode == 13) { // h-split
>> +            if (h == 2) // too many splits
>> +                return VX_VERSION_OLD;
>> +            h >>= 1;
>> +            continue;
>> +        } else if (mode == 11 || mode == 22) { // predict notile
>> +            if (get_ue_golomb_31(gb) != 2) // mode_y != dc
>> +                return VX_VERSION_OLD;
>> +            if (get_ue_golomb_31(gb) != 0) // mode_uv != dc
>> +                return VX_VERSION_OLD;
>> +            break; //we should have enough evidence now
>> +        } else if (mode == 15 || mode == 19) { // predict4
>> +            // initial prediction is always dc
>> +            // we don't expect that prediction to be wrong
>> +            if (!get_bits1(gb))
>> +                return VX_VERSION_OLD;
>> +            break; //we should have enough evidence now
>> +        } else // inter prediction, plane or any other value
>> +            return VX_VERSION_OLD;
>> +    }
>> +    return VX_VERSION_NEW;
>> +}
>> +
>> +static int actimagine_decode(AVCodecContext *avctx, void *data,
>> +                            int *got_frame, AVPacket *pkt)
>> +{
>> +    MVec *vectors;
>> +    int ret;
>> +    ActimagineContext *s = avctx->priv_data;
>> +    GetBitContext *gb = &s->gb;
>> +    AVFrame *frame = s->cur_frame;
>> +
>> +    // in avi files the frames start with a 32 bit number that seems to
>> +    // indicate the total number of bits
>> +    int offset = s->avi ? 4 : 0;
>> +
>> +    av_fast_padded_malloc(&s->bitstream, &s->bitstream_size,
>> +                          pkt->size);
>> +
>> +    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
>> +        return ret;
>> +
>> +    s->bdsp.bswap16_buf((uint16_t *)s->bitstream, (uint16_t *)pkt->data,
>> +                        (pkt->size + 1) >> 1);
>> +
>> +    ret = init_get_bits8(gb, s->bitstream + offset,
>> +                         FFALIGN(pkt->size - offset, 2));
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    // determine the bitstream version if this was not done yet
>> +    if (s->version == VX_VERSION_INVALID) {
>> +        if (s->ref_frame_count != 0) {
>> +            av_log(avctx, AV_LOG_ERROR, "can't determine version on p frame\n");
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +        s->version = detect_format(avctx);
>> +
>> +        // reinit bitreader
>> +        ret = init_get_bits8(gb, s->bitstream + offset,
>> +                             FFALIGN(pkt->size - offset, 2));
>> +        if (ret < 0)
>> +            return ret;
>> +    }
>> +
>> +    vectors = s->vectors + s->vectors_stride + 1;
>> +
>> +    frame->pict_type = AV_PICTURE_TYPE_I;
>> +    frame->key_frame = 1;
>> +
>> +    if (s->quantizer == -1) {
>> +        av_log(avctx, AV_LOG_ERROR, "no quantizer setup\n");
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    for (int y = 0; y < avctx->height; y += 16) {
>> +        MVec *vec_cur = vectors;
>> +        for (int x = 0; x < avctx->width; x += 16) {
>> +            MVec predVec;
>> +            vec_cur[0].x = 0;
>> +            vec_cur[0].y = 0;
>> +            predVec.x = mid_pred(vec_cur[-1].x, vec_cur[-s->vectors_stride].x,
>> +                                 vec_cur[-s->vectors_stride + 1].x);
>> +            predVec.y = mid_pred(vec_cur[-1].y, vec_cur[-s->vectors_stride].y,
>> +                                 vec_cur[-s->vectors_stride + 1].y);
>> +            if ((ret = decode_mb(avctx, x, y, 16, 16, &predVec)) < 0)
>> +                return ret;
>> +            vec_cur++;
>> +        }
>> +        vectors += s->vectors_stride;
>> +    }
>> +
>> +    if (s->ref_frame_count == 3)
>> +        av_frame_unref(s->ref_frames[2]);
>> +
>> +    s->cur_frame = s->ref_frames[2];
>> +    s->ref_frames[2] = s->ref_frames[1];
>> +    s->ref_frames[1] = s->ref_frames[0];
>> +    s->ref_frames[0] = frame;
>> +
>> +    if (s->ref_frame_count < 3)
>> +        s->ref_frame_count++;
>> +
>> +    if ((ret = av_frame_ref(data, frame)) < 0)
>> +        return ret;
>> +    *got_frame = 1;
>> +
>> +    return 0;
>> +}
>> +
>> +static void actimagine_flush(AVCodecContext *avctx)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    for (int i = 0; i < 3; i++)
>> +        av_frame_unref(s->ref_frames[i]);
>> +
>> +    s->ref_frame_count = 0;
>> +}
>> +
>> +static av_cold int actimagine_close(AVCodecContext *avctx)
>> +{
>> +    ActimagineContext *s = avctx->priv_data;
>> +
>> +    av_freep(&s->vectors);
>> +    s->vectors_stride = 0;
>> +    av_freep(&s->total_coeff_y);
>> +    s->total_coeff_y_stride = 0;
>> +    av_freep(&s->total_coeff_uv);
>> +    s->total_coeff_uv_stride = 0;
>> +
>> +    av_freep(&s->bitstream);
>> +    s->bitstream_size = 0;
>> +
>> +    for (int i = 0; i < 3; i++)
>> +        av_frame_free(&s->ref_frames[i]);
>> +    av_frame_free(&s->cur_frame);
>> +
>> +    return 0;
>> +}
>> +
>> +AVCodec ff_actimagine_decoder = {
>> +    .name           = "actimagine",
>> +    .long_name      = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
>> +    .type           = AVMEDIA_TYPE_VIDEO,
>> +    .id             = AV_CODEC_ID_ACTIMAGINE,
>> +    .priv_data_size = sizeof(ActimagineContext),
>> +    .init           = actimagine_init,
>> +    .decode         = actimagine_decode,
>> +    .flush          = actimagine_flush,
>> +    .close          = actimagine_close,
>> +    .capabilities   = AV_CODEC_CAP_DR1,
>> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
>> +};
>> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
>> index 2e9a3581de..10809f3492 100644
>> --- a/libavcodec/allcodecs.c
>> +++ b/libavcodec/allcodecs.c
>> @@ -32,6 +32,7 @@
>>  extern AVCodec ff_a64multi_encoder;
>>  extern AVCodec ff_a64multi5_encoder;
>>  extern AVCodec ff_aasc_decoder;
>> +extern AVCodec ff_actimagine_decoder;
>>  extern AVCodec ff_aic_decoder;
>>  extern AVCodec ff_alias_pix_encoder;
>>  extern AVCodec ff_alias_pix_decoder;
>> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
>> index 17f8a14044..65d96c21af 100644
>> --- a/libavcodec/codec_desc.c
>> +++ b/libavcodec/codec_desc.c
>> @@ -1856,6 +1856,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>>          .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
>>          .props     = AV_CODEC_PROP_LOSSY,
>>      },
>> +    {
>> +        .id        = AV_CODEC_ID_ACTIMAGINE,
>> +        .type      = AVMEDIA_TYPE_VIDEO,
>> +        .name      = "actimagine",
>> +        .long_name = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
>> +        .props     = AV_CODEC_PROP_LOSSY,
>> +    },
>>
>>      /* various PCM "codecs" */
>>      {
>> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
>> index ab7bc68ee2..a4b3f3955d 100644
>> --- a/libavcodec/codec_id.h
>> +++ b/libavcodec/codec_id.h
>> @@ -307,6 +307,7 @@ enum AVCodecID {
>>      AV_CODEC_ID_CRI,
>>      AV_CODEC_ID_SIMBIOSIS_IMX,
>>      AV_CODEC_ID_SGA_VIDEO,
>> +    AV_CODEC_ID_ACTIMAGINE,
>>
>>      /* various PCM "codecs" */
>>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
>> diff --git a/libavcodec/h264_cavlc.c b/libavcodec/h264_cavlc.c
>> index 9f5f692331..642fb2659d 100644
>> --- a/libavcodec/h264_cavlc.c
>> +++ b/libavcodec/h264_cavlc.c
>> @@ -36,6 +36,7 @@
>>  #include "golomb.h"
>>  #include "mpegutils.h"
>>  #include "libavutil/avassert.h"
>> +#include "h264_cavlc_data.h"
>>
>>
>>  static const uint8_t golomb_to_inter_cbp_gray[16]={
>> @@ -86,104 +87,6 @@ static const uint8_t chroma422_dc_coeff_token_bits[4*9]={
>>    7,   5,  4, 4,
>>  };
>>
>> -static const uint8_t coeff_token_len[4][4*17]={
>> -{
>> -     1, 0, 0, 0,
>> -     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
>> -    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
>> -    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
>> -    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
>> -},
>> -{
>> -     2, 0, 0, 0,
>> -     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
>> -     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
>> -    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
>> -    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
>> -},
>> -{
>> -     4, 0, 0, 0,
>> -     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
>> -     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
>> -     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
>> -    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
>> -},
>> -{
>> -     6, 0, 0, 0,
>> -     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
>> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> -     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> -}
>> -};
>> -
>> -static const uint8_t coeff_token_bits[4][4*17]={
>> -{
>> -     1, 0, 0, 0,
>> -     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
>> -     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
>> -    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
>> -    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
>> -},
>> -{
>> -     3, 0, 0, 0,
>> -    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
>> -     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
>> -    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
>> -    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
>> -},
>> -{
>> -    15, 0, 0, 0,
>> -    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
>> -    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
>> -    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
>> -    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
>> -},
>> -{
>> -     3, 0, 0, 0,
>> -     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
>> -    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
>> -    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
>> -    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
>> -}
>> -};
>> -
>> -static const uint8_t total_zeros_len[16][16]= {
>> -    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
>> -    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
>> -    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
>> -    {5,3,4,4,3,3,3,4,3,4,5,5,5},
>> -    {4,4,4,3,3,3,3,3,4,5,4,5},
>> -    {6,5,3,3,3,3,3,3,4,3,6},
>> -    {6,5,3,3,3,2,3,4,3,6},
>> -    {6,4,5,3,2,2,3,3,6},
>> -    {6,6,4,2,2,3,2,5},
>> -    {5,5,3,2,2,2,4},
>> -    {4,4,3,3,1,3},
>> -    {4,4,2,1,3},
>> -    {3,3,1,2},
>> -    {2,2,1},
>> -    {1,1},
>> -};
>> -
>> -static const uint8_t total_zeros_bits[16][16]= {
>> -    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
>> -    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
>> -    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
>> -    {3,7,5,4,6,5,4,3,3,2,2,1,0},
>> -    {5,4,3,7,6,5,4,3,2,1,1,0},
>> -    {1,1,7,6,5,4,3,2,1,1,0},
>> -    {1,1,5,4,3,3,2,1,1,0},
>> -    {1,1,1,3,3,2,2,1,0},
>> -    {1,0,1,3,2,1,1,1},
>> -    {1,0,1,3,2,1,1},
>> -    {0,1,1,2,1,3},
>> -    {0,1,1,1,1},
>> -    {0,1,1,1},
>> -    {0,1,1},
>> -    {0,1},
>> -};
>> -
>>  static const uint8_t chroma_dc_total_zeros_len[3][4]= {
>>      { 1, 2, 3, 3,},
>>      { 1, 2, 2, 0,},
>> @@ -216,26 +119,6 @@ static const uint8_t chroma422_dc_total_zeros_bits[7][8]= {
>>      { 0, 1 },
>>  };
>>
>> -static const uint8_t run_len[7][16]={
>> -    {1,1},
>> -    {1,2,2},
>> -    {2,2,2,2},
>> -    {2,2,2,3,3},
>> -    {2,2,3,3,3,3},
>> -    {2,3,3,3,3,3,3},
>> -    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
>> -};
>> -
>> -static const uint8_t run_bits[7][16]={
>> -    {1,0},
>> -    {1,1,0},
>> -    {3,2,1,0},
>> -    {3,2,1,1,0},
>> -    {3,2,3,2,1,0},
>> -    {3,0,1,3,2,5,4},
>> -    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
>> -};
>> -
>>  static VLC coeff_token_vlc[4];
>>  static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
>>  static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
>> @@ -347,8 +230,8 @@ av_cold void ff_h264_decode_init_vlc(void)
>>          coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
>>          coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
>>          init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4*17,
>> -                 &coeff_token_len [i][0], 1, 1,
>> -                 &coeff_token_bits[i][0], 1, 1,
>> +                 &ff_h264_cavlc_coeff_token_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_coeff_token_bits[i][0], 1, 1,
>>                   INIT_VLC_USE_NEW_STATIC);
>>          offset += coeff_token_vlc_tables_size[i];
>>      }
>> @@ -384,8 +267,8 @@ av_cold void ff_h264_decode_init_vlc(void)
>>          total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
>>          init_vlc(&total_zeros_vlc[i + 1],
>>                   TOTAL_ZEROS_VLC_BITS, 16,
>> -                 &total_zeros_len [i][0], 1, 1,
>> -                 &total_zeros_bits[i][0], 1, 1,
>> +                 &ff_h264_cavlc_total_zeros_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_total_zeros_bits[i][0], 1, 1,
>>                   INIT_VLC_USE_NEW_STATIC);
>>      }
>>
>> @@ -394,15 +277,15 @@ av_cold void ff_h264_decode_init_vlc(void)
>>          run_vlc[i + 1].table_allocated = run_vlc_tables_size;
>>          init_vlc(&run_vlc[i + 1],
>>                   RUN_VLC_BITS, 7,
>> -                 &run_len [i][0], 1, 1,
>> -                 &run_bits[i][0], 1, 1,
>> +                 &ff_h264_cavlc_run_len [i][0], 1, 1,
>> +                 &ff_h264_cavlc_run_bits[i][0], 1, 1,
>>                   INIT_VLC_USE_NEW_STATIC);
>>      }
>>      run7_vlc.table = run7_vlc_table;
>>      run7_vlc.table_allocated = run7_vlc_table_size;
>>      init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
>> -             &run_len [6][0], 1, 1,
>> -             &run_bits[6][0], 1, 1,
>> +             &ff_h264_cavlc_run_len [6][0], 1, 1,
>> +             &ff_h264_cavlc_run_bits[6][0], 1, 1,
>>               INIT_VLC_USE_NEW_STATIC);
>>
>>      init_cavlc_level_tab();
>> diff --git a/libavcodec/h264_cavlc_data.c b/libavcodec/h264_cavlc_data.c
>> new file mode 100644
>> index 0000000000..d18ab1954c
>> --- /dev/null
>> +++ b/libavcodec/h264_cavlc_data.c
>> @@ -0,0 +1,140 @@
>> +/*
>> + * H.264 cavlc tables
>> + * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
>> + *
>> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include "h264_cavlc_data.h"
>> +
>> +const uint8_t ff_h264_cavlc_coeff_token_len[4][4*17]={
>> +{
>> +     1, 0, 0, 0,
>> +     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
>> +    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
>> +    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
>> +    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
>> +},
>> +{
>> +     2, 0, 0, 0,
>> +     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
>> +     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
>> +    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
>> +    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
>> +},
>> +{
>> +     4, 0, 0, 0,
>> +     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
>> +     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
>> +     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
>> +    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
>> +},
>> +{
>> +     6, 0, 0, 0,
>> +     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
>> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> +     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
>> +}
>> +};
>> +
>> +const uint8_t ff_h264_cavlc_coeff_token_bits[4][4*17]={
>> +{
>> +     1, 0, 0, 0,
>> +     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
>> +     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
>> +    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
>> +    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
>> +},
>> +{
>> +     3, 0, 0, 0,
>> +    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
>> +     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
>> +    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
>> +    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
>> +},
>> +{
>> +    15, 0, 0, 0,
>> +    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
>> +    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
>> +    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
>> +    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
>> +},
>> +{
>> +     3, 0, 0, 0,
>> +     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
>> +    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
>> +    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
>> +    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
>> +}
>> +};
>> +
>> +const uint8_t ff_h264_cavlc_total_zeros_len[16][16]= {
>> +    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
>> +    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
>> +    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
>> +    {5,3,4,4,3,3,3,4,3,4,5,5,5},
>> +    {4,4,4,3,3,3,3,3,4,5,4,5},
>> +    {6,5,3,3,3,3,3,3,4,3,6},
>> +    {6,5,3,3,3,2,3,4,3,6},
>> +    {6,4,5,3,2,2,3,3,6},
>> +    {6,6,4,2,2,3,2,5},
>> +    {5,5,3,2,2,2,4},
>> +    {4,4,3,3,1,3},
>> +    {4,4,2,1,3},
>> +    {3,3,1,2},
>> +    {2,2,1},
>> +    {1,1},
>> +};
>> +
>> +const uint8_t ff_h264_cavlc_total_zeros_bits[16][16]= {
>> +    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
>> +    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
>> +    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
>> +    {3,7,5,4,6,5,4,3,3,2,2,1,0},
>> +    {5,4,3,7,6,5,4,3,2,1,1,0},
>> +    {1,1,7,6,5,4,3,2,1,1,0},
>> +    {1,1,5,4,3,3,2,1,1,0},
>> +    {1,1,1,3,3,2,2,1,0},
>> +    {1,0,1,3,2,1,1,1},
>> +    {1,0,1,3,2,1,1},
>> +    {0,1,1,2,1,3},
>> +    {0,1,1,1,1},
>> +    {0,1,1,1},
>> +    {0,1,1},
>> +    {0,1},
>> +};
>> +
>> +const uint8_t ff_h264_cavlc_run_len[7][16]={
>> +    {1,1},
>> +    {1,2,2},
>> +    {2,2,2,2},
>> +    {2,2,2,3,3},
>> +    {2,2,3,3,3,3},
>> +    {2,3,3,3,3,3,3},
>> +    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
>> +};
>> +
>> +const uint8_t ff_h264_cavlc_run_bits[7][16]={
>> +    {1,0},
>> +    {1,1,0},
>> +    {3,2,1,0},
>> +    {3,2,1,1,0},
>> +    {3,2,3,2,1,0},
>> +    {3,0,1,3,2,5,4},
>> +    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
>> +};
>> diff --git a/libavcodec/h264_cavlc_data.h b/libavcodec/h264_cavlc_data.h
>> new file mode 100644
>> index 0000000000..f20453fa26
>> --- /dev/null
>> +++ b/libavcodec/h264_cavlc_data.h
>> @@ -0,0 +1,33 @@
>> +/*
>> + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#ifndef AVCODEC_H264_CAVLC_DATA_H
>> +#define AVCODEC_H264_CAVLC_DATA_H
>> +
>> +#include <stdint.h>
>> +
>> +extern const uint8_t ff_h264_cavlc_coeff_token_len[4][4*17];
>> +extern const uint8_t ff_h264_cavlc_coeff_token_bits[4][4*17];
>> +
>> +extern const uint8_t ff_h264_cavlc_total_zeros_len[16][16];
>> +extern const uint8_t ff_h264_cavlc_total_zeros_bits[16][16];
>> +
>> +extern const uint8_t ff_h264_cavlc_run_len[7][16];
>> +extern const uint8_t ff_h264_cavlc_run_bits[7][16];
>> +
>> +#endif /* AVCODEC_H264_CAVLC_DATA_H */
>> diff --git a/libavcodec/version.h b/libavcodec/version.h
>> index 4299ad4239..f992e1b36e 100644
>> --- a/libavcodec/version.h
>> +++ b/libavcodec/version.h
>> @@ -28,7 +28,7 @@
>>  #include "libavutil/version.h"
>>
>>  #define LIBAVCODEC_VERSION_MAJOR  58
>> -#define LIBAVCODEC_VERSION_MINOR 131
>> +#define LIBAVCODEC_VERSION_MINOR 132
>>  #define LIBAVCODEC_VERSION_MICRO 100
>>
>>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
>> diff --git a/libavformat/riff.c b/libavformat/riff.c
>> index 270ff7c024..5d5cfe16b0 100644
>> --- a/libavformat/riff.c
>> +++ b/libavformat/riff.c
>> @@ -496,6 +496,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>>      { AV_CODEC_ID_MVHA,         MKTAG('M', 'V', 'H', 'A') },
>>      { AV_CODEC_ID_MV30,         MKTAG('M', 'V', '3', '0') },
>>      { AV_CODEC_ID_NOTCHLC,      MKTAG('n', 'l', 'c', '1') },
>> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('V', 'X', 'S', '1') },
>> +    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('v', 'x', 's', '1') },
>>      { AV_CODEC_ID_NONE,         0 }
>>  };
>>
>> --
>> 2.17.1
>>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Lynne March 15, 2021, 8:07 p.m. UTC | #7
Mar 15, 2021, 19:37 by fnouwt2@gmail.com:

> I'll have a look if functions can be reused. If it's the case, the same
> could likely be done to the mobiclip decoder too since that one does a lot
> of stuff in a similar way.
> The "idct" looks very similar to "ff_h264_idct_add" in h264idct_template.c.
> Which would make sense since the codec is largely based on techniques used
> in h264. In that function the order seems to be reversed though
> (column->row, row->row instead of row->column, row->row) and I wonder if
> that can have any impact on the output? (Rounding errors or so)
> I think there's a good chance that h264 prediction functions can also be
> used. I will check if they are 100% equal.
>

The order doesn't matter, and rounding errors shouldn't occur as all your
coefficients are left-shifted by 6.


> I have a question about the color space btw. This format uses a yuv
> approximation based on bit shifting as I described in the init function.
> Currently it is not supported by ffmpeg. Would there be a way to add it or
> so? The regular yuv coefficients do look alright, but it is not correct.
> Everything is a bit less saturated or so.
>

Are there any docs or code on what the encoder uses? In general we don't
do colorspace conversions in decoders, but if it's close enough that some
correction can make it fit into bt601 or bt709, I think it would be okay.
Florian Nouwt March 15, 2021, 8:20 p.m. UTC | #8
Good to know the order doesn't matter. In that case I should be able to use
it!

I don't have any docs about the format because it's all proprietary, but I
did some testing with the official codec for windows and the color
conversion I reverse engineered from the decoder used in ds games
r = y + (v << 1)
g = y - (u >> 1) - v
b = y + (u << 1)
results in colors that are equal to whatever I throw into the codec and
frames that are 1:1 equal to the output of the decoder included in the
windows codec.
I suppose that I would just have to allocate an extra frame if I wanted to
do conversion to normal yuv colors. That frame would then be returned and
the original frame would be put in the ref queue.

Op ma 15 mrt. 2021 21:07 schreef Lynne <dev@lynne.ee>:

> Mar 15, 2021, 19:37 by fnouwt2@gmail.com:
>
> > I'll have a look if functions can be reused. If it's the case, the same
> > could likely be done to the mobiclip decoder too since that one does a
> lot
> > of stuff in a similar way.
> > The "idct" looks very similar to "ff_h264_idct_add" in
> h264idct_template.c.
> > Which would make sense since the codec is largely based on techniques
> used
> > in h264. In that function the order seems to be reversed though
> > (column->row, row->row instead of row->column, row->row) and I wonder if
> > that can have any impact on the output? (Rounding errors or so)
> > I think there's a good chance that h264 prediction functions can also be
> > used. I will check if they are 100% equal.
> >
>
> The order doesn't matter, and rounding errors shouldn't occur as all your
> coefficients are left-shifted by 6.
>
>
> > I have a question about the color space btw. This format uses a yuv
> > approximation based on bit shifting as I described in the init function.
> > Currently it is not supported by ffmpeg. Would there be a way to add it
> or
> > so? The regular yuv coefficients do look alright, but it is not correct.
> > Everything is a bit less saturated or so.
> >
>
> Are there any docs or code on what the encoder uses? In general we don't
> do colorspace conversions in decoders, but if it's close enough that some
> correction can make it fit into bt601 or bt709, I think it would be okay.
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Lynne March 15, 2021, 8:42 p.m. UTC | #9
Mar 15, 2021, 21:20 by fnouwt2@gmail.com:

> Good to know the order doesn't matter. In that case I should be able to use
> it!
>
> I don't have any docs about the format because it's all proprietary, but I
> did some testing with the official codec for windows and the color
> conversion I reverse engineered from the decoder used in ds games
> r = y + (v << 1)
> g = y - (u >> 1) - v
> b = y + (u << 1)
> results in colors that are equal to whatever I throw into the codec and
> frames that are 1:1 equal to the output of the decoder included in the
> windows codec.
>

That's looking really close to YCoCg. In fact it probably is just a variant of
YCoCg. You should be able to convert between both with no loss of quality
or rounding errors and just mark the output frame as being YUV with a
YCoCg colorspace. The wikipedia page on YCoCg has RGB<->YCoCg
conversions you can follow.


> I suppose that I would just have to allocate an extra frame if I wanted to
> do conversion to normal yuv colors. That frame would then be returned and
> the original frame would be put in the ref queue.
>

You could implement an inverse step when you use the reference frames,
but for such a codec, where the frame size is going to be comparitively
tiny, I think you can just get away quicker with copying and the converting
the copied frame, while keeping your ref frames as-is.
Florian Nouwt March 15, 2021, 9:02 p.m. UTC | #10
It's actually closer to normal yuv than ycocg. If you look at the
coefficients of normal yuv
r = y + 1.14v
g = y - 0.39u - 0.58v
b = y + 2.03u

ycocg
r = y + co - cg
g = y + cg
b = y - co - cg

the format used in actimagine
r = y + 2v
g = y - 0.5u - v
b = y + 2u

You can see it's more like yuv than ycocg. That's also why currently the
decoded colors still look "alright". I think it wouldn't be a good idea to
use converted ref frames and then convert back as it would likely introduce
errors. But like you are saying, this coded is as far as I know, never used
for large frame sizes, so it shouldn't really be an issue to have an extra
frame and it prevents other problems.

Op ma 15 mrt. 2021 21:42 schreef Lynne <dev@lynne.ee>:

> Mar 15, 2021, 21:20 by fnouwt2@gmail.com:
>
> > Good to know the order doesn't matter. In that case I should be able to
> use
> > it!
> >
> > I don't have any docs about the format because it's all proprietary, but
> I
> > did some testing with the official codec for windows and the color
> > conversion I reverse engineered from the decoder used in ds games
> > r = y + (v << 1)
> > g = y - (u >> 1) - v
> > b = y + (u << 1)
> > results in colors that are equal to whatever I throw into the codec and
> > frames that are 1:1 equal to the output of the decoder included in the
> > windows codec.
> >
>
> That's looking really close to YCoCg. In fact it probably is just a
> variant of
> YCoCg. You should be able to convert between both with no loss of quality
> or rounding errors and just mark the output frame as being YUV with a
> YCoCg colorspace. The wikipedia page on YCoCg has RGB<->YCoCg
> conversions you can follow.
>
>
> > I suppose that I would just have to allocate an extra frame if I wanted
> to
> > do conversion to normal yuv colors. That frame would then be returned and
> > the original frame would be put in the ref queue.
> >
>
> You could implement an inverse step when you use the reference frames,
> but for such a codec, where the frame size is going to be comparitively
> tiny, I think you can just get away quicker with copying and the converting
> the copied frame, while keeping your ref frames as-is.
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Lynne March 15, 2021, 9:20 p.m. UTC | #11
Mar 15, 2021, 22:02 by fnouwt2@gmail.com:

> It's actually closer to normal yuv than ycocg. If you look at the
> coefficients of normal yuv
> r = y + 1.14v
> g = y - 0.39u - 0.58v
> b = y + 2.03u
>
> ycocg
> r = y + co - cg
> g = y + cg
> b = y - co - cg
>
> the format used in actimagine
> r = y + 2v
> g = y - 0.5u - v
> b = y + 2u
>
> You can see it's more like yuv than ycocg. That's also why currently the
> decoded colors still look "alright". I think it wouldn't be a good idea to
> use converted ref frames and then convert back as it would likely introduce
> errors. But like you are saying, this coded is as far as I know, never used
> for large frame sizes, so it shouldn't really be an issue to have an extra
> frame and it prevents other problems.
>

Right, I couldn't remember the YUV formula offhand, and the shifts
made it look like YCoCg.
In that case, you can generate the magic constants to multiply, and
then shift the v and u components. For such small numbers you can
even do it by hand, for example 1.14/2 = 0.57 = (584 * v) >> 10.
You'll want to go as high as you can when you multiply without
overflowing, then shift down to decrease rounding error (it always
rounds to 0).
Florian Nouwt March 15, 2021, 9:54 p.m. UTC | #12
Now that I think about it, it might not be possible to an accurate
straightforward conversion to regular yuv because of the subsampling. The u
and v cannot just linearly be scaled to line up with the required values
for regular yuv. That would mean the output would have to be in rgb format.
It wouldn't really be an issue, but not the most elegant solution. Wouldn't
it be possible to support these alternative coefficients in libswscale or
so? I suppose that would still give issues with applications that have
their own conversion algorithms.

Op ma 15 mrt. 2021 22:20 schreef Lynne <dev@lynne.ee>:

> Mar 15, 2021, 22:02 by fnouwt2@gmail.com:
>
> > It's actually closer to normal yuv than ycocg. If you look at the
> > coefficients of normal yuv
> > r = y + 1.14v
> > g = y - 0.39u - 0.58v
> > b = y + 2.03u
> >
> > ycocg
> > r = y + co - cg
> > g = y + cg
> > b = y - co - cg
> >
> > the format used in actimagine
> > r = y + 2v
> > g = y - 0.5u - v
> > b = y + 2u
> >
> > You can see it's more like yuv than ycocg. That's also why currently the
> > decoded colors still look "alright". I think it wouldn't be a good idea
> to
> > use converted ref frames and then convert back as it would likely
> introduce
> > errors. But like you are saying, this coded is as far as I know, never
> used
> > for large frame sizes, so it shouldn't really be an issue to have an
> extra
> > frame and it prevents other problems.
> >
>
> Right, I couldn't remember the YUV formula offhand, and the shifts
> made it look like YCoCg.
> In that case, you can generate the magic constants to multiply, and
> then shift the v and u components. For such small numbers you can
> even do it by hand, for example 1.14/2 = 0.57 = (584 * v) >> 10.
> You'll want to go as high as you can when you multiply without
> overflowing, then shift down to decrease rounding error (it always
> rounds to 0).
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Lynne March 15, 2021, 10:02 p.m. UTC | #13
Mar 15, 2021, 22:54 by fnouwt2@gmail.com:

> Now that I think about it, it might not be possible to an accurate
> straightforward conversion to regular yuv because of the subsampling. The u
> and v cannot just linearly be scaled to line up with the required values
> for regular yuv. That would mean the output would have to be in rgb format.
> It wouldn't really be an issue, but not the most elegant solution. Wouldn't
> it be possible to support these alternative coefficients in libswscale or
> so? I suppose that would still give issues with applications that have
> their own conversion algorithms.
>

Sure, but none of us really want to dive into libswscale.
I don't see how subsampling would be an issue, as long as the same
subsampling is used, it should be fine, shouldn't it?
Hendrik Leppkes March 15, 2021, 10:34 p.m. UTC | #14
On Mon, Mar 15, 2021 at 10:55 PM Florian Nouwt <fnouwt2@gmail.com> wrote:
>
> Now that I think about it, it might not be possible to an accurate
> straightforward conversion to regular yuv because of the subsampling. The u
> and v cannot just linearly be scaled to line up with the required values
> for regular yuv. That would mean the output would have to be in rgb format.
> It wouldn't really be an issue, but not the most elegant solution. Wouldn't
> it be possible to support these alternative coefficients in libswscale or
> so? I suppose that would still give issues with applications that have
> their own conversion algorithms.
>

I don't think a single game codec warrants creating an entirely new
pixel format for, unless it has a concrete independent definition and
is used outside of this codec as well.
Otherwise, the conversion logic looks rather straight forward and
relatively cheap, and should probably just output RGB (either packed
or planar, whichever is easier). It does remind me a bit of YCgCo, but
with different  formulas unfortunately.

- Hendrik
Florian Nouwt March 16, 2021, 7:22 a.m. UTC | #15
Alright, I'll just convert to rgb then. Both the windows codec and the ds
decoder decode the subsampling by simply duplicating the u/v samples within
a 2x2 block (no interpolation). Should I do that too? Or would
interpolation be preferred? As far as I was able to see the windows codec
takes the average of 2x2 blocks to create the subsampled data.

Op ma 15 mrt. 2021 23:35 schreef Hendrik Leppkes <h.leppkes@gmail.com>:

> On Mon, Mar 15, 2021 at 10:55 PM Florian Nouwt <fnouwt2@gmail.com> wrote:
> >
> > Now that I think about it, it might not be possible to an accurate
> > straightforward conversion to regular yuv because of the subsampling.
> The u
> > and v cannot just linearly be scaled to line up with the required values
> > for regular yuv. That would mean the output would have to be in rgb
> format.
> > It wouldn't really be an issue, but not the most elegant solution.
> Wouldn't
> > it be possible to support these alternative coefficients in libswscale or
> > so? I suppose that would still give issues with applications that have
> > their own conversion algorithms.
> >
>
> I don't think a single game codec warrants creating an entirely new
> pixel format for, unless it has a concrete independent definition and
> is used outside of this codec as well.
> Otherwise, the conversion logic looks rather straight forward and
> relatively cheap, and should probably just output RGB (either packed
> or planar, whichever is easier). It does remind me a bit of YCgCo, but
> with different  formulas unfortunately.
>
> - Hendrik
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol March 16, 2021, 7:41 a.m. UTC | #16
On Tue, Mar 16, 2021 at 8:22 AM Florian Nouwt <fnouwt2@gmail.com> wrote:

> Alright, I'll just convert to rgb then. Both the windows codec and the ds
> decoder decode the subsampling by simply duplicating the u/v samples within
> a 2x2 block (no interpolation). Should I do that too? Or would
> interpolation be preferred? As far as I was able to see the windows codec
> takes the average of 2x2 blocks to create the subsampled data.
>

That is unacceptable non-solution.


>
> Op ma 15 mrt. 2021 23:35 schreef Hendrik Leppkes <h.leppkes@gmail.com>:
>
> > On Mon, Mar 15, 2021 at 10:55 PM Florian Nouwt <fnouwt2@gmail.com>
> wrote:
> > >
> > > Now that I think about it, it might not be possible to an accurate
> > > straightforward conversion to regular yuv because of the subsampling.
> > The u
> > > and v cannot just linearly be scaled to line up with the required
> values
> > > for regular yuv. That would mean the output would have to be in rgb
> > format.
> > > It wouldn't really be an issue, but not the most elegant solution.
> > Wouldn't
> > > it be possible to support these alternative coefficients in libswscale
> or
> > > so? I suppose that would still give issues with applications that have
> > > their own conversion algorithms.
> > >
> >
> > I don't think a single game codec warrants creating an entirely new
> > pixel format for, unless it has a concrete independent definition and
> > is used outside of this codec as well.
> > Otherwise, the conversion logic looks rather straight forward and
> > relatively cheap, and should probably just output RGB (either packed
> > or planar, whichever is easier). It does remind me a bit of YCgCo, but
> > with different  formulas unfortunately.
> >
> > - Hendrik
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol March 16, 2021, 7:55 a.m. UTC | #17
On Mon, Mar 15, 2021 at 10:02 PM Florian Nouwt <fnouwt2@gmail.com> wrote:

> It's actually closer to normal yuv than ycocg. If you look at the
> coefficients of normal yuv
> r = y + 1.14v
> g = y - 0.39u - 0.58v
> b = y + 2.03u
>
> ycocg
> r = y + co - cg
> g = y + cg
> b = y - co - cg
>
> the format used in actimagine
> r = y + 2v
> g = y - 0.5u - v
> b = y + 2u
>


Something is very wrong with those equations.
They do not have sense.

Next time make sure to use real player like mpv, and set frame color_space
to ycgco or ycocg.


> You can see it's more like yuv than ycocg. That's also why currently the
> decoded colors still look "alright". I think it wouldn't be a good idea to
> use converted ref frames and then convert back as it would likely introduce
> errors. But like you are saying, this coded is as far as I know, never used
> for large frame sizes, so it shouldn't really be an issue to have an extra
> frame and it prevents other problems.
>
> Op ma 15 mrt. 2021 21:42 schreef Lynne <dev@lynne.ee>:
>
> > Mar 15, 2021, 21:20 by fnouwt2@gmail.com:
> >
> > > Good to know the order doesn't matter. In that case I should be able to
> > use
> > > it!
> > >
> > > I don't have any docs about the format because it's all proprietary,
> but
> > I
> > > did some testing with the official codec for windows and the color
> > > conversion I reverse engineered from the decoder used in ds games
> > > r = y + (v << 1)
> > > g = y - (u >> 1) - v
> > > b = y + (u << 1)
> > > results in colors that are equal to whatever I throw into the codec and
> > > frames that are 1:1 equal to the output of the decoder included in the
> > > windows codec.
> > >
> >
> > That's looking really close to YCoCg. In fact it probably is just a
> > variant of
> > YCoCg. You should be able to convert between both with no loss of quality
> > or rounding errors and just mark the output frame as being YUV with a
> > YCoCg colorspace. The wikipedia page on YCoCg has RGB<->YCoCg
> > conversions you can follow.
> >
> >
> > > I suppose that I would just have to allocate an extra frame if I wanted
> > to
> > > do conversion to normal yuv colors. That frame would then be returned
> and
> > > the original frame would be put in the ref queue.
> > >
> >
> > You could implement an inverse step when you use the reference frames,
> > but for such a codec, where the frame size is going to be comparitively
> > tiny, I think you can just get away quicker with copying and the
> converting
> > the copied frame, while keeping your ref frames as-is.
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Florian Nouwt March 16, 2021, 7:59 a.m. UTC | #18
Why would those equations not make sense?? I literally looked the top two
up on wikipedia. And the bottom one was reverse engineered from the ds
decoder and I verified it to be accurate for this codec.
I too think that converting to rgb is not the most elegant solution, but it
would be better than just wrong colors I think.

Op di 16 mrt. 2021 08:55 schreef Paul B Mahol <onemda@gmail.com>:

> On Mon, Mar 15, 2021 at 10:02 PM Florian Nouwt <fnouwt2@gmail.com> wrote:
>
> > It's actually closer to normal yuv than ycocg. If you look at the
> > coefficients of normal yuv
> > r = y + 1.14v
> > g = y - 0.39u - 0.58v
> > b = y + 2.03u
> >
> > ycocg
> > r = y + co - cg
> > g = y + cg
> > b = y - co - cg
> >
> > the format used in actimagine
> > r = y + 2v
> > g = y - 0.5u - v
> > b = y + 2u
> >
>
>
> Something is very wrong with those equations.
> They do not have sense.
>
> Next time make sure to use real player like mpv, and set frame color_space
> to ycgco or ycocg.
>
>
> > You can see it's more like yuv than ycocg. That's also why currently the
> > decoded colors still look "alright". I think it wouldn't be a good idea
> to
> > use converted ref frames and then convert back as it would likely
> introduce
> > errors. But like you are saying, this coded is as far as I know, never
> used
> > for large frame sizes, so it shouldn't really be an issue to have an
> extra
> > frame and it prevents other problems.
> >
> > Op ma 15 mrt. 2021 21:42 schreef Lynne <dev@lynne.ee>:
> >
> > > Mar 15, 2021, 21:20 by fnouwt2@gmail.com:
> > >
> > > > Good to know the order doesn't matter. In that case I should be able
> to
> > > use
> > > > it!
> > > >
> > > > I don't have any docs about the format because it's all proprietary,
> > but
> > > I
> > > > did some testing with the official codec for windows and the color
> > > > conversion I reverse engineered from the decoder used in ds games
> > > > r = y + (v << 1)
> > > > g = y - (u >> 1) - v
> > > > b = y + (u << 1)
> > > > results in colors that are equal to whatever I throw into the codec
> and
> > > > frames that are 1:1 equal to the output of the decoder included in
> the
> > > > windows codec.
> > > >
> > >
> > > That's looking really close to YCoCg. In fact it probably is just a
> > > variant of
> > > YCoCg. You should be able to convert between both with no loss of
> quality
> > > or rounding errors and just mark the output frame as being YUV with a
> > > YCoCg colorspace. The wikipedia page on YCoCg has RGB<->YCoCg
> > > conversions you can follow.
> > >
> > >
> > > > I suppose that I would just have to allocate an extra frame if I
> wanted
> > > to
> > > > do conversion to normal yuv colors. That frame would then be returned
> > and
> > > > the original frame would be put in the ref queue.
> > > >
> > >
> > > You could implement an inverse step when you use the reference frames,
> > > but for such a codec, where the frame size is going to be comparitively
> > > tiny, I think you can just get away quicker with copying and the
> > converting
> > > the copied frame, while keeping your ref frames as-is.
> > > _______________________________________________
> > > ffmpeg-devel mailing list
> > > ffmpeg-devel@ffmpeg.org
> > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >
> > > To unsubscribe, visit link above, or email
> > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Florian Nouwt March 16, 2021, 12:23 p.m. UTC | #19
I managed to utilize the h264 idct and h264 prediction code now. The
only prediction code I kept is for the 4 modes that work on any
possible block size (horizontal, vertical, dc and plane). I also moved
the actual vlc tables and their initialization into h264_cavlc_data.c
and checked that make fate was successful. I will await the discussion
on the color format before submitting the new patch. I have attached 3
images that show an original video frame I encoded to the vx format,
the version decoded with the windows codec and the version decoded by
ffmpeg utilizing the regular yuv formulas. Before I implemented this
format in ffmpeg I had written a decoder in C# which utilized the
color conversion formulas I discussed before, and the output of that
program was exactly equal to the frame as decoded by the windows
codec. So I think that should be enough evidence that those formulas
are right.
Florian Nouwt March 17, 2021, 1:21 p.m. UTC | #20
I found the origin of this color system. It was originally used with
the first gba video paks (which used a way different codec).
It is being described in this patent by Majesco (the 7th image, FIG.
4A and FIG. 4B): https://patents.google.com/patent/US7253819B1/en
Later gba video paks seem to use the actimage vx format, so I guess
there must have been a collaboration of some sort there.
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index a96e350e09..8807f3dcb3 100644
--- a/Changelog
+++ b/Changelog
@@ -83,6 +83,7 @@  version <next>:
 - msad video filter
 - gophers protocol
 - RIST protocol via librist
+- Actimagine VX video decoder
 
 
 version 4.3:
diff --git a/configure b/configure
index f0ac719d2d..10a07da95f 100755
--- a/configure
+++ b/configure
@@ -2662,6 +2662,7 @@  ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct"
 ac3_encoder_select="ac3dsp audiodsp mdct me_cmp"
 ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp"
 acelp_kelvin_decoder_select="audiodsp"
+actimagine_decoder_select="bswapdsp golomb"
 adpcm_g722_decoder_select="g722dsp"
 adpcm_g722_encoder_select="g722dsp"
 aic_decoder_select="golomb idctdsp"
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 33ece6e884..d4261386fc 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -807,6 +807,8 @@  following image formats are supported:
 @item 8088flex TMV           @tab     @tab  X
 @item A64 multicolor         @tab  X  @tab
     @tab Creates video suitable to be played on a commodore 64 (multicolor mode).
+@item Actimagine VX Video    @tab     @tab  X
+    @tab fourcc: vxs1, VXS1
 @item Amazing Studio PAF Video @tab     @tab  X
 @item American Laser Games MM  @tab    @tab X
     @tab Used in games like Mad Dog McCree.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 81cc16471b..39b3bc968d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -182,6 +182,7 @@  OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
 OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o
 OBJS-$(CONFIG_AC3_MF_ENCODER)          += mfenc.o mf_utils.o
 OBJS-$(CONFIG_ACELP_KELVIN_DECODER)    += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o
+OBJS-$(CONFIG_ACTIMAGINE_DECODER)      += actimagine.o
 OBJS-$(CONFIG_AGM_DECODER)             += agm.o
 OBJS-$(CONFIG_AIC_DECODER)             += aic.o
 OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
diff --git a/libavcodec/actimagine.c b/libavcodec/actimagine.c
new file mode 100644
index 0000000000..6bb5126b05
--- /dev/null
+++ b/libavcodec/actimagine.c
@@ -0,0 +1,1644 @@ 
+/*
+ * Actimagine VX Video decoder
+ * Copyright (c) 2021 Florian Nouwt
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/thread.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "bswapdsp.h"
+#include "get_bits.h"
+#include "golomb.h"
+#include "internal.h"
+
+static const uint8_t predict_dc_shift_tab[] = {1, 2, 3, 0, 4};
+
+static const uint8_t zigzag4x4_tab[] =
+{
+    0x00, 0x04, 0x01, 0x02, 0x05, 0x08, 0x0C, 0x09,
+    0x06, 0x03, 0x07, 0x0A, 0x0D, 0x0E, 0x0B, 0x0F
+};
+
+static const uint8_t quant4x4_tab[][8] =
+{
+    { 0x0A, 0x0D, 0x0A, 0x0D, 0x0D, 0x10, 0x0D, 0x10 },
+    { 0x0B, 0x0E, 0x0B, 0x0E, 0x0E, 0x12, 0x0E, 0x12 },
+    { 0x0D, 0x10, 0x0D, 0x10, 0x10, 0x14, 0x10, 0x14 },
+    { 0x0E, 0x12, 0x0E, 0x12, 0x12, 0x17, 0x12, 0x17 },
+    { 0x10, 0x14, 0x10, 0x14, 0x14, 0x19, 0x14, 0x19 },
+    { 0x12, 0x17, 0x12, 0x17, 0x17, 0x1D, 0x17, 0x1D }
+};
+
+static const uint8_t old_mb_mode_remap_tab[] =
+{
+     1,  2,  0,  4,  7,  3, 11, 15,  9,  5, 14,  6,
+    12, 13,  8, 16, 23, 10, 22, 19, 20, 17, 21, 18
+};
+
+static const uint8_t residu_mask_old_tab[] =
+{
+    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x03, 0x05,
+    0x0A, 0x0C, 0x0F, 0x1F, 0x07, 0x0B, 0x0D, 0x0E,
+    0x06, 0x09, 0x13, 0x15, 0x1A, 0x1C, 0x11, 0x12,
+    0x14, 0x18, 0x17, 0x1B, 0x1D, 0x1E, 0x16, 0x19
+};
+
+static const uint8_t residu_mask_new_tab[] =
+{
+    0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
+    0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
+    0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
+    0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
+};
+
+static const int cavlc_suffix_len_update_tab[] =
+{
+    2, 5, 11, 23, 47, 32768
+};
+
+// same tables as h264 cavlc
+static const uint8_t coeff_token_table_index[17] =
+{
+    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3
+};
+
+static const uint8_t coeff_token_len[4][4 * 17] = {
+{
+     1, 0, 0, 0,
+     6, 2, 0, 0,     8, 6, 3, 0,     9, 8, 7, 5,    10, 9, 8, 6,
+    11,10, 9, 7,    13,11,10, 8,    13,13,11, 9,    13,13,13,10,
+    14,14,13,11,    14,14,14,13,    15,15,14,14,    15,15,15,14,
+    16,15,15,15,    16,16,16,15,    16,16,16,16,    16,16,16,16,
+},
+{
+     2, 0, 0, 0,
+     6, 2, 0, 0,     6, 5, 3, 0,     7, 6, 6, 4,     8, 6, 6, 4,
+     8, 7, 7, 5,     9, 8, 8, 6,    11, 9, 9, 6,    11,11,11, 7,
+    12,11,11, 9,    12,12,12,11,    12,12,12,11,    13,13,13,12,
+    13,13,13,13,    13,14,13,13,    14,14,14,13,    14,14,14,14,
+},
+{
+     4, 0, 0, 0,
+     6, 4, 0, 0,     6, 5, 4, 0,     6, 5, 5, 4,     7, 5, 5, 4,
+     7, 5, 5, 4,     7, 6, 6, 4,     7, 6, 6, 4,     8, 7, 7, 5,
+     8, 8, 7, 6,     9, 8, 8, 7,     9, 9, 8, 8,     9, 9, 9, 8,
+    10, 9, 9, 9,    10,10,10,10,    10,10,10,10,    10,10,10,10,
+},
+{
+     6, 0, 0, 0,
+     6, 6, 0, 0,     6, 6, 6, 0,     6, 6, 6, 6,     6, 6, 6, 6,
+     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
+     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
+     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,     6, 6, 6, 6,
+}
+};
+
+static const uint8_t coeff_token_bits[4][4 * 17] = {
+{
+     1, 0, 0, 0,
+     5, 1, 0, 0,     7, 4, 1, 0,     7, 6, 5, 3,     7, 6, 5, 3,
+     7, 6, 5, 4,    15, 6, 5, 4,    11,14, 5, 4,     8,10,13, 4,
+    15,14, 9, 4,    11,10,13,12,    15,14, 9,12,    11,10,13, 8,
+    15, 1, 9,12,    11,14,13, 8,     7,10, 9,12,     4, 6, 5, 8,
+},
+{
+     3, 0, 0, 0,
+    11, 2, 0, 0,     7, 7, 3, 0,     7,10, 9, 5,     7, 6, 5, 4,
+     4, 6, 5, 6,     7, 6, 5, 8,    15, 6, 5, 4,    11,14,13, 4,
+    15,10, 9, 4,    11,14,13,12,     8,10, 9, 8,    15,14,13,12,
+    11,10, 9,12,     7,11, 6, 8,     9, 8,10, 1,     7, 6, 5, 4,
+},
+{
+    15, 0, 0, 0,
+    15,14, 0, 0,    11,15,13, 0,     8,12,14,12,    15,10,11,11,
+    11, 8, 9,10,     9,14,13, 9,     8,10, 9, 8,    15,14,13,13,
+    11,14,10,12,    15,10,13,12,    11,14, 9,12,     8,10,13, 8,
+    13, 7, 9,12,     9,12,11,10,     5, 8, 7, 6,     1, 4, 3, 2,
+},
+{
+     3, 0, 0, 0,
+     0, 1, 0, 0,     4, 5, 6, 0,     8, 9,10,11,    12,13,14,15,
+    16,17,18,19,    20,21,22,23,    24,25,26,27,    28,29,30,31,
+    32,33,34,35,    36,37,38,39,    40,41,42,43,    44,45,46,47,
+    48,49,50,51,    52,53,54,55,    56,57,58,59,    60,61,62,63,
+}
+};
+
+static const uint8_t total_zeros_len[16][16] = {
+    {1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
+    {3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
+    {4,3,3,3,4,4,3,3,4,5,5,6,5,6},
+    {5,3,4,4,3,3,3,4,3,4,5,5,5},
+    {4,4,4,3,3,3,3,3,4,5,4,5},
+    {6,5,3,3,3,3,3,3,4,3,6},
+    {6,5,3,3,3,2,3,4,3,6},
+    {6,4,5,3,2,2,3,3,6},
+    {6,6,4,2,2,3,2,5},
+    {5,5,3,2,2,2,4},
+    {4,4,3,3,1,3},
+    {4,4,2,1,3},
+    {3,3,1,2},
+    {2,2,1},
+    {1,1},
+};
+
+static const uint8_t total_zeros_bits[16][16] = {
+    {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
+    {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
+    {5,7,6,5,4,3,4,3,2,3,2,1,1,0},
+    {3,7,5,4,6,5,4,3,3,2,2,1,0},
+    {5,4,3,7,6,5,4,3,2,1,1,0},
+    {1,1,7,6,5,4,3,2,1,1,0},
+    {1,1,5,4,3,3,2,1,1,0},
+    {1,1,1,3,3,2,2,1,0},
+    {1,0,1,3,2,1,1,1},
+    {1,0,1,3,2,1,1},
+    {0,1,1,2,1,3},
+    {0,1,1,1,1},
+    {0,1,1,1},
+    {0,1,1},
+    {0,1},
+};
+
+static const uint8_t run_len[7][16] = {
+    {1,1},
+    {1,2,2},
+    {2,2,2,2},
+    {2,2,2,3,3},
+    {2,2,3,3,3,3},
+    {2,3,3,3,3,3,3},
+    {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
+};
+
+static const uint8_t run_bits[7][16] = {
+    {1,0},
+    {1,1,0},
+    {3,2,1,0},
+    {3,2,1,1,0},
+    {3,2,3,2,1,0},
+    {3,0,1,3,2,5,4},
+    {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
+};
+
+typedef struct MVec {
+    int x, y;
+} MVec;
+
+typedef struct ActimagineContext {
+    AVFrame *cur_frame;
+    AVFrame *ref_frames[3];
+    int      ref_frame_count;
+
+    int version;
+    int quantizer;
+    int avi;
+
+    GetBitContext gb;
+
+    uint8_t *bitstream;
+    int bitstream_size;
+
+    int qtab[2][4];
+
+    uint8_t pred4_cache[5][5];
+
+    MVec *vectors;
+    int   vectors_stride;
+
+    uint8_t *total_coeff_y;
+    int      total_coeff_y_stride;
+
+    uint8_t *total_coeff_uv;
+    int      total_coeff_uv_stride;
+
+    BswapDSPContext bdsp;
+} ActimagineContext;
+
+static VLC coeff_token_vlc[4];
+static VLC_TYPE coeff_token_vlc_tables[520+332+280+256][2];
+static const int coeff_token_vlc_tables_size[4]={520,332,280,256};
+
+static VLC total_zeros_vlc[15+1];
+static VLC_TYPE total_zeros_vlc_tables[15][512][2];
+static const int total_zeros_vlc_tables_size = 512;
+
+static VLC run_vlc[6+1];
+static VLC_TYPE run_vlc_tables[6][8][2];
+static const int run_vlc_tables_size = 8;
+
+static VLC run7_vlc;
+static VLC_TYPE run7_vlc_table[96][2];
+static const int run7_vlc_table_size = 96;
+
+#define COEFF_TOKEN_VLC_BITS    8
+#define TOTAL_ZEROS_VLC_BITS    9
+#define RUN_VLC_BITS            3
+#define RUN7_VLC_BITS           6
+
+#define PIXEL_REF(s, ref, plane, x, y)\
+    ((s)->ref_frames[(ref)]->data[(plane)]\
+        [(y) * (s)->ref_frames[(ref)]->linesize[(plane)] + (x)])
+
+#define PIXEL_CUR(s, plane, x, y)\
+    ((s)->cur_frame->data[(plane)]\
+        [(y) * (s)->cur_frame->linesize[(plane)] + (x)])
+
+#define PREDICT2(a,b)       (((a) + (b) + 1) >> 1)
+#define PREDICT3(a,b,c)     (((a) + 2 * (b) + (c) + 2) >> 2)
+
+#define VX_VERSION_INVALID  -1
+#define VX_VERSION_OLD       0
+#define VX_VERSION_NEW       1
+
+static av_cold void actimagine_init_static(void)
+{
+    // all these tables are equal to the ones used for h264 cavlc
+    int offset = 0;
+    for (int i = 0; i < 4; i++) {
+        coeff_token_vlc[i].table = coeff_token_vlc_tables + offset;
+        coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i];
+        init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4 * 17,
+                 &coeff_token_len [i][0], 1, 1,
+                 &coeff_token_bits[i][0], 1, 1,
+                 INIT_VLC_USE_NEW_STATIC);
+        offset += coeff_token_vlc_tables_size[i];
+    }
+    /*
+     * This is a one time safety check to make sure that
+     * the packed static coeff_token_vlc table sizes
+     * were initialized correctly.
+     */
+    av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables));
+
+    for (int i = 0; i < 15; i++) {
+        total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i];
+        total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size;
+        init_vlc(&total_zeros_vlc[i + 1],
+                 TOTAL_ZEROS_VLC_BITS, 16,
+                 &total_zeros_len [i][0], 1, 1,
+                 &total_zeros_bits[i][0], 1, 1,
+                 INIT_VLC_USE_NEW_STATIC);
+    }
+
+    for (int i = 0; i < 6; i++) {
+        run_vlc[i + 1].table = run_vlc_tables[i];
+        run_vlc[i + 1].table_allocated = run_vlc_tables_size;
+        init_vlc(&run_vlc[i + 1],
+                 RUN_VLC_BITS, 7,
+                 &run_len [i][0], 1, 1,
+                 &run_bits[i][0], 1, 1,
+                 INIT_VLC_USE_NEW_STATIC);
+    }
+
+    run7_vlc.table = run7_vlc_table,
+    run7_vlc.table_allocated = run7_vlc_table_size;
+    init_vlc(&run7_vlc, RUN7_VLC_BITS, 16,
+             &run_len [6][0], 1, 1,
+             &run_bits[6][0], 1, 1,
+             INIT_VLC_USE_NEW_STATIC);
+}
+
+static int setup_qtables(AVCodecContext *avctx, int quantizer)
+{
+    int qx, qy;
+    ActimagineContext *s = avctx->priv_data;
+
+    if (quantizer < 12 || quantizer > 161)
+        return AVERROR_INVALIDDATA;
+
+    s->quantizer = quantizer;
+
+    qx = quantizer % 6;
+    qy = quantizer / 6;
+
+    for (int i = 0; i < 2; i++)
+        for (int j = 0; j < 4; j++)
+            s->qtab[i][j] = quant4x4_tab[qx][4 * i + j] << qy;
+
+    return 0;
+}
+
+static av_cold int actimagine_init(AVCodecContext *avctx)
+{
+    int vectors_size;
+    int total_coeff_y_size;
+    int total_coeff_uv_size;
+    static AVOnce init_static_once = AV_ONCE_INIT;
+    ActimagineContext *s = avctx->priv_data;
+
+    if (avctx->width & 15 || avctx->height & 15) {
+        av_log(avctx, AV_LOG_ERROR, "width/height not multiple of 16\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    ff_bswapdsp_init(&s->bdsp);
+
+    avctx->pix_fmt = AV_PIX_FMT_YUV420P;
+    avctx->color_range = AVCOL_RANGE_JPEG;
+    avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
+    // the color space of this video format is not supported currently
+    // it is a yuv approximation that can be converted back to rgb using bitshifts
+    // r = y + (v << 1)
+    // g = y - (u >> 1) - v
+    // b = y + (u << 1)
+    avctx->colorspace = AVCOL_SPC_NB;
+
+    // predict4 cache
+    for (int i = 0; i < 5; i++)
+        for (int j = 0; j < 5; j++)
+            s->pred4_cache[i][j] = 9;
+
+    // motion vector cache
+    s->vectors_stride = (avctx->width >> 4) + 2;
+    vectors_size = ((avctx->height >> 4) + 1) * s->vectors_stride;
+    s->vectors = av_calloc(vectors_size, sizeof(MVec));
+    if (!s->vectors)
+        return AVERROR(ENOMEM);
+    memset(s->vectors, 0, vectors_size * sizeof(MVec));
+
+    // total dct coefficient cache for luma
+    s->total_coeff_y_stride = (avctx->width >> 2) + 1;
+    total_coeff_y_size = ((avctx->height >> 2) + 1) * s->total_coeff_y_stride;
+    s->total_coeff_y = av_malloc(total_coeff_y_size);
+    if (!s->total_coeff_y)
+        return AVERROR(ENOMEM);
+    memset(s->total_coeff_y, 0, total_coeff_y_size);
+
+    // total dct coefficient cache for chroma
+    s->total_coeff_uv_stride = (avctx->width >> 3) + 1;
+    total_coeff_uv_size = ((avctx->height >> 3) + 1) * s->total_coeff_uv_stride;
+    s->total_coeff_uv = av_malloc(total_coeff_uv_size);
+    if (!s->total_coeff_uv)
+        return AVERROR(ENOMEM);
+    memset(s->total_coeff_uv, 0, total_coeff_uv_size);
+
+    s->ref_frame_count = 0;
+    for (int i = 0; i < 3; i++) {
+        s->ref_frames[i] = av_frame_alloc();
+        if (!s->ref_frames[i])
+            return AVERROR(ENOMEM);
+    }
+    s->cur_frame = av_frame_alloc();
+    if (!s->cur_frame)
+        return AVERROR(ENOMEM);
+
+    s->version = VX_VERSION_INVALID;
+    s->quantizer = -1;
+    s->avi = 0;
+
+    // when the source is an avi file, the quantizer is stored in the extradata
+    if (avctx->extradata_size == 4)
+        if (!setup_qtables(avctx, AV_RL32(avctx->extradata)))
+            s->avi = 1;
+
+    ff_thread_once(&init_static_once, actimagine_init_static);
+
+    return 0;
+}
+
+static void clear_total_coeff(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+
+    // luma
+    uint8_t *total_coeff = &s->total_coeff_y[
+        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
+
+    for (int y2 = 0; y2 < (h >> 2); y2++)
+        for (int x2 = 0; x2 < (w >> 2); x2++)
+            total_coeff[y2 * s->total_coeff_y_stride + x2] = 0;
+
+    // chroma
+    total_coeff = &s->total_coeff_uv[
+        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
+
+    for (int y2 = 0; y2 < (h >> 3); y2++)
+        for (int x2 = 0; x2 < (w >> 3); x2++)
+            total_coeff[y2 * s->total_coeff_uv_stride + x2] = 0;
+}
+
+static void predict_plane_intern(AVCodecContext *avctx, int x, int y,
+                                 int w, int h, int plane)
+{
+    ActimagineContext *s = avctx->priv_data;
+    if (w == 1 && h == 1)
+        return;
+    if (w == 1 && h != 1) {
+        uint8_t top    = PIXEL_CUR(s, plane, x, y - 1);
+        uint8_t bottom = PIXEL_CUR(s, plane, x, y + h - 1);
+        PIXEL_CUR(s, plane, x, y + (h >> 1) - 1) = (top + bottom) >> 1;
+        predict_plane_intern(avctx, x, y, 1, h >> 1, plane);
+        predict_plane_intern(avctx, x, y + (h >> 1), 1, h >> 1, plane);
+    } else if (w != 1 && h == 1) {
+        uint8_t left  = PIXEL_CUR(s, plane, x - 1, y);
+        uint8_t right = PIXEL_CUR(s, plane, x + w - 1, y);
+        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y) = (left + right) >> 1;
+        predict_plane_intern(avctx, x, y, w >> 1, 1, plane);
+        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, 1, plane);
+    } else {
+        uint8_t bottom_left  = PIXEL_CUR(s, plane, x - 1, y + h - 1);
+        uint8_t top_right    = PIXEL_CUR(s, plane, x + w - 1, y - 1);
+        uint8_t bottom_right = PIXEL_CUR(s, plane, x + w - 1, y + h - 1);
+        uint8_t bottom_center = (bottom_left + bottom_right) >> 1;
+        uint8_t center_right = (top_right + bottom_right) >> 1;
+        PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + h - 1) = bottom_center;
+        PIXEL_CUR(s, plane, x + w - 1, y + (h >> 1) - 1) = center_right;
+        if ((w == 4 || w == 16) ^ (h == 4 || h == 16)) {
+            uint8_t center_left = PIXEL_CUR(s, plane, x - 1, y + (h >> 1) - 1);
+            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
+                = (center_left + center_right) >> 1;
+        } else {
+            uint8_t top_center = PIXEL_CUR(s, plane, x + (w >> 1) - 1, y - 1);
+            PIXEL_CUR(s, plane, x + (w >> 1) - 1, y + (h >> 1) - 1)
+                = (top_center + bottom_center) >> 1;
+        }
+        predict_plane_intern(avctx, x, y, w >> 1, h >> 1, plane);
+        predict_plane_intern(avctx, x + (w >> 1), y, w >> 1, h >> 1, plane);
+        predict_plane_intern(avctx, x, y + (h >> 1), w >> 1, h >> 1, plane);
+        predict_plane_intern(avctx, x + (w >> 1), y + (h >> 1), w >> 1, h >> 1,
+                             plane);
+    }
+}
+
+static void predict_plane(AVCodecContext *avctx, int x, int y, int w, int h,
+                          int plane, int param)
+{
+    ActimagineContext *s = avctx->priv_data;
+    uint8_t bottom_left = PIXEL_CUR(s, plane, x - 1, y + h - 1);
+    uint8_t top_right   = PIXEL_CUR(s, plane, x + w - 1, y - 1);
+    PIXEL_CUR(s, plane, x + w - 1, y + h - 1)
+        = ((bottom_left + top_right + 1) >> 1) + param;
+    predict_plane_intern(avctx, x, y, w, h, plane);
+}
+
+static int predict_mb_plane(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    // y
+    int param = get_se_golomb(gb);
+    if (param < -(1 << 16) || param >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
+        return AVERROR_INVALIDDATA;
+    }
+    predict_plane(avctx, x, y, w, h, 0, param << 1);
+
+    // u
+    param = get_se_golomb(gb);
+    if (param < -(1 << 16) || param >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
+        return AVERROR_INVALIDDATA;
+    }
+    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, param << 1);
+
+    // v
+    param = get_se_golomb(gb);
+    if (param < -(1 << 16) || param >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid plane param\n");
+        return AVERROR_INVALIDDATA;
+    }
+    predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, param << 1);
+
+    return 0;
+}
+
+static void predict_horizontal(AVCodecContext *avctx, int x, int y,
+                               int w, int h, int plane)
+{
+    ActimagineContext *s = avctx->priv_data;
+    for (int y2 = 0; y2 < h; y2++) {
+        uint8_t pixel = PIXEL_CUR(s, plane, x - 1, y + y2);
+        for (int x2 = 0; x2 < w; x2++)
+            PIXEL_CUR(s, plane, x + x2, y + y2) = pixel;
+    }
+}
+
+static void predict_vertical(AVCodecContext *avctx, int x, int y,
+                             int w, int h, int plane)
+{
+    ActimagineContext *s = avctx->priv_data;
+    for (int y2 = 0; y2 < h; y2++)
+        for (int x2 = 0; x2 < w; x2++)
+            PIXEL_CUR(s, plane, x + x2, y + y2)
+                = PIXEL_CUR(s, plane, x + x2, y - 1);
+}
+
+static void predict_dc(AVCodecContext *avctx, int x, int y, int w, int h,
+                       int plane)
+{
+    uint8_t dc;
+    ActimagineContext *s = avctx->priv_data;
+    if (x != 0 && y != 0) {
+        int sum_h, sum_v;
+        sum_h = w >> 1;
+        for (int x2 = 0; x2 < w; x2++)
+            sum_h += PIXEL_CUR(s, plane, x + x2, y - 1);
+        sum_h >>= predict_dc_shift_tab[w >> 2];
+
+        sum_v = h >> 1;
+        for (int y2 = 0; y2 < h; y2++)
+            sum_v += PIXEL_CUR(s, plane, x - 1, y + y2);
+        sum_v >>= predict_dc_shift_tab[h >> 2];
+
+        dc = (sum_h + sum_v + 1) >> 1;
+    } else if (x == 0 && y != 0) {
+        int sum = w >> 1;
+        for (int x2 = 0; x2 < w; x2++)
+            sum += PIXEL_CUR(s, plane, x + x2, y - 1);
+        dc = sum >> predict_dc_shift_tab[w >> 2];
+    } else if (x != 0 && y == 0) {
+        int sum = h >> 1;
+        for (int y2 = 0; y2 < h; y2++)
+            sum += PIXEL_CUR(s, plane, x - 1, y + y2);
+        dc = sum >> predict_dc_shift_tab[h >> 2];
+    } else
+        dc = 128;
+
+    for (int y2 = 0; y2 < h; y2++)
+        for (int x2 = 0; x2 < w; x2++)
+            PIXEL_CUR(s, plane, x + x2, y + y2) = dc;
+}
+
+static int predict_notile_uv(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    int mode_uv = get_ue_golomb_31(gb);
+    switch (mode_uv) {
+    case 0:// dc
+        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
+        predict_dc(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
+        break;
+    case 1:// horizontal
+        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
+        predict_horizontal(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
+        break;
+    case 2:// vertical
+        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1);
+        predict_vertical(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2);
+        break;
+    case 3:// plane
+        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 1, 0);
+        predict_plane(avctx, x >> 1, y >> 1, w >> 1, h >> 1, 2, 0);
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "invalid predict notile uv mode\n");
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
+static int predict_notile(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    int mode_y = get_ue_golomb_31(gb);
+    switch (mode_y) {
+    case 0:// vertical
+        predict_vertical(avctx, x, y, w, h, 0);
+        break;
+    case 1:// horizontal
+        predict_horizontal(avctx, x, y, w, h, 0);
+        break;
+    case 2:// dc
+        predict_dc(avctx, x, y, w, h, 0);
+        break;
+    case 3:// plane
+        predict_plane(avctx, x, y, w, h, 0, 0);
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "invalid predict notile y mode\n");
+        return AVERROR_INVALIDDATA;
+    }
+    return predict_notile_uv(avctx, x, y, w, h);
+}
+
+// slightly different from the common dc prediction method
+static void predict4_dc(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t dc;
+    ActimagineContext *s = avctx->priv_data;
+    if (x == 0 && y == 0)
+        dc = 128;
+    else {
+        int sum   = 0;
+        int shift = 1;
+
+        if (x != 0) {
+            shift++;
+            for (int y2 = 0; y2 < 4; y2++)
+                sum += PIXEL_CUR(s, 0, x - 1, y + y2);
+            sum += 2;
+        }
+
+        if (y != 0) {
+            shift++;
+            for (int x2 = 0; x2 < 4; x2++)
+                sum += PIXEL_CUR(s, 0, x + x2, y - 1);
+            sum += 2;
+        }
+
+        dc = sum >> shift;
+    }
+
+    for (int y2 = 0; y2 < 4; y2++)
+        for (int x2 = 0; x2 < 4; x2++)
+            PIXEL_CUR(s, 0, x + x2, y + y2) = dc;
+}
+
+static void predict4_diagonal_down_left(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t val;
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
+    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
+    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
+    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
+    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
+    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
+    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
+    uint8_t h = PIXEL_CUR(s, 0, x + 7, y - 1);
+
+    PIXEL_CUR(s, 0, x, y) = PREDICT3(a, b, c);
+
+    val = PREDICT3(b, c, d);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
+
+    val = PREDICT3(c, d, e);
+    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
+
+    val = PREDICT3(d, e, f);
+    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
+
+    val = PREDICT3(e, f, g);
+    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
+
+    val = PREDICT3(f, g, h);
+    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
+
+    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(g, h, h);
+}
+
+static void predict4_diagonal_down_right(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t val;
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
+    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
+    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
+    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
+
+    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
+    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
+    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
+    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
+
+    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
+
+    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(j, k, l);
+
+    val = PREDICT3(i, j, k);
+    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
+
+    val = PREDICT3(m, i, j);
+    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
+
+    val = PREDICT3(i, m, a);
+    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
+
+    val = PREDICT3(m, a, b);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
+
+    val = PREDICT3(a, b, c);
+    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
+
+    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(b, c, d);
+}
+
+static void predict4_vertical_right(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t val;
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
+    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
+    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
+    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
+
+    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
+    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
+    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
+
+    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
+
+    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT3(i, j, k);
+    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT3(m, i, j);
+
+    val = PREDICT3(a, m, i);
+    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
+
+    val = PREDICT2(a, m);
+    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
+
+    val = PREDICT3(b, a, m);
+    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
+
+    val = PREDICT2(b, a);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
+
+    val = PREDICT3(c, b, a);
+    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
+
+    val = PREDICT2(c, b);
+    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
+
+    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(d, c, b);
+    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT2(d, c);
+}
+
+static void predict4_horizontal_down(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t val;
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
+    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
+    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
+
+    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
+    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
+    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
+    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
+
+    uint8_t m = PIXEL_CUR(s, 0, x - 1, y - 1);
+
+    PIXEL_CUR(s, 0, x + 0, y + 3) = PREDICT2(k, l);
+    PIXEL_CUR(s, 0, x + 1, y + 3) = PREDICT3(j, k, l);
+
+    val = PREDICT2(j, k);
+    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
+
+    val = PREDICT3(i, j, k);
+    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 3) = val;
+
+    val = PREDICT2(i, j);
+    PIXEL_CUR(s, 0, x + 0, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
+
+    val = PREDICT3(m, i, j);
+    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 2) = val;
+
+    val = PREDICT2(i, m);
+    PIXEL_CUR(s, 0, x + 0, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
+
+    val = PREDICT3(i, m, a);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
+
+    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT3(m, a, b);
+    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(a, b, c);
+}
+
+static void predict4_vertical_left(AVCodecContext *avctx, int x, int y)
+{
+    uint8_t val;
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t a = PIXEL_CUR(s, 0, x + 0, y - 1);
+    uint8_t b = PIXEL_CUR(s, 0, x + 1, y - 1);
+    uint8_t c = PIXEL_CUR(s, 0, x + 2, y - 1);
+    uint8_t d = PIXEL_CUR(s, 0, x + 3, y - 1);
+    uint8_t e = PIXEL_CUR(s, 0, x + 4, y - 1);
+    uint8_t f = PIXEL_CUR(s, 0, x + 5, y - 1);
+    uint8_t g = PIXEL_CUR(s, 0, x + 6, y - 1);
+
+    PIXEL_CUR(s, 0, x + 3, y + 3) = PREDICT3(e, f, g);
+    PIXEL_CUR(s, 0, x + 3, y + 2) = PREDICT2(e, f);
+
+    val = PREDICT3(d, e, f);
+    PIXEL_CUR(s, 0, x + 3, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = val;
+
+    val = PREDICT2(d, e);
+    PIXEL_CUR(s, 0, x + 3, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 2, y + 2) = val;
+
+    val = PREDICT3(c, d, e);
+    PIXEL_CUR(s, 0, x + 2, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 3) = val;
+
+    val = PREDICT2(c, d);
+    PIXEL_CUR(s, 0, x + 2, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 1, y + 2) = val;
+
+    val = PREDICT3(b, c, d);
+    PIXEL_CUR(s, 0, x + 1, y + 1) = val;
+    PIXEL_CUR(s, 0, x + 0, y + 3) = val;
+
+    val = PREDICT2(b, c);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = val;
+    PIXEL_CUR(s, 0, x + 0, y + 2) = val;
+
+    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT3(a, b, c);
+    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(a, b);
+}
+
+static void predict4_horizontal_up(AVCodecContext *avctx, int x, int y)
+{
+    ActimagineContext *s = avctx->priv_data;
+
+    uint8_t i = PIXEL_CUR(s, 0, x - 1, y + 0);
+    uint8_t j = PIXEL_CUR(s, 0, x - 1, y + 1);
+    uint8_t k = PIXEL_CUR(s, 0, x - 1, y + 2);
+    uint8_t l = PIXEL_CUR(s, 0, x - 1, y + 3);
+
+    PIXEL_CUR(s, 0, x + 0, y + 0) = PREDICT2(i, j);
+    PIXEL_CUR(s, 0, x + 1, y + 0) = PREDICT3(i, j, k);
+    PIXEL_CUR(s, 0, x + 2, y + 0) = PREDICT2(j, k);
+    PIXEL_CUR(s, 0, x + 3, y + 0) = PREDICT3(j, k, l);
+
+    PIXEL_CUR(s, 0, x + 0, y + 1) = PREDICT2(j, k);
+    PIXEL_CUR(s, 0, x + 1, y + 1) = PREDICT3(j, k, l);
+    PIXEL_CUR(s, 0, x + 2, y + 1) = PREDICT2(k, l);
+    PIXEL_CUR(s, 0, x + 3, y + 1) = PREDICT3(k, l, l);
+
+    PIXEL_CUR(s, 0, x + 0, y + 2) = PREDICT2(k, l);
+    PIXEL_CUR(s, 0, x + 1, y + 2) = PREDICT3(k, l, l);
+    PIXEL_CUR(s, 0, x + 2, y + 2) = l;
+    PIXEL_CUR(s, 0, x + 3, y + 2) = l;
+
+    PIXEL_CUR(s, 0, x + 0, y + 3) = l;
+    PIXEL_CUR(s, 0, x + 1, y + 3) = l;
+    PIXEL_CUR(s, 0, x + 2, y + 3) = l;
+    PIXEL_CUR(s, 0, x + 3, y + 3) = l;
+}
+
+static int predict4(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    for (int y2 = 0; y2 < h >> 2; y2++) {
+        for (int x2 = 0; x2 < w >> 2; x2++) {
+            uint8_t mode = FFMIN(s->pred4_cache[1 + y2 - 1][1 + x2],
+                                 s->pred4_cache[1 + y2][1 + x2 - 1]);
+            if (mode == 9)// if invalid predict dc
+                mode = 2;
+
+            if (!get_bits1(gb)) {
+                uint8_t val = get_bits(gb, 3);
+                if (val >= mode)
+                    val++;
+                mode = val;
+            }
+
+            s->pred4_cache[1 + y2][1 + x2] = mode;
+
+            switch (mode) {
+            case 0:// vertical
+                predict_vertical(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
+                break;
+            case 1:// horizontal
+                predict_horizontal(avctx, x + x2 * 4, y + y2 * 4, 4, 4, 0);
+                break;
+            case 2:// dc
+                predict4_dc(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 3:// diagonal-down-left
+                predict4_diagonal_down_left(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 4:// diagonal-down-right
+                predict4_diagonal_down_right(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 5:// vertical-right
+                predict4_vertical_right(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 6:// horizontal-down
+                predict4_horizontal_down(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 7:// vertical-left
+                predict4_vertical_left(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            case 8:// horizontal-up
+                predict4_horizontal_up(avctx, x + x2 * 4, y + y2 * 4);
+                break;
+            default:
+                av_log(avctx, AV_LOG_ERROR, "invalid predict4 mode\n");
+                return AVERROR_INVALIDDATA;
+            }
+        }
+    }
+    return predict_notile_uv(avctx, x, y, w, h);
+}
+
+static void decode_dct(AVCodecContext *avctx, int x, int y, int plane,
+                       const int* level)
+{
+    int a, b, c, d, e, f;
+    int dct[16];
+    int tmp[16];
+    ActimagineContext *s = avctx->priv_data;
+
+    // dezigzag
+    for (int i = 0; i < 16; i++)
+        dct[zigzag4x4_tab[i]] = level[i];
+
+    // dequantize
+    for (int i = 0; i < 2; i++) {
+        for (int j = 0; j < 4; j++) {
+            dct[4 * j + i]     *= s->qtab[i][j];
+            dct[4 * j + i + 2] *= s->qtab[i][j];
+        }
+    }
+
+    dct[0] += 32;// rounding
+
+    for (int i = 0; i < 4; i++) {
+        a = dct[i * 4 + 0];
+        b = dct[i * 4 + 1];
+        c = dct[i * 4 + 2];
+        d = dct[i * 4 + 3];
+        a += c;
+        c = a - c * 2;
+        e = (b >> 1) - d;
+        f = b + (d >> 1);
+        tmp[ 0 + i] = a + f;
+        tmp[ 4 + i] = c + e;
+        tmp[ 8 + i] = c - e;
+        tmp[12 + i] = a - f;
+    }
+
+    for (int i = 0; i < 4; i++) {
+        a = tmp[i * 4 + 0];
+        b = tmp[i * 4 + 1];
+        c = tmp[i * 4 + 2];
+        d = tmp[i * 4 + 3];
+        a += c;
+        c =  a - c * 2;
+        e  = (b >> 1) - d;
+        f = b + (d >> 1);
+        PIXEL_CUR(s, plane, x + 0, y + i)
+            = av_clip_uint8(PIXEL_CUR(s, plane, x + 0, y + i) + ((a + f) >> 6));
+        PIXEL_CUR(s, plane, x + 1, y + i)
+            = av_clip_uint8(PIXEL_CUR(s, plane, x + 1, y + i) + ((c + e) >> 6));
+        PIXEL_CUR(s, plane, x + 2, y + i)
+            = av_clip_uint8(PIXEL_CUR(s, plane, x + 2, y + i) + ((c - e) >> 6));
+        PIXEL_CUR(s, plane, x + 3, y + i)
+            = av_clip_uint8(PIXEL_CUR(s, plane, x + 3, y + i) + ((a - f) >> 6));
+    }
+}
+
+static void decode_residu_cavlc(AVCodecContext *avctx, int x, int y, int plane,
+                                int nc, uint8_t *out_total_coeff)
+{
+    int level[16];
+    int coeff_token, total_coeff, trailing_ones, i, zeros_left, suffix_length;
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+
+    coeff_token = get_vlc2(gb, coeff_token_vlc[coeff_token_table_index[nc]].table,
+                           COEFF_TOKEN_VLC_BITS, 2);
+    trailing_ones = coeff_token & 3;
+    total_coeff   = coeff_token >> 2;
+
+    *out_total_coeff = total_coeff;
+    if (total_coeff == 0)
+        return;
+
+    av_assert2(total_coeff <= 16);
+
+    i = 15;
+    if (total_coeff != 16) {
+        int trailing_zeros;
+        zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff].table,
+                              TOTAL_ZEROS_VLC_BITS, 1);
+        trailing_zeros = 16 - (total_coeff + zeros_left);
+        while(trailing_zeros-- > 0)
+            level[i--] = 0;
+    } else
+        zeros_left = 0;
+
+    suffix_length = 0;
+    while (1) {
+        int level_suffix, level_code, run_before;
+        if (trailing_ones > 0) {
+            trailing_ones--;
+            level[i--] = get_bits1(gb) ? -1 : 1;
+        } else {
+            int level_prefix = 0;
+            while (!get_bits1(gb))
+                level_prefix++;
+
+            if (level_prefix == 15)
+                level_suffix = get_bits(gb, 11);
+            else
+                level_suffix = suffix_length == 0 ? 0 : get_bits(gb, suffix_length);
+
+            level_code = level_suffix + (level_prefix << suffix_length);
+
+            if (level_code > cavlc_suffix_len_update_tab[suffix_length])
+                suffix_length++;
+
+            level_code++;
+            if (get_bits1(gb))
+                level_code = -level_code;
+            level[i--] = level_code;
+        }
+
+        if (--total_coeff == 0)
+            break;
+
+        if (zeros_left == 0)
+            continue;
+
+        if(zeros_left < 7)
+            run_before = get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1);
+        else
+            run_before = get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2);
+        zeros_left -= run_before;
+        while(run_before-- > 0)
+            level[i--] = 0;
+    }
+
+    while(zeros_left-- > 0)
+        level[i--] = 0;
+
+    decode_dct(avctx, x, y, plane, level);
+}
+
+static int decode_residu_blocks(AVCodecContext *avctx, int x, int y,
+                                int w, int h)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    uint8_t *total_coeff_y = &s->total_coeff_y[
+        ((y >> 2) + 1) * s->total_coeff_y_stride + (x >> 2) + 1];
+    uint8_t *total_coeff_uv = &s->total_coeff_uv[
+        ((y >> 3) + 1) * s->total_coeff_uv_stride + (x >> 3) + 1];
+    for (int y2 = 0; y2 < h >> 3; y2++) {
+        for (int x2 = 0; x2 < w >> 3; x2++) {
+            uint8_t residu_mask;
+            int code = get_ue_golomb_31(gb);
+            if (code > 0x1F) {
+                av_log(avctx, AV_LOG_ERROR, "invalid residu mask code\n");
+                return AVERROR_INVALIDDATA;
+            }
+            if (s->version == VX_VERSION_OLD)
+                residu_mask = residu_mask_old_tab[code];
+            else
+                residu_mask = residu_mask_new_tab[code];
+
+            if (residu_mask & 1) {
+                int nc = (total_coeff_y[-1] +
+                          total_coeff_y[-s->total_coeff_y_stride] + 1) >> 1;
+                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8, 0, nc,
+                                    &total_coeff_y[0]);
+            } else
+                total_coeff_y[0] = 0;
+
+            if (residu_mask & 2) {
+                int nc = (total_coeff_y[0] +
+                          total_coeff_y[-s->total_coeff_y_stride + 1] + 1) >> 1;
+                decode_residu_cavlc(avctx, x + x2 * 8 + 4, y + y2 * 8, 0, nc,
+                                    &total_coeff_y[1]);
+            } else
+                total_coeff_y[1] = 0;
+
+            if (residu_mask & 4) {
+                int nc = (total_coeff_y[s->total_coeff_y_stride - 1] +
+                          total_coeff_y[0] + 1) >> 1;
+                decode_residu_cavlc(avctx, x + x2 * 8, y + y2 * 8 + 4, 0, nc,
+                                    &total_coeff_y[s->total_coeff_y_stride]);
+            } else
+                total_coeff_y[s->total_coeff_y_stride] = 0;
+
+            if (residu_mask & 8) {
+                int nc = (total_coeff_y[s->total_coeff_y_stride] +
+                          total_coeff_y[1] + 1) >> 1;
+                decode_residu_cavlc(
+                    avctx, x + x2 * 8 + 4, y + y2 * 8 + 4, 0, nc,
+                    &total_coeff_y[s->total_coeff_y_stride + 1]);
+            } else
+                total_coeff_y[s->total_coeff_y_stride + 1] = 0;
+
+            if (residu_mask & 16) {
+                uint8_t total_coeff_u, total_coeff_v;
+                int nc = (total_coeff_uv[-1] +
+                          total_coeff_uv[-s->total_coeff_uv_stride] + 1) >> 1;
+                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
+                                    1, nc, &total_coeff_u);
+                decode_residu_cavlc(avctx, (x + x2 * 8) >> 1, (y + y2 * 8) >> 1,
+                                    2, nc, &total_coeff_v);
+                total_coeff_uv[0] = (total_coeff_u + total_coeff_v + 1) >> 1;
+            } else
+                total_coeff_uv[0] = 0;
+
+            total_coeff_y += 2;
+            total_coeff_uv++;
+        }
+        total_coeff_y  += (s->total_coeff_y_stride << 1) - (w >> 2);
+        total_coeff_uv += s->total_coeff_uv_stride - (w >> 3);
+    }
+    return 0;
+}
+
+static int predict_inter(AVCodecContext *avctx, int x, int y, int w, int h,
+                          const MVec *predVec, int has_delta, int ref_frame)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    MVec vec = *predVec;
+
+    if (ref_frame >= s->ref_frame_count) {
+        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
+    s->cur_frame->key_frame = 0;
+
+    if (has_delta) {
+        vec.x += (unsigned)get_se_golomb(gb);
+        vec.y += (unsigned)get_se_golomb(gb);
+    }
+
+    if (vec.x >= INT_MAX || vec.y >= INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    s->vectors[(1 + (y >> 4)) * s->vectors_stride + 1 + (x >> 4)] = vec;
+
+    if (x + vec.x < 0 || x + vec.x + w > avctx->width ||
+        y + vec.y < 0 || y + vec.y + h > avctx->height) {
+        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    // luma
+    for (int y2 = 0; y2 < h; y2++)
+        for (int x2 = 0; x2 < w; x2++)
+            PIXEL_CUR(s, 0, x + x2, y + y2)
+                = PIXEL_REF(s, ref_frame, 0, x + x2 + vec.x, y + y2 + vec.y);
+
+    // chroma
+    for (int y2 = 0; y2 < (h >> 1); y2++) {
+        for (int x2 = 0; x2 < (w >> 1); x2++) {
+            // u
+            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2)
+                = PIXEL_REF(s, ref_frame, 1, (x >> 1) + x2 + (vec.x >> 1),
+                            (y >> 1) + y2 + (vec.y >> 1));
+            // v
+            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2)
+                = PIXEL_REF(s, ref_frame, 2, (x >> 1) + x2 + (vec.x >> 1),
+                            (y >> 1) + y2 + (vec.y >> 1));
+        }
+    }
+
+    return 0;
+}
+
+static int predict_inter_dc(AVCodecContext *avctx, int x, int y, int w, int h)
+{
+    int dx, dy, dc_y, dc_u, dc_v;
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+
+    if (s->ref_frame_count == 0) {
+        av_log(avctx, AV_LOG_ERROR, "reference to unavailable frame\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    dx   = get_se_golomb(gb);
+    dy   = get_se_golomb(gb);
+
+    if (x + dx < 0 || x + dx + w > avctx->width ||
+        y + dy < 0 || y + dy + h > avctx->height) {
+        av_log(avctx, AV_LOG_ERROR, "motion vector out of bounds\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    dc_y = get_se_golomb(gb);
+    if (dc_y < -(1<<16) || dc_y >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
+        return AVERROR_INVALIDDATA;
+    }
+    dc_y <<= 1;
+
+    dc_u = get_se_golomb(gb);
+    if (dc_u < -(1<<16) || dc_u >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
+        return AVERROR_INVALIDDATA;
+    }
+    dc_u <<= 1;
+
+    dc_v = get_se_golomb(gb);
+    if (dc_v < -(1<<16) || dc_v >= (1 << 16)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid dc offset\n");
+        return AVERROR_INVALIDDATA;
+    }
+    dc_v <<= 1;
+
+    s->cur_frame->pict_type = AV_PICTURE_TYPE_P;
+    s->cur_frame->key_frame = 0;
+
+    // luma
+    for (int y2 = 0; y2 < h; y2++)
+        for (int x2 = 0; x2 < w; x2++)
+            PIXEL_CUR(s, 0, x + x2, y + y2) = av_clip_uint8(
+                PIXEL_REF(s, 0, 0, x + x2 + dx, y + y2 + dy) + dc_y);
+
+    // chroma
+    for (int y2 = 0; y2 < (h >> 1); y2++) {
+        for (int x2 = 0; x2 < (w >> 1); x2++) {
+            PIXEL_CUR(s, 1, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
+                PIXEL_REF(s, 0, 1, (x >> 1) + x2 + (dx >> 1),
+                          (y >> 1) + y2 + (dy >> 1)) + dc_u);
+            PIXEL_CUR(s, 2, (x >> 1) + x2, (y >> 1) + y2) = av_clip_uint8(
+                PIXEL_REF(s, 0, 2, (x >> 1) + x2 + (dx >> 1),
+                          (y >> 1) + y2 + (dy >> 1)) + dc_v);
+        }
+    }
+
+    return 0;
+}
+
+static int decode_mb(AVCodecContext *avctx, int x, int y, int w, int h,
+                     const MVec *predVec)
+{
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    int ret = 0;
+
+    int mode = get_ue_golomb_31(gb);
+    if (s->version == VX_VERSION_OLD)
+        mode = old_mb_mode_remap_tab[mode];
+
+    switch (mode) {
+    case 0:// v-split, no residu
+        if (w == 2) {
+            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
+            return ret;
+        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
+            return ret;
+        if (w == 8 && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 1:// no delta, no residu, ref 0
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 2:// h-split, no residu
+        if (h == 2) {
+            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
+            return ret;
+        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && h == 8)
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 3:// unpredicted delta ref0 + dc offset, no residu
+        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 4:// delta, no residu, ref 0
+    case 5:// delta, no residu, ref 1
+    case 6:// delta, no residu, ref 2
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 4)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 7:// plane, no residu
+        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 8:// v-split, residu
+        if (w == 2) {
+            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if ((ret = decode_mb(avctx, x, y, w >> 1, h, predVec)) < 0)
+            return ret;
+        if ((ret = decode_mb(avctx, x + (w >> 1), y, w >> 1, h, predVec)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 9:// no delta, no residu, ref 1
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 1)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 10:// unpredicted delta ref0 + dc offset, residu
+        if ((ret = predict_inter_dc(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 11:// predict notile, no residu
+        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 12:// no delta, residu, ref 0
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 0)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 13:// h-split, residu
+        if (h == 2) {
+            av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
+            return AVERROR_INVALIDDATA;
+        }
+        if ((ret = decode_mb(avctx, x, y, w, h >> 1, predVec)) < 0)
+            return ret;
+        if ((ret = decode_mb(avctx, x, y + (h >> 1), w, h >> 1, predVec)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 14:// no delta, no residu, ref 2
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, 2)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 15:// predict4, no residu
+        if ((ret = predict4(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((w == 8 || w == 16) && (h == 8 || h == 16))
+            clear_total_coeff(avctx, x, y, w, h);
+        break;
+    case 16:// delta, residu, ref 0
+    case 17:// delta, residu, ref 1
+    case 18:// delta, residu, ref 2
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 1, mode - 16)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 19:// predict4, residu
+        if ((ret = predict4(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 20:// no delta, residu, ref 1
+    case 21:// no delta, residu, ref 2
+        if ((ret = predict_inter(avctx, x, y, w, h, predVec, 0, mode - 20 + 1)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 22:// predict notile, residu
+        if ((ret = predict_notile(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    case 23:// plane, residu
+        if ((ret = predict_mb_plane(avctx, x, y, w, h)) < 0)
+            return ret;
+        if ((ret = decode_residu_blocks(avctx, x, y, w, h)) < 0)
+            return ret;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "invalid macroblock mode\n");
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
+static int detect_format(AVCodecContext *avctx)
+{
+    // assume the new format, if any incorrect decisions are made for the
+    // first macroblock of a keyframe (ref, non-dc prediction) then it must be
+    // the old format
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    int w = 16;
+    int h = 16;
+    while (1) {
+        int mode = get_ue_golomb_31(gb);
+        if (mode == 0 || mode == 8) { // v-split
+            if (w == 2) // too many splits
+                return VX_VERSION_OLD;
+            w >>= 1;
+            continue;
+        } else if (mode == 2 || mode == 13) { // h-split
+            if (h == 2) // too many splits
+                return VX_VERSION_OLD;
+            h >>= 1;
+            continue;
+        } else if (mode == 11 || mode == 22) { // predict notile            
+            if (get_ue_golomb_31(gb) != 2) // mode_y != dc
+                return VX_VERSION_OLD;
+            if (get_ue_golomb_31(gb) != 0) // mode_uv != dc
+                return VX_VERSION_OLD;
+            break; //we should have enough evidence now
+        } else if (mode == 15 || mode == 19) { // predict4
+            // initial prediction is always dc
+            // we don't expect that prediction to be wrong
+            if (!get_bits1(gb))
+                return VX_VERSION_OLD;
+            break; //we should have enough evidence now
+        } else // inter prediction, plane or any other value
+            return VX_VERSION_OLD;
+    }
+    return VX_VERSION_NEW;
+}
+
+static int actimagine_decode(AVCodecContext *avctx, void *data,
+                            int *got_frame, AVPacket *pkt)
+{
+    MVec *vectors;
+    int ret;
+    ActimagineContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    AVFrame *frame = s->cur_frame;
+
+    // in avi files the frames start with a 32 bit number that seems to
+    // indicate the total number of bits
+    int offset = s->avi ? 4 : 0;
+
+    av_fast_padded_malloc(&s->bitstream, &s->bitstream_size,
+                          pkt->size);
+
+    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
+        return ret;
+
+    s->bdsp.bswap16_buf((uint16_t *)s->bitstream, (uint16_t *)pkt->data,
+                        (pkt->size + 1) >> 1);
+
+    ret = init_get_bits8(gb, s->bitstream + offset,
+                         FFALIGN(pkt->size - offset, 2));
+    if (ret < 0)
+        return ret;
+
+    // determine the bitstream version if this was not done yet
+    if (s->version == VX_VERSION_INVALID) {
+        if (s->ref_frame_count != 0) {
+            av_log(avctx, AV_LOG_ERROR, "can't determine version on p frame\n");
+            return AVERROR_INVALIDDATA;
+        }
+        s->version = detect_format(avctx);
+
+        // reinit bitreader
+        ret = init_get_bits8(gb, s->bitstream + offset,
+                             FFALIGN(pkt->size - offset, 2));
+        if (ret < 0)
+            return ret;
+    }
+
+    vectors = s->vectors + s->vectors_stride + 1;
+
+    frame->pict_type = AV_PICTURE_TYPE_I;
+    frame->key_frame = 1;
+
+    if (s->quantizer == -1) {
+        av_log(avctx, AV_LOG_ERROR, "no quantizer setup\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    for (int y = 0; y < avctx->height; y += 16) {
+        MVec *vec_cur = vectors;
+        for (int x = 0; x < avctx->width; x += 16) {
+            MVec predVec;
+            vec_cur[0].x = 0;
+            vec_cur[0].y = 0;
+            predVec.x = mid_pred(vec_cur[-1].x, vec_cur[-s->vectors_stride].x,
+                                 vec_cur[-s->vectors_stride + 1].x);
+            predVec.y = mid_pred(vec_cur[-1].y, vec_cur[-s->vectors_stride].y,
+                                 vec_cur[-s->vectors_stride + 1].y);
+            if ((ret = decode_mb(avctx, x, y, 16, 16, &predVec)) < 0)
+                return ret;
+            vec_cur++;
+        }
+        vectors += s->vectors_stride;
+    }
+
+    if (s->ref_frame_count == 3)
+        av_frame_unref(s->ref_frames[2]);
+
+    s->cur_frame = s->ref_frames[2];
+    s->ref_frames[2] = s->ref_frames[1];
+    s->ref_frames[1] = s->ref_frames[0];
+    s->ref_frames[0] = frame;
+
+    if (s->ref_frame_count < 3)
+        s->ref_frame_count++;
+
+    if ((ret = av_frame_ref(data, frame)) < 0)
+        return ret;
+    *got_frame = 1;
+
+    return 0;
+}
+
+static void actimagine_flush(AVCodecContext *avctx)
+{
+    ActimagineContext *s = avctx->priv_data;
+
+    for (int i = 0; i < 3; i++)
+        av_frame_unref(s->ref_frames[i]);
+
+    s->ref_frame_count = 0;
+}
+
+static av_cold int actimagine_close(AVCodecContext *avctx)
+{
+    ActimagineContext *s = avctx->priv_data;
+
+    av_freep(&s->vectors);
+    s->vectors_stride = 0;
+    av_freep(&s->total_coeff_y);
+    s->total_coeff_y_stride = 0;
+    av_freep(&s->total_coeff_uv);
+    s->total_coeff_uv_stride = 0;
+
+    av_freep(&s->bitstream);
+    s->bitstream_size = 0;
+
+    for (int i = 0; i < 3; i++)
+        av_frame_free(&s->ref_frames[i]);
+    av_frame_free(&s->cur_frame);
+
+    return 0;
+}
+
+AVCodec ff_actimagine_decoder = {
+    .name           = "actimagine",
+    .long_name      = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_ACTIMAGINE,
+    .priv_data_size = sizeof(ActimagineContext),
+    .init           = actimagine_init,
+    .decode         = actimagine_decode,
+    .flush          = actimagine_flush,
+    .close          = actimagine_close,
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2e9a3581de..10809f3492 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -32,6 +32,7 @@ 
 extern AVCodec ff_a64multi_encoder;
 extern AVCodec ff_a64multi5_encoder;
 extern AVCodec ff_aasc_decoder;
+extern AVCodec ff_actimagine_decoder;
 extern AVCodec ff_aic_decoder;
 extern AVCodec ff_alias_pix_encoder;
 extern AVCodec ff_alias_pix_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 17f8a14044..65d96c21af 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1856,6 +1856,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Digital Pictures SGA Video"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_ACTIMAGINE,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "actimagine",
+        .long_name = NULL_IF_CONFIG_SMALL("Actimagine VX Video"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index ab7bc68ee2..a4b3f3955d 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -307,6 +307,7 @@  enum AVCodecID {
     AV_CODEC_ID_CRI,
     AV_CODEC_ID_SIMBIOSIS_IMX,
     AV_CODEC_ID_SGA_VIDEO,
+    AV_CODEC_ID_ACTIMAGINE,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 4299ad4239..f992e1b36e 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@ 
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  58
-#define LIBAVCODEC_VERSION_MINOR 131
+#define LIBAVCODEC_VERSION_MINOR 132
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 270ff7c024..5d5cfe16b0 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -496,6 +496,8 @@  const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_MVHA,         MKTAG('M', 'V', 'H', 'A') },
     { AV_CODEC_ID_MV30,         MKTAG('M', 'V', '3', '0') },
     { AV_CODEC_ID_NOTCHLC,      MKTAG('n', 'l', 'c', '1') },
+    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('V', 'X', 'S', '1') },
+    { AV_CODEC_ID_ACTIMAGINE,   MKTAG('v', 'x', 's', '1') },
     { AV_CODEC_ID_NONE,         0 }
 };