Merge commit '458446acfa1441d283dacf9e6e545beb083b8bb0'
authorMichael Niedermayer <michaelni@gmx.at>
Fri, 15 Nov 2013 12:56:05 +0000 (13:56 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Fri, 15 Nov 2013 14:07:10 +0000 (15:07 +0100)
* commit '458446acfa1441d283dacf9e6e545beb083b8bb0':
  lavc: Edge emulation with dst/src linesize

Conflicts:
libavcodec/cavs.c
libavcodec/h264.c
libavcodec/hevc.c
libavcodec/mpegvideo_enc.c
libavcodec/mpegvideo_motion.c
libavcodec/rv34.c
libavcodec/svq3.c
libavcodec/vc1dec.c
libavcodec/videodsp.h
libavcodec/videodsp_template.c
libavcodec/vp3.c
libavcodec/vp8.c
libavcodec/wmv2.c
libavcodec/x86/videodsp.asm
libavcodec/x86/videodsp_init.c

Changes to the asm are not merged, they are left for volunteers or
in their absence for later.
The changes this merge introduces are reordering of the function
arguments

See: face578d56c2d1375e40d5e2a28acc122132bc55
Merged-by: Michael Niedermayer <michaelni@gmx.at>
20 files changed:
1  2 
libavcodec/cavs.c
libavcodec/diracdec.c
libavcodec/h264.c
libavcodec/hevc.c
libavcodec/mpegvideo.c
libavcodec/mpegvideo_enc.c
libavcodec/mpegvideo_motion.c
libavcodec/rv34.c
libavcodec/snow.c
libavcodec/svq3.c
libavcodec/vc1dec.c
libavcodec/videodsp.h
libavcodec/videodsp_template.c
libavcodec/vp3.c
libavcodec/vp56.c
libavcodec/vp8.c
libavcodec/vp9.c
libavcodec/wmv2.c
libavcodec/x86/dsputil_mmx.c
libavcodec/x86/videodsp_init.c

Simple merge
index 15baf75,0000000..dbc2aab
mode 100644,000000..100644
--- /dev/null
@@@ -1,1939 -1,0 +1,1939 @@@
-             ff_emulated_edge_mc(s->edge_emu_buffer[i], p->stride,
-                                 src[i], p->stride,
 +/*
 + * Copyright (C) 2007 Marco Gerards <marco@gnu.org>
 + * Copyright (C) 2009 David Conrad
 + * Copyright (C) 2011 Jordi Ortiz
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Dirac Decoder
 + * @author Marco Gerards <marco@gnu.org>, David Conrad, Jordi Ortiz <nenjordi@gmail.com>
 + */
 +
 +#include "avcodec.h"
 +#include "dsputil.h"
 +#include "get_bits.h"
 +#include "bytestream.h"
 +#include "internal.h"
 +#include "golomb.h"
 +#include "dirac_arith.h"
 +#include "mpeg12data.h"
 +#include "dirac_dwt.h"
 +#include "dirac.h"
 +#include "diracdsp.h"
 +#include "videodsp.h" // for ff_emulated_edge_mc_8
 +
 +/**
 + * The spec limits the number of wavelet decompositions to 4 for both
 + * level 1 (VC-2) and 128 (long-gop default).
 + * 5 decompositions is the maximum before >16-bit buffers are needed.
 + * Schroedinger allows this for DD 9,7 and 13,7 wavelets only, limiting
 + * the others to 4 decompositions (or 3 for the fidelity filter).
 + *
 + * We use this instead of MAX_DECOMPOSITIONS to save some memory.
 + */
 +#define MAX_DWT_LEVELS 5
 +
 +/**
 + * The spec limits this to 3 for frame coding, but in practice can be as high as 6
 + */
 +#define MAX_REFERENCE_FRAMES 8
 +#define MAX_DELAY 5         /* limit for main profile for frame coding (TODO: field coding) */
 +#define MAX_FRAMES (MAX_REFERENCE_FRAMES + MAX_DELAY + 1)
 +#define MAX_QUANT 68        /* max quant for VC-2 */
 +#define MAX_BLOCKSIZE 32    /* maximum xblen/yblen we support */
 +
 +/**
 + * DiracBlock->ref flags, if set then the block does MC from the given ref
 + */
 +#define DIRAC_REF_MASK_REF1   1
 +#define DIRAC_REF_MASK_REF2   2
 +#define DIRAC_REF_MASK_GLOBAL 4
 +
 +/**
 + * Value of Picture.reference when Picture is not a reference picture, but
 + * is held for delayed output.
 + */
 +#define DELAYED_PIC_REF 4
 +
 +#define ff_emulated_edge_mc ff_emulated_edge_mc_8 /* Fix: change the calls to this function regarding bit depth */
 +
 +#define CALC_PADDING(size, depth)                       \
 +    (((size + (1 << depth) - 1) >> depth) << depth)
 +
 +#define DIVRNDUP(a, b) (((a) + (b) - 1) / (b))
 +
 +typedef struct {
 +    AVFrame avframe;
 +    int interpolated[3];    /* 1 if hpel[] is valid */
 +    uint8_t *hpel[3][4];
 +    uint8_t *hpel_base[3][4];
 +} DiracFrame;
 +
 +typedef struct {
 +    union {
 +        int16_t mv[2][2];
 +        int16_t dc[3];
 +    } u; /* anonymous unions aren't in C99 :( */
 +    uint8_t ref;
 +} DiracBlock;
 +
 +typedef struct SubBand {
 +    int level;
 +    int orientation;
 +    int stride;
 +    int width;
 +    int height;
 +    int quant;
 +    IDWTELEM *ibuf;
 +    struct SubBand *parent;
 +
 +    /* for low delay */
 +    unsigned length;
 +    const uint8_t *coeff_data;
 +} SubBand;
 +
 +typedef struct Plane {
 +    int width;
 +    int height;
 +    ptrdiff_t stride;
 +
 +    int idwt_width;
 +    int idwt_height;
 +    int idwt_stride;
 +    IDWTELEM *idwt_buf;
 +    IDWTELEM *idwt_buf_base;
 +    IDWTELEM *idwt_tmp;
 +
 +    /* block length */
 +    uint8_t xblen;
 +    uint8_t yblen;
 +    /* block separation (block n+1 starts after this many pixels in block n) */
 +    uint8_t xbsep;
 +    uint8_t ybsep;
 +    /* amount of overspill on each edge (half of the overlap between blocks) */
 +    uint8_t xoffset;
 +    uint8_t yoffset;
 +
 +    SubBand band[MAX_DWT_LEVELS][4];
 +} Plane;
 +
 +typedef struct DiracContext {
 +    AVCodecContext *avctx;
 +    DSPContext dsp;
 +    DiracDSPContext diracdsp;
 +    GetBitContext gb;
 +    dirac_source_params source;
 +    int seen_sequence_header;
 +    int frame_number;           /* number of the next frame to display       */
 +    Plane plane[3];
 +    int chroma_x_shift;
 +    int chroma_y_shift;
 +
 +    int zero_res;               /* zero residue flag                         */
 +    int is_arith;               /* whether coeffs use arith or golomb coding */
 +    int low_delay;              /* use the low delay syntax                  */
 +    int globalmc_flag;          /* use global motion compensation            */
 +    int num_refs;               /* number of reference pictures              */
 +
 +    /* wavelet decoding */
 +    unsigned wavelet_depth;     /* depth of the IDWT                         */
 +    unsigned wavelet_idx;
 +
 +    /**
 +     * schroedinger older than 1.0.8 doesn't store
 +     * quant delta if only one codebook exists in a band
 +     */
 +    unsigned old_delta_quant;
 +    unsigned codeblock_mode;
 +
 +    struct {
 +        unsigned width;
 +        unsigned height;
 +    } codeblock[MAX_DWT_LEVELS+1];
 +
 +    struct {
 +        unsigned num_x;         /* number of horizontal slices               */
 +        unsigned num_y;         /* number of vertical slices                 */
 +        AVRational bytes;       /* average bytes per slice                   */
 +        uint8_t quant[MAX_DWT_LEVELS][4]; /* [DIRAC_STD] E.1 */
 +    } lowdelay;
 +
 +    struct {
 +        int pan_tilt[2];        /* pan/tilt vector                           */
 +        int zrs[2][2];          /* zoom/rotate/shear matrix                  */
 +        int perspective[2];     /* perspective vector                        */
 +        unsigned zrs_exp;
 +        unsigned perspective_exp;
 +    } globalmc[2];
 +
 +    /* motion compensation */
 +    uint8_t mv_precision;       /* [DIRAC_STD] REFS_WT_PRECISION             */
 +    int16_t weight[2];          /* [DIRAC_STD] REF1_WT and REF2_WT           */
 +    unsigned weight_log2denom;  /* [DIRAC_STD] REFS_WT_PRECISION             */
 +
 +    int blwidth;                /* number of blocks (horizontally)           */
 +    int blheight;               /* number of blocks (vertically)             */
 +    int sbwidth;                /* number of superblocks (horizontally)      */
 +    int sbheight;               /* number of superblocks (vertically)        */
 +
 +    uint8_t *sbsplit;
 +    DiracBlock *blmotion;
 +
 +    uint8_t *edge_emu_buffer[4];
 +    uint8_t *edge_emu_buffer_base;
 +
 +    uint16_t *mctmp;            /* buffer holding the MC data multipled by OBMC weights */
 +    uint8_t *mcscratch;
 +
 +    DECLARE_ALIGNED(16, uint8_t, obmc_weight)[3][MAX_BLOCKSIZE*MAX_BLOCKSIZE];
 +
 +    void (*put_pixels_tab[4])(uint8_t *dst, const uint8_t *src[5], int stride, int h);
 +    void (*avg_pixels_tab[4])(uint8_t *dst, const uint8_t *src[5], int stride, int h);
 +    void (*add_obmc)(uint16_t *dst, const uint8_t *src, int stride, const uint8_t *obmc_weight, int yblen);
 +    dirac_weight_func weight_func;
 +    dirac_biweight_func biweight_func;
 +
 +    DiracFrame *current_picture;
 +    DiracFrame *ref_pics[2];
 +
 +    DiracFrame *ref_frames[MAX_REFERENCE_FRAMES+1];
 +    DiracFrame *delay_frames[MAX_DELAY+1];
 +    DiracFrame all_frames[MAX_FRAMES];
 +} DiracContext;
 +
 +/**
 + * Dirac Specification ->
 + * Parse code values. 9.6.1 Table 9.1
 + */
 +enum dirac_parse_code {
 +    pc_seq_header         = 0x00,
 +    pc_eos                = 0x10,
 +    pc_aux_data           = 0x20,
 +    pc_padding            = 0x30,
 +};
 +
 +enum dirac_subband {
 +    subband_ll = 0,
 +    subband_hl = 1,
 +    subband_lh = 2,
 +    subband_hh = 3
 +};
 +
 +static const uint8_t default_qmat[][4][4] = {
 +    { { 5,  3,  3,  0}, { 0,  4,  4,  1}, { 0,  5,  5,  2}, { 0,  6,  6,  3} },
 +    { { 4,  2,  2,  0}, { 0,  4,  4,  2}, { 0,  5,  5,  3}, { 0,  7,  7,  5} },
 +    { { 5,  3,  3,  0}, { 0,  4,  4,  1}, { 0,  5,  5,  2}, { 0,  6,  6,  3} },
 +    { { 8,  4,  4,  0}, { 0,  4,  4,  0}, { 0,  4,  4,  0}, { 0,  4,  4,  0} },
 +    { { 8,  4,  4,  0}, { 0,  4,  4,  0}, { 0,  4,  4,  0}, { 0,  4,  4,  0} },
 +    { { 0,  4,  4,  8}, { 0,  8,  8, 12}, { 0, 13, 13, 17}, { 0, 17, 17, 21} },
 +    { { 3,  1,  1,  0}, { 0,  4,  4,  2}, { 0,  6,  6,  5}, { 0,  9,  9,  7} },
 +};
 +
 +static const int qscale_tab[MAX_QUANT+1] = {
 +    4,     5,     6,     7,     8,    10,    11,    13,
 +    16,    19,    23,    27,    32,    38,    45,    54,
 +    64,    76,    91,   108,   128,   152,   181,   215,
 +    256,   304,   362,   431,   512,   609,   724,   861,
 +    1024,  1218,  1448,  1722,  2048,  2435,  2896,  3444,
 +    4096,  4871,  5793,  6889,  8192,  9742, 11585, 13777,
 +    16384, 19484, 23170, 27554, 32768, 38968, 46341, 55109,
 +    65536, 77936
 +};
 +
 +static const int qoffset_intra_tab[MAX_QUANT+1] = {
 +    1,     2,     3,     4,     4,     5,     6,     7,
 +    8,    10,    12,    14,    16,    19,    23,    27,
 +    32,    38,    46,    54,    64,    76,    91,   108,
 +    128,   152,   181,   216,   256,   305,   362,   431,
 +    512,   609,   724,   861,  1024,  1218,  1448,  1722,
 +    2048,  2436,  2897,  3445,  4096,  4871,  5793,  6889,
 +    8192,  9742, 11585, 13777, 16384, 19484, 23171, 27555,
 +    32768, 38968
 +};
 +
 +static const int qoffset_inter_tab[MAX_QUANT+1] = {
 +    1,     2,     2,     3,     3,     4,     4,     5,
 +    6,     7,     9,    10,    12,    14,    17,    20,
 +    24,    29,    34,    41,    48,    57,    68,    81,
 +    96,   114,   136,   162,   192,   228,   272,   323,
 +    384,   457,   543,   646,   768,   913,  1086,  1292,
 +    1536,  1827,  2172,  2583,  3072,  3653,  4344,  5166,
 +    6144,  7307,  8689, 10333, 12288, 14613, 17378, 20666,
 +    24576, 29226
 +};
 +
 +/* magic number division by 3 from schroedinger */
 +static inline int divide3(int x)
 +{
 +    return ((x+1)*21845 + 10922) >> 16;
 +}
 +
 +static DiracFrame *remove_frame(DiracFrame *framelist[], int picnum)
 +{
 +    DiracFrame *remove_pic = NULL;
 +    int i, remove_idx = -1;
 +
 +    for (i = 0; framelist[i]; i++)
 +        if (framelist[i]->avframe.display_picture_number == picnum) {
 +            remove_pic = framelist[i];
 +            remove_idx = i;
 +        }
 +
 +    if (remove_pic)
 +        for (i = remove_idx; framelist[i]; i++)
 +            framelist[i] = framelist[i+1];
 +
 +    return remove_pic;
 +}
 +
 +static int add_frame(DiracFrame *framelist[], int maxframes, DiracFrame *frame)
 +{
 +    int i;
 +    for (i = 0; i < maxframes; i++)
 +        if (!framelist[i]) {
 +            framelist[i] = frame;
 +            return 0;
 +        }
 +    return -1;
 +}
 +
 +static int alloc_sequence_buffers(DiracContext *s)
 +{
 +    int sbwidth  = DIVRNDUP(s->source.width,  4);
 +    int sbheight = DIVRNDUP(s->source.height, 4);
 +    int i, w, h, top_padding;
 +
 +    /* todo: think more about this / use or set Plane here */
 +    for (i = 0; i < 3; i++) {
 +        int max_xblen = MAX_BLOCKSIZE >> (i ? s->chroma_x_shift : 0);
 +        int max_yblen = MAX_BLOCKSIZE >> (i ? s->chroma_y_shift : 0);
 +        w = s->source.width  >> (i ? s->chroma_x_shift : 0);
 +        h = s->source.height >> (i ? s->chroma_y_shift : 0);
 +
 +        /* we allocate the max we support here since num decompositions can
 +         * change from frame to frame. Stride is aligned to 16 for SIMD, and
 +         * 1<<MAX_DWT_LEVELS top padding to avoid if(y>0) in arith decoding
 +         * MAX_BLOCKSIZE padding for MC: blocks can spill up to half of that
 +         * on each side */
 +        top_padding = FFMAX(1<<MAX_DWT_LEVELS, max_yblen/2);
 +        w = FFALIGN(CALC_PADDING(w, MAX_DWT_LEVELS), 8); /* FIXME: Should this be 16 for SSE??? */
 +        h = top_padding + CALC_PADDING(h, MAX_DWT_LEVELS) + max_yblen/2;
 +
 +        s->plane[i].idwt_buf_base = av_mallocz((w+max_xblen)*h * sizeof(IDWTELEM));
 +        s->plane[i].idwt_tmp      = av_malloc((w+16) * sizeof(IDWTELEM));
 +        s->plane[i].idwt_buf      = s->plane[i].idwt_buf_base + top_padding*w;
 +        if (!s->plane[i].idwt_buf_base || !s->plane[i].idwt_tmp)
 +            return AVERROR(ENOMEM);
 +    }
 +
 +    w = s->source.width;
 +    h = s->source.height;
 +
 +    /* fixme: allocate using real stride here */
 +    s->sbsplit  = av_malloc(sbwidth * sbheight);
 +    s->blmotion = av_malloc(sbwidth * sbheight * 16 * sizeof(*s->blmotion));
 +    s->edge_emu_buffer_base = av_malloc((w+64)*MAX_BLOCKSIZE);
 +
 +    s->mctmp     = av_malloc((w+64+MAX_BLOCKSIZE) * (h+MAX_BLOCKSIZE) * sizeof(*s->mctmp));
 +    s->mcscratch = av_malloc((w+64)*MAX_BLOCKSIZE);
 +
 +    if (!s->sbsplit || !s->blmotion || !s->mctmp || !s->mcscratch)
 +        return AVERROR(ENOMEM);
 +    return 0;
 +}
 +
 +static void free_sequence_buffers(DiracContext *s)
 +{
 +    int i, j, k;
 +
 +    for (i = 0; i < MAX_FRAMES; i++) {
 +        if (s->all_frames[i].avframe.data[0]) {
 +            av_frame_unref(&s->all_frames[i].avframe);
 +            memset(s->all_frames[i].interpolated, 0, sizeof(s->all_frames[i].interpolated));
 +        }
 +
 +        for (j = 0; j < 3; j++)
 +            for (k = 1; k < 4; k++)
 +                av_freep(&s->all_frames[i].hpel_base[j][k]);
 +    }
 +
 +    memset(s->ref_frames, 0, sizeof(s->ref_frames));
 +    memset(s->delay_frames, 0, sizeof(s->delay_frames));
 +
 +    for (i = 0; i < 3; i++) {
 +        av_freep(&s->plane[i].idwt_buf_base);
 +        av_freep(&s->plane[i].idwt_tmp);
 +    }
 +
 +    av_freep(&s->sbsplit);
 +    av_freep(&s->blmotion);
 +    av_freep(&s->edge_emu_buffer_base);
 +
 +    av_freep(&s->mctmp);
 +    av_freep(&s->mcscratch);
 +}
 +
 +static av_cold int dirac_decode_init(AVCodecContext *avctx)
 +{
 +    DiracContext *s = avctx->priv_data;
 +    s->avctx = avctx;
 +    s->frame_number = -1;
 +
 +    if (avctx->flags&CODEC_FLAG_EMU_EDGE) {
 +        av_log(avctx, AV_LOG_ERROR, "Edge emulation not supported!\n");
 +        return AVERROR_PATCHWELCOME;
 +    }
 +
 +    ff_dsputil_init(&s->dsp, avctx);
 +    ff_diracdsp_init(&s->diracdsp);
 +
 +    return 0;
 +}
 +
 +static void dirac_decode_flush(AVCodecContext *avctx)
 +{
 +    DiracContext *s = avctx->priv_data;
 +    free_sequence_buffers(s);
 +    s->seen_sequence_header = 0;
 +    s->frame_number = -1;
 +}
 +
 +static av_cold int dirac_decode_end(AVCodecContext *avctx)
 +{
 +    dirac_decode_flush(avctx);
 +    return 0;
 +}
 +
 +#define SIGN_CTX(x) (CTX_SIGN_ZERO + ((x) > 0) - ((x) < 0))
 +
 +static inline void coeff_unpack_arith(DiracArith *c, int qfactor, int qoffset,
 +                                      SubBand *b, IDWTELEM *buf, int x, int y)
 +{
 +    int coeff, sign;
 +    int sign_pred = 0;
 +    int pred_ctx = CTX_ZPZN_F1;
 +
 +    /* Check if the parent subband has a 0 in the corresponding position */
 +    if (b->parent)
 +        pred_ctx += !!b->parent->ibuf[b->parent->stride * (y>>1) + (x>>1)] << 1;
 +
 +    if (b->orientation == subband_hl)
 +        sign_pred = buf[-b->stride];
 +
 +    /* Determine if the pixel has only zeros in its neighbourhood */
 +    if (x) {
 +        pred_ctx += !(buf[-1] | buf[-b->stride] | buf[-1-b->stride]);
 +        if (b->orientation == subband_lh)
 +            sign_pred = buf[-1];
 +    } else {
 +        pred_ctx += !buf[-b->stride];
 +    }
 +
 +    coeff = dirac_get_arith_uint(c, pred_ctx, CTX_COEFF_DATA);
 +    if (coeff) {
 +        coeff = (coeff * qfactor + qoffset + 2) >> 2;
 +        sign  = dirac_get_arith_bit(c, SIGN_CTX(sign_pred));
 +        coeff = (coeff ^ -sign) + sign;
 +    }
 +    *buf = coeff;
 +}
 +
 +static inline int coeff_unpack_golomb(GetBitContext *gb, int qfactor, int qoffset)
 +{
 +    int sign, coeff;
 +
 +    coeff = svq3_get_ue_golomb(gb);
 +    if (coeff) {
 +        coeff = (coeff * qfactor + qoffset + 2) >> 2;
 +        sign  = get_bits1(gb);
 +        coeff = (coeff ^ -sign) + sign;
 +    }
 +    return coeff;
 +}
 +
 +/**
 + * Decode the coeffs in the rectangle defined by left, right, top, bottom
 + * [DIRAC_STD] 13.4.3.2 Codeblock unpacking loop. codeblock()
 + */
 +static inline void codeblock(DiracContext *s, SubBand *b,
 +                             GetBitContext *gb, DiracArith *c,
 +                             int left, int right, int top, int bottom,
 +                             int blockcnt_one, int is_arith)
 +{
 +    int x, y, zero_block;
 +    int qoffset, qfactor;
 +    IDWTELEM *buf;
 +
 +    /* check for any coded coefficients in this codeblock */
 +    if (!blockcnt_one) {
 +        if (is_arith)
 +            zero_block = dirac_get_arith_bit(c, CTX_ZERO_BLOCK);
 +        else
 +            zero_block = get_bits1(gb);
 +
 +        if (zero_block)
 +            return;
 +    }
 +
 +    if (s->codeblock_mode && !(s->old_delta_quant && blockcnt_one)) {
 +        int quant = b->quant;
 +        if (is_arith)
 +            quant += dirac_get_arith_int(c, CTX_DELTA_Q_F, CTX_DELTA_Q_DATA);
 +        else
 +            quant += dirac_get_se_golomb(gb);
 +        if (quant < 0) {
 +            av_log(s->avctx, AV_LOG_ERROR, "Invalid quant\n");
 +            return;
 +        }
 +        b->quant = quant;
 +    }
 +
 +    b->quant = FFMIN(b->quant, MAX_QUANT);
 +
 +    qfactor = qscale_tab[b->quant];
 +    /* TODO: context pointer? */
 +    if (!s->num_refs)
 +        qoffset = qoffset_intra_tab[b->quant];
 +    else
 +        qoffset = qoffset_inter_tab[b->quant];
 +
 +    buf = b->ibuf + top * b->stride;
 +    for (y = top; y < bottom; y++) {
 +        for (x = left; x < right; x++) {
 +            /* [DIRAC_STD] 13.4.4 Subband coefficients. coeff_unpack() */
 +            if (is_arith)
 +                coeff_unpack_arith(c, qfactor, qoffset, b, buf+x, x, y);
 +            else
 +                buf[x] = coeff_unpack_golomb(gb, qfactor, qoffset);
 +        }
 +        buf += b->stride;
 +    }
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 13.3 intra_dc_prediction(band)
 + */
 +static inline void intra_dc_prediction(SubBand *b)
 +{
 +    IDWTELEM *buf = b->ibuf;
 +    int x, y;
 +
 +    for (x = 1; x < b->width; x++)
 +        buf[x] += buf[x-1];
 +    buf += b->stride;
 +
 +    for (y = 1; y < b->height; y++) {
 +        buf[0] += buf[-b->stride];
 +
 +        for (x = 1; x < b->width; x++) {
 +            int pred = buf[x - 1] + buf[x - b->stride] + buf[x - b->stride-1];
 +            buf[x]  += divide3(pred);
 +        }
 +        buf += b->stride;
 +    }
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 13.4.2 Non-skipped subbands.  subband_coeffs()
 + */
 +static av_always_inline void decode_subband_internal(DiracContext *s, SubBand *b, int is_arith)
 +{
 +    int cb_x, cb_y, left, right, top, bottom;
 +    DiracArith c;
 +    GetBitContext gb;
 +    int cb_width  = s->codeblock[b->level + (b->orientation != subband_ll)].width;
 +    int cb_height = s->codeblock[b->level + (b->orientation != subband_ll)].height;
 +    int blockcnt_one = (cb_width + cb_height) == 2;
 +
 +    if (!b->length)
 +        return;
 +
 +    init_get_bits8(&gb, b->coeff_data, b->length);
 +
 +    if (is_arith)
 +        ff_dirac_init_arith_decoder(&c, &gb, b->length);
 +
 +    top = 0;
 +    for (cb_y = 0; cb_y < cb_height; cb_y++) {
 +        bottom = (b->height * (cb_y+1)) / cb_height;
 +        left = 0;
 +        for (cb_x = 0; cb_x < cb_width; cb_x++) {
 +            right = (b->width * (cb_x+1)) / cb_width;
 +            codeblock(s, b, &gb, &c, left, right, top, bottom, blockcnt_one, is_arith);
 +            left = right;
 +        }
 +        top = bottom;
 +    }
 +
 +    if (b->orientation == subband_ll && s->num_refs == 0)
 +        intra_dc_prediction(b);
 +}
 +
 +static int decode_subband_arith(AVCodecContext *avctx, void *b)
 +{
 +    DiracContext *s = avctx->priv_data;
 +    decode_subband_internal(s, b, 1);
 +    return 0;
 +}
 +
 +static int decode_subband_golomb(AVCodecContext *avctx, void *arg)
 +{
 +    DiracContext *s = avctx->priv_data;
 +    SubBand **b     = arg;
 +    decode_subband_internal(s, *b, 0);
 +    return 0;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * [DIRAC_STD] 13.4.1 core_transform_data()
 + */
 +static void decode_component(DiracContext *s, int comp)
 +{
 +    AVCodecContext *avctx = s->avctx;
 +    SubBand *bands[3*MAX_DWT_LEVELS+1];
 +    enum dirac_subband orientation;
 +    int level, num_bands = 0;
 +
 +    /* Unpack all subbands at all levels. */
 +    for (level = 0; level < s->wavelet_depth; level++) {
 +        for (orientation = !!level; orientation < 4; orientation++) {
 +            SubBand *b = &s->plane[comp].band[level][orientation];
 +            bands[num_bands++] = b;
 +
 +            align_get_bits(&s->gb);
 +            /* [DIRAC_STD] 13.4.2 subband() */
 +            b->length = svq3_get_ue_golomb(&s->gb);
 +            if (b->length) {
 +                b->quant = svq3_get_ue_golomb(&s->gb);
 +                align_get_bits(&s->gb);
 +                b->coeff_data = s->gb.buffer + get_bits_count(&s->gb)/8;
 +                b->length = FFMIN(b->length, FFMAX(get_bits_left(&s->gb)/8, 0));
 +                skip_bits_long(&s->gb, b->length*8);
 +            }
 +        }
 +        /* arithmetic coding has inter-level dependencies, so we can only execute one level at a time */
 +        if (s->is_arith)
 +            avctx->execute(avctx, decode_subband_arith, &s->plane[comp].band[level][!!level],
 +                           NULL, 4-!!level, sizeof(SubBand));
 +    }
 +    /* golomb coding has no inter-level dependencies, so we can execute all subbands in parallel */
 +    if (!s->is_arith)
 +        avctx->execute(avctx, decode_subband_golomb, bands, NULL, num_bands, sizeof(SubBand*));
 +}
 +
 +/* [DIRAC_STD] 13.5.5.2 Luma slice subband data. luma_slice_band(level,orient,sx,sy) --> if b2 == NULL */
 +/* [DIRAC_STD] 13.5.5.3 Chroma slice subband data. chroma_slice_band(level,orient,sx,sy) --> if b2 != NULL */
 +static void lowdelay_subband(DiracContext *s, GetBitContext *gb, int quant,
 +                             int slice_x, int slice_y, int bits_end,
 +                             SubBand *b1, SubBand *b2)
 +{
 +    int left   = b1->width  * slice_x    / s->lowdelay.num_x;
 +    int right  = b1->width  *(slice_x+1) / s->lowdelay.num_x;
 +    int top    = b1->height * slice_y    / s->lowdelay.num_y;
 +    int bottom = b1->height *(slice_y+1) / s->lowdelay.num_y;
 +
 +    int qfactor = qscale_tab[FFMIN(quant, MAX_QUANT)];
 +    int qoffset = qoffset_intra_tab[FFMIN(quant, MAX_QUANT)];
 +
 +    IDWTELEM *buf1 =      b1->ibuf + top * b1->stride;
 +    IDWTELEM *buf2 = b2 ? b2->ibuf + top * b2->stride : NULL;
 +    int x, y;
 +    /* we have to constantly check for overread since the spec explictly
 +       requires this, with the meaning that all remaining coeffs are set to 0 */
 +    if (get_bits_count(gb) >= bits_end)
 +        return;
 +
 +    for (y = top; y < bottom; y++) {
 +        for (x = left; x < right; x++) {
 +            buf1[x] = coeff_unpack_golomb(gb, qfactor, qoffset);
 +            if (get_bits_count(gb) >= bits_end)
 +                return;
 +            if (buf2) {
 +                buf2[x] = coeff_unpack_golomb(gb, qfactor, qoffset);
 +                if (get_bits_count(gb) >= bits_end)
 +                    return;
 +            }
 +        }
 +        buf1 += b1->stride;
 +        if (buf2)
 +            buf2 += b2->stride;
 +    }
 +}
 +
 +struct lowdelay_slice {
 +    GetBitContext gb;
 +    int slice_x;
 +    int slice_y;
 +    int bytes;
 +};
 +
 +
 +/**
 + * Dirac Specification ->
 + * 13.5.2 Slices. slice(sx,sy)
 + */
 +static int decode_lowdelay_slice(AVCodecContext *avctx, void *arg)
 +{
 +    DiracContext *s = avctx->priv_data;
 +    struct lowdelay_slice *slice = arg;
 +    GetBitContext *gb = &slice->gb;
 +    enum dirac_subband orientation;
 +    int level, quant, chroma_bits, chroma_end;
 +
 +    int quant_base  = get_bits(gb, 7); /*[DIRAC_STD] qindex */
 +    int length_bits = av_log2(8 * slice->bytes)+1;
 +    int luma_bits   = get_bits_long(gb, length_bits);
 +    int luma_end    = get_bits_count(gb) + FFMIN(luma_bits, get_bits_left(gb));
 +
 +    /* [DIRAC_STD] 13.5.5.2 luma_slice_band */
 +    for (level = 0; level < s->wavelet_depth; level++)
 +        for (orientation = !!level; orientation < 4; orientation++) {
 +            quant = FFMAX(quant_base - s->lowdelay.quant[level][orientation], 0);
 +            lowdelay_subband(s, gb, quant, slice->slice_x, slice->slice_y, luma_end,
 +                             &s->plane[0].band[level][orientation], NULL);
 +        }
 +
 +    /* consume any unused bits from luma */
 +    skip_bits_long(gb, get_bits_count(gb) - luma_end);
 +
 +    chroma_bits = 8*slice->bytes - 7 - length_bits - luma_bits;
 +    chroma_end  = get_bits_count(gb) + FFMIN(chroma_bits, get_bits_left(gb));
 +    /* [DIRAC_STD] 13.5.5.3 chroma_slice_band */
 +    for (level = 0; level < s->wavelet_depth; level++)
 +        for (orientation = !!level; orientation < 4; orientation++) {
 +            quant = FFMAX(quant_base - s->lowdelay.quant[level][orientation], 0);
 +            lowdelay_subband(s, gb, quant, slice->slice_x, slice->slice_y, chroma_end,
 +                             &s->plane[1].band[level][orientation],
 +                             &s->plane[2].band[level][orientation]);
 +        }
 +
 +    return 0;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 13.5.1 low_delay_transform_data()
 + */
 +static void decode_lowdelay(DiracContext *s)
 +{
 +    AVCodecContext *avctx = s->avctx;
 +    int slice_x, slice_y, bytes, bufsize;
 +    const uint8_t *buf;
 +    struct lowdelay_slice *slices;
 +    int slice_num = 0;
 +
 +    slices = av_mallocz(s->lowdelay.num_x * s->lowdelay.num_y * sizeof(struct lowdelay_slice));
 +
 +    align_get_bits(&s->gb);
 +    /*[DIRAC_STD] 13.5.2 Slices. slice(sx,sy) */
 +    buf = s->gb.buffer + get_bits_count(&s->gb)/8;
 +    bufsize = get_bits_left(&s->gb);
 +
 +    for (slice_y = 0; bufsize > 0 && slice_y < s->lowdelay.num_y; slice_y++)
 +        for (slice_x = 0; bufsize > 0 && slice_x < s->lowdelay.num_x; slice_x++) {
 +            bytes = (slice_num+1) * s->lowdelay.bytes.num / s->lowdelay.bytes.den
 +                - slice_num    * s->lowdelay.bytes.num / s->lowdelay.bytes.den;
 +
 +            slices[slice_num].bytes   = bytes;
 +            slices[slice_num].slice_x = slice_x;
 +            slices[slice_num].slice_y = slice_y;
 +            init_get_bits(&slices[slice_num].gb, buf, bufsize);
 +            slice_num++;
 +
 +            buf     += bytes;
 +            bufsize -= bytes*8;
 +        }
 +
 +    avctx->execute(avctx, decode_lowdelay_slice, slices, NULL, slice_num,
 +                   sizeof(struct lowdelay_slice)); /* [DIRAC_STD] 13.5.2 Slices */
 +    intra_dc_prediction(&s->plane[0].band[0][0]);  /* [DIRAC_STD] 13.3 intra_dc_prediction() */
 +    intra_dc_prediction(&s->plane[1].band[0][0]);  /* [DIRAC_STD] 13.3 intra_dc_prediction() */
 +    intra_dc_prediction(&s->plane[2].band[0][0]);  /* [DIRAC_STD] 13.3 intra_dc_prediction() */
 +    av_free(slices);
 +}
 +
 +static void init_planes(DiracContext *s)
 +{
 +    int i, w, h, level, orientation;
 +
 +    for (i = 0; i < 3; i++) {
 +        Plane *p = &s->plane[i];
 +
 +        p->width       = s->source.width  >> (i ? s->chroma_x_shift : 0);
 +        p->height      = s->source.height >> (i ? s->chroma_y_shift : 0);
 +        p->idwt_width  = w = CALC_PADDING(p->width , s->wavelet_depth);
 +        p->idwt_height = h = CALC_PADDING(p->height, s->wavelet_depth);
 +        p->idwt_stride = FFALIGN(p->idwt_width, 8);
 +
 +        for (level = s->wavelet_depth-1; level >= 0; level--) {
 +            w = w>>1;
 +            h = h>>1;
 +            for (orientation = !!level; orientation < 4; orientation++) {
 +                SubBand *b = &p->band[level][orientation];
 +
 +                b->ibuf   = p->idwt_buf;
 +                b->level  = level;
 +                b->stride = p->idwt_stride << (s->wavelet_depth - level);
 +                b->width  = w;
 +                b->height = h;
 +                b->orientation = orientation;
 +
 +                if (orientation & 1)
 +                    b->ibuf += w;
 +                if (orientation > 1)
 +                    b->ibuf += b->stride>>1;
 +
 +                if (level)
 +                    b->parent = &p->band[level-1][orientation];
 +            }
 +        }
 +
 +        if (i > 0) {
 +            p->xblen = s->plane[0].xblen >> s->chroma_x_shift;
 +            p->yblen = s->plane[0].yblen >> s->chroma_y_shift;
 +            p->xbsep = s->plane[0].xbsep >> s->chroma_x_shift;
 +            p->ybsep = s->plane[0].ybsep >> s->chroma_y_shift;
 +        }
 +
 +        p->xoffset = (p->xblen - p->xbsep)/2;
 +        p->yoffset = (p->yblen - p->ybsep)/2;
 +    }
 +}
 +
 +/**
 + * Unpack the motion compensation parameters
 + * Dirac Specification ->
 + * 11.2 Picture prediction data. picture_prediction()
 + */
 +static int dirac_unpack_prediction_parameters(DiracContext *s)
 +{
 +    static const uint8_t default_blen[] = { 4, 12, 16, 24 };
 +    static const uint8_t default_bsep[] = { 4,  8, 12, 16 };
 +
 +    GetBitContext *gb = &s->gb;
 +    unsigned idx, ref;
 +
 +    align_get_bits(gb);
 +    /* [DIRAC_STD] 11.2.2 Block parameters. block_parameters() */
 +    /* Luma and Chroma are equal. 11.2.3 */
 +    idx = svq3_get_ue_golomb(gb); /* [DIRAC_STD] index */
 +
 +    if (idx > 4) {
 +        av_log(s->avctx, AV_LOG_ERROR, "Block prediction index too high\n");
 +        return -1;
 +    }
 +
 +    if (idx == 0) {
 +        s->plane[0].xblen = svq3_get_ue_golomb(gb);
 +        s->plane[0].yblen = svq3_get_ue_golomb(gb);
 +        s->plane[0].xbsep = svq3_get_ue_golomb(gb);
 +        s->plane[0].ybsep = svq3_get_ue_golomb(gb);
 +    } else {
 +        /*[DIRAC_STD] preset_block_params(index). Table 11.1 */
 +        s->plane[0].xblen = default_blen[idx-1];
 +        s->plane[0].yblen = default_blen[idx-1];
 +        s->plane[0].xbsep = default_bsep[idx-1];
 +        s->plane[0].ybsep = default_bsep[idx-1];
 +    }
 +    /*[DIRAC_STD] 11.2.4 motion_data_dimensions()
 +      Calculated in function dirac_unpack_block_motion_data */
 +
 +    if (!s->plane[0].xbsep || !s->plane[0].ybsep || s->plane[0].xbsep < s->plane[0].xblen/2 || s->plane[0].ybsep < s->plane[0].yblen/2) {
 +        av_log(s->avctx, AV_LOG_ERROR, "Block separation too small\n");
 +        return -1;
 +    }
 +    if (s->plane[0].xbsep > s->plane[0].xblen || s->plane[0].ybsep > s->plane[0].yblen) {
 +        av_log(s->avctx, AV_LOG_ERROR, "Block separation greater than size\n");
 +        return -1;
 +    }
 +    if (FFMAX(s->plane[0].xblen, s->plane[0].yblen) > MAX_BLOCKSIZE) {
 +        av_log(s->avctx, AV_LOG_ERROR, "Unsupported large block size\n");
 +        return -1;
 +    }
 +
 +    /*[DIRAC_STD] 11.2.5 Motion vector precision. motion_vector_precision()
 +      Read motion vector precision */
 +    s->mv_precision = svq3_get_ue_golomb(gb);
 +    if (s->mv_precision > 3) {
 +        av_log(s->avctx, AV_LOG_ERROR, "MV precision finer than eighth-pel\n");
 +        return -1;
 +    }
 +
 +    /*[DIRAC_STD] 11.2.6 Global motion. global_motion()
 +      Read the global motion compensation parameters */
 +    s->globalmc_flag = get_bits1(gb);
 +    if (s->globalmc_flag) {
 +        memset(s->globalmc, 0, sizeof(s->globalmc));
 +        /* [DIRAC_STD] pan_tilt(gparams) */
 +        for (ref = 0; ref < s->num_refs; ref++) {
 +            if (get_bits1(gb)) {
 +                s->globalmc[ref].pan_tilt[0] = dirac_get_se_golomb(gb);
 +                s->globalmc[ref].pan_tilt[1] = dirac_get_se_golomb(gb);
 +            }
 +            /* [DIRAC_STD] zoom_rotate_shear(gparams)
 +               zoom/rotation/shear parameters */
 +            if (get_bits1(gb)) {
 +                s->globalmc[ref].zrs_exp   = svq3_get_ue_golomb(gb);
 +                s->globalmc[ref].zrs[0][0] = dirac_get_se_golomb(gb);
 +                s->globalmc[ref].zrs[0][1] = dirac_get_se_golomb(gb);
 +                s->globalmc[ref].zrs[1][0] = dirac_get_se_golomb(gb);
 +                s->globalmc[ref].zrs[1][1] = dirac_get_se_golomb(gb);
 +            } else {
 +                s->globalmc[ref].zrs[0][0] = 1;
 +                s->globalmc[ref].zrs[1][1] = 1;
 +            }
 +            /* [DIRAC_STD] perspective(gparams) */
 +            if (get_bits1(gb)) {
 +                s->globalmc[ref].perspective_exp = svq3_get_ue_golomb(gb);
 +                s->globalmc[ref].perspective[0]  = dirac_get_se_golomb(gb);
 +                s->globalmc[ref].perspective[1]  = dirac_get_se_golomb(gb);
 +            }
 +        }
 +    }
 +
 +    /*[DIRAC_STD] 11.2.7 Picture prediction mode. prediction_mode()
 +      Picture prediction mode, not currently used. */
 +    if (svq3_get_ue_golomb(gb)) {
 +        av_log(s->avctx, AV_LOG_ERROR, "Unknown picture prediction mode\n");
 +        return -1;
 +    }
 +
 +    /* [DIRAC_STD] 11.2.8 Reference picture weight. reference_picture_weights()
 +       just data read, weight calculation will be done later on. */
 +    s->weight_log2denom = 1;
 +    s->weight[0]        = 1;
 +    s->weight[1]        = 1;
 +
 +    if (get_bits1(gb)) {
 +        s->weight_log2denom = svq3_get_ue_golomb(gb);
 +        s->weight[0] = dirac_get_se_golomb(gb);
 +        if (s->num_refs == 2)
 +            s->weight[1] = dirac_get_se_golomb(gb);
 +    }
 +    return 0;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 11.3 Wavelet transform data. wavelet_transform()
 + */
 +static int dirac_unpack_idwt_params(DiracContext *s)
 +{
 +    GetBitContext *gb = &s->gb;
 +    int i, level;
 +    unsigned tmp;
 +
 +#define CHECKEDREAD(dst, cond, errmsg) \
 +    tmp = svq3_get_ue_golomb(gb); \
 +    if (cond) { \
 +        av_log(s->avctx, AV_LOG_ERROR, errmsg); \
 +        return -1; \
 +    }\
 +    dst = tmp;
 +
 +    align_get_bits(gb);
 +
 +    s->zero_res = s->num_refs ? get_bits1(gb) : 0;
 +    if (s->zero_res)
 +        return 0;
 +
 +    /*[DIRAC_STD] 11.3.1 Transform parameters. transform_parameters() */
 +    CHECKEDREAD(s->wavelet_idx, tmp > 6, "wavelet_idx is too big\n")
 +
 +    CHECKEDREAD(s->wavelet_depth, tmp > MAX_DWT_LEVELS || tmp < 1, "invalid number of DWT decompositions\n")
 +
 +    if (!s->low_delay) {
 +        /* Codeblock parameters (core syntax only) */
 +        if (get_bits1(gb)) {
 +            for (i = 0; i <= s->wavelet_depth; i++) {
 +                CHECKEDREAD(s->codeblock[i].width , tmp < 1, "codeblock width invalid\n")
 +                CHECKEDREAD(s->codeblock[i].height, tmp < 1, "codeblock height invalid\n")
 +            }
 +
 +            CHECKEDREAD(s->codeblock_mode, tmp > 1, "unknown codeblock mode\n")
 +        } else
 +            for (i = 0; i <= s->wavelet_depth; i++)
 +                s->codeblock[i].width = s->codeblock[i].height = 1;
 +    } else {
 +        /* Slice parameters + quantization matrix*/
 +        /*[DIRAC_STD] 11.3.4 Slice coding Parameters (low delay syntax only). slice_parameters() */
 +        s->lowdelay.num_x     = svq3_get_ue_golomb(gb);
 +        s->lowdelay.num_y     = svq3_get_ue_golomb(gb);
 +        s->lowdelay.bytes.num = svq3_get_ue_golomb(gb);
 +        s->lowdelay.bytes.den = svq3_get_ue_golomb(gb);
 +
 +        if (s->lowdelay.bytes.den <= 0) {
 +            av_log(s->avctx,AV_LOG_ERROR,"Invalid lowdelay.bytes.den\n");
 +            return AVERROR_INVALIDDATA;
 +        }
 +
 +        /* [DIRAC_STD] 11.3.5 Quantisation matrices (low-delay syntax). quant_matrix() */
 +        if (get_bits1(gb)) {
 +            av_log(s->avctx,AV_LOG_DEBUG,"Low Delay: Has Custom Quantization Matrix!\n");
 +            /* custom quantization matrix */
 +            s->lowdelay.quant[0][0] = svq3_get_ue_golomb(gb);
 +            for (level = 0; level < s->wavelet_depth; level++) {
 +                s->lowdelay.quant[level][1] = svq3_get_ue_golomb(gb);
 +                s->lowdelay.quant[level][2] = svq3_get_ue_golomb(gb);
 +                s->lowdelay.quant[level][3] = svq3_get_ue_golomb(gb);
 +            }
 +        } else {
 +            if (s->wavelet_depth > 4) {
 +                av_log(s->avctx,AV_LOG_ERROR,"Mandatory custom low delay matrix missing for depth %d\n", s->wavelet_depth);
 +                return AVERROR_INVALIDDATA;
 +            }
 +            /* default quantization matrix */
 +            for (level = 0; level < s->wavelet_depth; level++)
 +                for (i = 0; i < 4; i++) {
 +                    s->lowdelay.quant[level][i] = default_qmat[s->wavelet_idx][level][i];
 +                    /* haar with no shift differs for different depths */
 +                    if (s->wavelet_idx == 3)
 +                        s->lowdelay.quant[level][i] += 4*(s->wavelet_depth-1 - level);
 +                }
 +        }
 +    }
 +    return 0;
 +}
 +
 +static inline int pred_sbsplit(uint8_t *sbsplit, int stride, int x, int y)
 +{
 +    static const uint8_t avgsplit[7] = { 0, 0, 1, 1, 1, 2, 2 };
 +
 +    if (!(x|y))
 +        return 0;
 +    else if (!y)
 +        return sbsplit[-1];
 +    else if (!x)
 +        return sbsplit[-stride];
 +
 +    return avgsplit[sbsplit[-1] + sbsplit[-stride] + sbsplit[-stride-1]];
 +}
 +
 +static inline int pred_block_mode(DiracBlock *block, int stride, int x, int y, int refmask)
 +{
 +    int pred;
 +
 +    if (!(x|y))
 +        return 0;
 +    else if (!y)
 +        return block[-1].ref & refmask;
 +    else if (!x)
 +        return block[-stride].ref & refmask;
 +
 +    /* return the majority */
 +    pred = (block[-1].ref & refmask) + (block[-stride].ref & refmask) + (block[-stride-1].ref & refmask);
 +    return (pred >> 1) & refmask;
 +}
 +
 +static inline void pred_block_dc(DiracBlock *block, int stride, int x, int y)
 +{
 +    int i, n = 0;
 +
 +    memset(block->u.dc, 0, sizeof(block->u.dc));
 +
 +    if (x && !(block[-1].ref & 3)) {
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] += block[-1].u.dc[i];
 +        n++;
 +    }
 +
 +    if (y && !(block[-stride].ref & 3)) {
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] += block[-stride].u.dc[i];
 +        n++;
 +    }
 +
 +    if (x && y && !(block[-1-stride].ref & 3)) {
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] += block[-1-stride].u.dc[i];
 +        n++;
 +    }
 +
 +    if (n == 2) {
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] = (block->u.dc[i]+1)>>1;
 +    } else if (n == 3) {
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] = divide3(block->u.dc[i]);
 +    }
 +}
 +
 +static inline void pred_mv(DiracBlock *block, int stride, int x, int y, int ref)
 +{
 +    int16_t *pred[3];
 +    int refmask = ref+1;
 +    int mask = refmask | DIRAC_REF_MASK_GLOBAL; /*  exclude gmc blocks */
 +    int n = 0;
 +
 +    if (x && (block[-1].ref & mask) == refmask)
 +        pred[n++] = block[-1].u.mv[ref];
 +
 +    if (y && (block[-stride].ref & mask) == refmask)
 +        pred[n++] = block[-stride].u.mv[ref];
 +
 +    if (x && y && (block[-stride-1].ref & mask) == refmask)
 +        pred[n++] = block[-stride-1].u.mv[ref];
 +
 +    switch (n) {
 +    case 0:
 +        block->u.mv[ref][0] = 0;
 +        block->u.mv[ref][1] = 0;
 +        break;
 +    case 1:
 +        block->u.mv[ref][0] = pred[0][0];
 +        block->u.mv[ref][1] = pred[0][1];
 +        break;
 +    case 2:
 +        block->u.mv[ref][0] = (pred[0][0] + pred[1][0] + 1) >> 1;
 +        block->u.mv[ref][1] = (pred[0][1] + pred[1][1] + 1) >> 1;
 +        break;
 +    case 3:
 +        block->u.mv[ref][0] = mid_pred(pred[0][0], pred[1][0], pred[2][0]);
 +        block->u.mv[ref][1] = mid_pred(pred[0][1], pred[1][1], pred[2][1]);
 +        break;
 +    }
 +}
 +
 +static void global_mv(DiracContext *s, DiracBlock *block, int x, int y, int ref)
 +{
 +    int ez      = s->globalmc[ref].zrs_exp;
 +    int ep      = s->globalmc[ref].perspective_exp;
 +    int (*A)[2] = s->globalmc[ref].zrs;
 +    int *b      = s->globalmc[ref].pan_tilt;
 +    int *c      = s->globalmc[ref].perspective;
 +
 +    int m       = (1<<ep) - (c[0]*x + c[1]*y);
 +    int mx      = m * ((A[0][0] * x + A[0][1]*y) + (1<<ez) * b[0]);
 +    int my      = m * ((A[1][0] * x + A[1][1]*y) + (1<<ez) * b[1]);
 +
 +    block->u.mv[ref][0] = (mx + (1<<(ez+ep))) >> (ez+ep);
 +    block->u.mv[ref][1] = (my + (1<<(ez+ep))) >> (ez+ep);
 +}
 +
 +static void decode_block_params(DiracContext *s, DiracArith arith[8], DiracBlock *block,
 +                                int stride, int x, int y)
 +{
 +    int i;
 +
 +    block->ref  = pred_block_mode(block, stride, x, y, DIRAC_REF_MASK_REF1);
 +    block->ref ^= dirac_get_arith_bit(arith, CTX_PMODE_REF1);
 +
 +    if (s->num_refs == 2) {
 +        block->ref |= pred_block_mode(block, stride, x, y, DIRAC_REF_MASK_REF2);
 +        block->ref ^= dirac_get_arith_bit(arith, CTX_PMODE_REF2) << 1;
 +    }
 +
 +    if (!block->ref) {
 +        pred_block_dc(block, stride, x, y);
 +        for (i = 0; i < 3; i++)
 +            block->u.dc[i] += dirac_get_arith_int(arith+1+i, CTX_DC_F1, CTX_DC_DATA);
 +        return;
 +    }
 +
 +    if (s->globalmc_flag) {
 +        block->ref |= pred_block_mode(block, stride, x, y, DIRAC_REF_MASK_GLOBAL);
 +        block->ref ^= dirac_get_arith_bit(arith, CTX_GLOBAL_BLOCK) << 2;
 +    }
 +
 +    for (i = 0; i < s->num_refs; i++)
 +        if (block->ref & (i+1)) {
 +            if (block->ref & DIRAC_REF_MASK_GLOBAL) {
 +                global_mv(s, block, x, y, i);
 +            } else {
 +                pred_mv(block, stride, x, y, i);
 +                block->u.mv[i][0] += dirac_get_arith_int(arith + 4 + 2 * i, CTX_MV_F1, CTX_MV_DATA);
 +                block->u.mv[i][1] += dirac_get_arith_int(arith + 5 + 2 * i, CTX_MV_F1, CTX_MV_DATA);
 +            }
 +        }
 +}
 +
 +/**
 + * Copies the current block to the other blocks covered by the current superblock split mode
 + */
 +static void propagate_block_data(DiracBlock *block, int stride, int size)
 +{
 +    int x, y;
 +    DiracBlock *dst = block;
 +
 +    for (x = 1; x < size; x++)
 +        dst[x] = *block;
 +
 +    for (y = 1; y < size; y++) {
 +        dst += stride;
 +        for (x = 0; x < size; x++)
 +            dst[x] = *block;
 +    }
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 12. Block motion data syntax
 + */
 +static int dirac_unpack_block_motion_data(DiracContext *s)
 +{
 +    GetBitContext *gb = &s->gb;
 +    uint8_t *sbsplit = s->sbsplit;
 +    int i, x, y, q, p;
 +    DiracArith arith[8];
 +
 +    align_get_bits(gb);
 +
 +    /* [DIRAC_STD] 11.2.4 and 12.2.1 Number of blocks and superblocks */
 +    s->sbwidth  = DIVRNDUP(s->source.width,  4*s->plane[0].xbsep);
 +    s->sbheight = DIVRNDUP(s->source.height, 4*s->plane[0].ybsep);
 +    s->blwidth  = 4 * s->sbwidth;
 +    s->blheight = 4 * s->sbheight;
 +
 +    /* [DIRAC_STD] 12.3.1 Superblock splitting modes. superblock_split_modes()
 +       decode superblock split modes */
 +    ff_dirac_init_arith_decoder(arith, gb, svq3_get_ue_golomb(gb));     /* svq3_get_ue_golomb(gb) is the length */
 +    for (y = 0; y < s->sbheight; y++) {
 +        for (x = 0; x < s->sbwidth; x++) {
 +            unsigned int split  = dirac_get_arith_uint(arith, CTX_SB_F1, CTX_SB_DATA);
 +            if (split > 2)
 +                return -1;
 +            sbsplit[x] = (split + pred_sbsplit(sbsplit+x, s->sbwidth, x, y)) % 3;
 +        }
 +        sbsplit += s->sbwidth;
 +    }
 +
 +    /* setup arith decoding */
 +    ff_dirac_init_arith_decoder(arith, gb, svq3_get_ue_golomb(gb));
 +    for (i = 0; i < s->num_refs; i++) {
 +        ff_dirac_init_arith_decoder(arith + 4 + 2 * i, gb, svq3_get_ue_golomb(gb));
 +        ff_dirac_init_arith_decoder(arith + 5 + 2 * i, gb, svq3_get_ue_golomb(gb));
 +    }
 +    for (i = 0; i < 3; i++)
 +        ff_dirac_init_arith_decoder(arith+1+i, gb, svq3_get_ue_golomb(gb));
 +
 +    for (y = 0; y < s->sbheight; y++)
 +        for (x = 0; x < s->sbwidth; x++) {
 +            int blkcnt = 1 << s->sbsplit[y * s->sbwidth + x];
 +            int step   = 4 >> s->sbsplit[y * s->sbwidth + x];
 +
 +            for (q = 0; q < blkcnt; q++)
 +                for (p = 0; p < blkcnt; p++) {
 +                    int bx = 4 * x + p*step;
 +                    int by = 4 * y + q*step;
 +                    DiracBlock *block = &s->blmotion[by*s->blwidth + bx];
 +                    decode_block_params(s, arith, block, s->blwidth, bx, by);
 +                    propagate_block_data(block, s->blwidth, step);
 +                }
 +        }
 +
 +    return 0;
 +}
 +
 +static int weight(int i, int blen, int offset)
 +{
 +#define ROLLOFF(i) offset == 1 ? ((i) ? 5 : 3) :        \
 +    (1 + (6*(i) + offset - 1) / (2*offset - 1))
 +
 +    if (i < 2*offset)
 +        return ROLLOFF(i);
 +    else if (i > blen-1 - 2*offset)
 +        return ROLLOFF(blen-1 - i);
 +    return 8;
 +}
 +
 +static void init_obmc_weight_row(Plane *p, uint8_t *obmc_weight, int stride,
 +                                 int left, int right, int wy)
 +{
 +    int x;
 +    for (x = 0; left && x < p->xblen >> 1; x++)
 +        obmc_weight[x] = wy*8;
 +    for (; x < p->xblen >> right; x++)
 +        obmc_weight[x] = wy*weight(x, p->xblen, p->xoffset);
 +    for (; x < p->xblen; x++)
 +        obmc_weight[x] = wy*8;
 +    for (; x < stride; x++)
 +        obmc_weight[x] = 0;
 +}
 +
 +static void init_obmc_weight(Plane *p, uint8_t *obmc_weight, int stride,
 +                             int left, int right, int top, int bottom)
 +{
 +    int y;
 +    for (y = 0; top && y < p->yblen >> 1; y++) {
 +        init_obmc_weight_row(p, obmc_weight, stride, left, right, 8);
 +        obmc_weight += stride;
 +    }
 +    for (; y < p->yblen >> bottom; y++) {
 +        int wy = weight(y, p->yblen, p->yoffset);
 +        init_obmc_weight_row(p, obmc_weight, stride, left, right, wy);
 +        obmc_weight += stride;
 +    }
 +    for (; y < p->yblen; y++) {
 +        init_obmc_weight_row(p, obmc_weight, stride, left, right, 8);
 +        obmc_weight += stride;
 +    }
 +}
 +
 +static void init_obmc_weights(DiracContext *s, Plane *p, int by)
 +{
 +    int top = !by;
 +    int bottom = by == s->blheight-1;
 +
 +    /* don't bother re-initing for rows 2 to blheight-2, the weights don't change */
 +    if (top || bottom || by == 1) {
 +        init_obmc_weight(p, s->obmc_weight[0], MAX_BLOCKSIZE, 1, 0, top, bottom);
 +        init_obmc_weight(p, s->obmc_weight[1], MAX_BLOCKSIZE, 0, 0, top, bottom);
 +        init_obmc_weight(p, s->obmc_weight[2], MAX_BLOCKSIZE, 0, 1, top, bottom);
 +    }
 +}
 +
 +static const uint8_t epel_weights[4][4][4] = {
 +    {{ 16,  0,  0,  0 },
 +     { 12,  4,  0,  0 },
 +     {  8,  8,  0,  0 },
 +     {  4, 12,  0,  0 }},
 +    {{ 12,  0,  4,  0 },
 +     {  9,  3,  3,  1 },
 +     {  6,  6,  2,  2 },
 +     {  3,  9,  1,  3 }},
 +    {{  8,  0,  8,  0 },
 +     {  6,  2,  6,  2 },
 +     {  4,  4,  4,  4 },
 +     {  2,  6,  2,  6 }},
 +    {{  4,  0, 12,  0 },
 +     {  3,  1,  9,  3 },
 +     {  2,  2,  6,  6 },
 +     {  1,  3,  3,  9 }}
 +};
 +
 +/**
 + * For block x,y, determine which of the hpel planes to do bilinear
 + * interpolation from and set src[] to the location in each hpel plane
 + * to MC from.
 + *
 + * @return the index of the put_dirac_pixels_tab function to use
 + *  0 for 1 plane (fpel,hpel), 1 for 2 planes (qpel), 2 for 4 planes (qpel), and 3 for epel
 + */
 +static int mc_subpel(DiracContext *s, DiracBlock *block, const uint8_t *src[5],
 +                     int x, int y, int ref, int plane)
 +{
 +    Plane *p = &s->plane[plane];
 +    uint8_t **ref_hpel = s->ref_pics[ref]->hpel[plane];
 +    int motion_x = block->u.mv[ref][0];
 +    int motion_y = block->u.mv[ref][1];
 +    int mx, my, i, epel, nplanes = 0;
 +
 +    if (plane) {
 +        motion_x >>= s->chroma_x_shift;
 +        motion_y >>= s->chroma_y_shift;
 +    }
 +
 +    mx         = motion_x & ~(-1 << s->mv_precision);
 +    my         = motion_y & ~(-1 << s->mv_precision);
 +    motion_x >>= s->mv_precision;
 +    motion_y >>= s->mv_precision;
 +    /* normalize subpel coordinates to epel */
 +    /* TODO: template this function? */
 +    mx      <<= 3 - s->mv_precision;
 +    my      <<= 3 - s->mv_precision;
 +
 +    x += motion_x;
 +    y += motion_y;
 +    epel = (mx|my)&1;
 +
 +    /* hpel position */
 +    if (!((mx|my)&3)) {
 +        nplanes = 1;
 +        src[0] = ref_hpel[(my>>1)+(mx>>2)] + y*p->stride + x;
 +    } else {
 +        /* qpel or epel */
 +        nplanes = 4;
 +        for (i = 0; i < 4; i++)
 +            src[i] = ref_hpel[i] + y*p->stride + x;
 +
 +        /* if we're interpolating in the right/bottom halves, adjust the planes as needed
 +           we increment x/y because the edge changes for half of the pixels */
 +        if (mx > 4) {
 +            src[0] += 1;
 +            src[2] += 1;
 +            x++;
 +        }
 +        if (my > 4) {
 +            src[0] += p->stride;
 +            src[1] += p->stride;
 +            y++;
 +        }
 +
 +        /* hpel planes are:
 +           [0]: F  [1]: H
 +           [2]: V  [3]: C */
 +        if (!epel) {
 +            /* check if we really only need 2 planes since either mx or my is
 +               a hpel position. (epel weights of 0 handle this there) */
 +            if (!(mx&3)) {
 +                /* mx == 0: average [0] and [2]
 +                   mx == 4: average [1] and [3] */
 +                src[!mx] = src[2 + !!mx];
 +                nplanes = 2;
 +            } else if (!(my&3)) {
 +                src[0] = src[(my>>1)  ];
 +                src[1] = src[(my>>1)+1];
 +                nplanes = 2;
 +            }
 +        } else {
 +            /* adjust the ordering if needed so the weights work */
 +            if (mx > 4) {
 +                FFSWAP(const uint8_t *, src[0], src[1]);
 +                FFSWAP(const uint8_t *, src[2], src[3]);
 +            }
 +            if (my > 4) {
 +                FFSWAP(const uint8_t *, src[0], src[2]);
 +                FFSWAP(const uint8_t *, src[1], src[3]);
 +            }
 +            src[4] = epel_weights[my&3][mx&3];
 +        }
 +    }
 +
 +    /* fixme: v/h _edge_pos */
 +    if (x + p->xblen > p->width +EDGE_WIDTH/2 ||
 +        y + p->yblen > p->height+EDGE_WIDTH/2 ||
 +        x < 0 || y < 0) {
 +        for (i = 0; i < nplanes; i++) {
++            ff_emulated_edge_mc(s->edge_emu_buffer[i], src[i],
++                                p->stride, p->stride,
 +                                p->xblen, p->yblen, x, y,
 +                                p->width+EDGE_WIDTH/2, p->height+EDGE_WIDTH/2);
 +            src[i] = s->edge_emu_buffer[i];
 +        }
 +    }
 +    return (nplanes>>1) + epel;
 +}
 +
 +static void add_dc(uint16_t *dst, int dc, int stride,
 +                   uint8_t *obmc_weight, int xblen, int yblen)
 +{
 +    int x, y;
 +    dc += 128;
 +
 +    for (y = 0; y < yblen; y++) {
 +        for (x = 0; x < xblen; x += 2) {
 +            dst[x  ] += dc * obmc_weight[x  ];
 +            dst[x+1] += dc * obmc_weight[x+1];
 +        }
 +        dst          += stride;
 +        obmc_weight  += MAX_BLOCKSIZE;
 +    }
 +}
 +
 +static void block_mc(DiracContext *s, DiracBlock *block,
 +                     uint16_t *mctmp, uint8_t *obmc_weight,
 +                     int plane, int dstx, int dsty)
 +{
 +    Plane *p = &s->plane[plane];
 +    const uint8_t *src[5];
 +    int idx;
 +
 +    switch (block->ref&3) {
 +    case 0: /* DC */
 +        add_dc(mctmp, block->u.dc[plane], p->stride, obmc_weight, p->xblen, p->yblen);
 +        return;
 +    case 1:
 +    case 2:
 +        idx = mc_subpel(s, block, src, dstx, dsty, (block->ref&3)-1, plane);
 +        s->put_pixels_tab[idx](s->mcscratch, src, p->stride, p->yblen);
 +        if (s->weight_func)
 +            s->weight_func(s->mcscratch, p->stride, s->weight_log2denom,
 +                           s->weight[0] + s->weight[1], p->yblen);
 +        break;
 +    case 3:
 +        idx = mc_subpel(s, block, src, dstx, dsty, 0, plane);
 +        s->put_pixels_tab[idx](s->mcscratch, src, p->stride, p->yblen);
 +        idx = mc_subpel(s, block, src, dstx, dsty, 1, plane);
 +        if (s->biweight_func) {
 +            /* fixme: +32 is a quick hack */
 +            s->put_pixels_tab[idx](s->mcscratch + 32, src, p->stride, p->yblen);
 +            s->biweight_func(s->mcscratch, s->mcscratch+32, p->stride, s->weight_log2denom,
 +                             s->weight[0], s->weight[1], p->yblen);
 +        } else
 +            s->avg_pixels_tab[idx](s->mcscratch, src, p->stride, p->yblen);
 +        break;
 +    }
 +    s->add_obmc(mctmp, s->mcscratch, p->stride, obmc_weight, p->yblen);
 +}
 +
 +static void mc_row(DiracContext *s, DiracBlock *block, uint16_t *mctmp, int plane, int dsty)
 +{
 +    Plane *p = &s->plane[plane];
 +    int x, dstx = p->xbsep - p->xoffset;
 +
 +    block_mc(s, block, mctmp, s->obmc_weight[0], plane, -p->xoffset, dsty);
 +    mctmp += p->xbsep;
 +
 +    for (x = 1; x < s->blwidth-1; x++) {
 +        block_mc(s, block+x, mctmp, s->obmc_weight[1], plane, dstx, dsty);
 +        dstx  += p->xbsep;
 +        mctmp += p->xbsep;
 +    }
 +    block_mc(s, block+x, mctmp, s->obmc_weight[2], plane, dstx, dsty);
 +}
 +
 +static void select_dsp_funcs(DiracContext *s, int width, int height, int xblen, int yblen)
 +{
 +    int idx = 0;
 +    if (xblen > 8)
 +        idx = 1;
 +    if (xblen > 16)
 +        idx = 2;
 +
 +    memcpy(s->put_pixels_tab, s->diracdsp.put_dirac_pixels_tab[idx], sizeof(s->put_pixels_tab));
 +    memcpy(s->avg_pixels_tab, s->diracdsp.avg_dirac_pixels_tab[idx], sizeof(s->avg_pixels_tab));
 +    s->add_obmc = s->diracdsp.add_dirac_obmc[idx];
 +    if (s->weight_log2denom > 1 || s->weight[0] != 1 || s->weight[1] != 1) {
 +        s->weight_func   = s->diracdsp.weight_dirac_pixels_tab[idx];
 +        s->biweight_func = s->diracdsp.biweight_dirac_pixels_tab[idx];
 +    } else {
 +        s->weight_func   = NULL;
 +        s->biweight_func = NULL;
 +    }
 +}
 +
 +static void interpolate_refplane(DiracContext *s, DiracFrame *ref, int plane, int width, int height)
 +{
 +    /* chroma allocates an edge of 8 when subsampled
 +       which for 4:2:2 means an h edge of 16 and v edge of 8
 +       just use 8 for everything for the moment */
 +    int i, edge = EDGE_WIDTH/2;
 +
 +    ref->hpel[plane][0] = ref->avframe.data[plane];
 +    s->dsp.draw_edges(ref->hpel[plane][0], ref->avframe.linesize[plane], width, height, edge, edge, EDGE_TOP | EDGE_BOTTOM); /* EDGE_TOP | EDGE_BOTTOM values just copied to make it build, this needs to be ensured */
 +
 +    /* no need for hpel if we only have fpel vectors */
 +    if (!s->mv_precision)
 +        return;
 +
 +    for (i = 1; i < 4; i++) {
 +        if (!ref->hpel_base[plane][i])
 +            ref->hpel_base[plane][i] = av_malloc((height+2*edge) * ref->avframe.linesize[plane] + 32);
 +        /* we need to be 16-byte aligned even for chroma */
 +        ref->hpel[plane][i] = ref->hpel_base[plane][i] + edge*ref->avframe.linesize[plane] + 16;
 +    }
 +
 +    if (!ref->interpolated[plane]) {
 +        s->diracdsp.dirac_hpel_filter(ref->hpel[plane][1], ref->hpel[plane][2],
 +                                      ref->hpel[plane][3], ref->hpel[plane][0],
 +                                      ref->avframe.linesize[plane], width, height);
 +        s->dsp.draw_edges(ref->hpel[plane][1], ref->avframe.linesize[plane], width, height, edge, edge, EDGE_TOP | EDGE_BOTTOM);
 +        s->dsp.draw_edges(ref->hpel[plane][2], ref->avframe.linesize[plane], width, height, edge, edge, EDGE_TOP | EDGE_BOTTOM);
 +        s->dsp.draw_edges(ref->hpel[plane][3], ref->avframe.linesize[plane], width, height, edge, edge, EDGE_TOP | EDGE_BOTTOM);
 +    }
 +    ref->interpolated[plane] = 1;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 13.0 Transform data syntax. transform_data()
 + */
 +static int dirac_decode_frame_internal(DiracContext *s)
 +{
 +    DWTContext d;
 +    int y, i, comp, dsty;
 +
 +    if (s->low_delay) {
 +        /* [DIRAC_STD] 13.5.1 low_delay_transform_data() */
 +        for (comp = 0; comp < 3; comp++) {
 +            Plane *p = &s->plane[comp];
 +            memset(p->idwt_buf, 0, p->idwt_stride * p->idwt_height * sizeof(IDWTELEM));
 +        }
 +        if (!s->zero_res)
 +            decode_lowdelay(s);
 +    }
 +
 +    for (comp = 0; comp < 3; comp++) {
 +        Plane *p       = &s->plane[comp];
 +        uint8_t *frame = s->current_picture->avframe.data[comp];
 +
 +        /* FIXME: small resolutions */
 +        for (i = 0; i < 4; i++)
 +            s->edge_emu_buffer[i] = s->edge_emu_buffer_base + i*FFALIGN(p->width, 16);
 +
 +        if (!s->zero_res && !s->low_delay)
 +        {
 +            memset(p->idwt_buf, 0, p->idwt_stride * p->idwt_height * sizeof(IDWTELEM));
 +            decode_component(s, comp); /* [DIRAC_STD] 13.4.1 core_transform_data() */
 +        }
 +        if (ff_spatial_idwt_init2(&d, p->idwt_buf, p->idwt_width, p->idwt_height, p->idwt_stride,
 +                                  s->wavelet_idx+2, s->wavelet_depth, p->idwt_tmp))
 +            return -1;
 +
 +        if (!s->num_refs) { /* intra */
 +            for (y = 0; y < p->height; y += 16) {
 +                ff_spatial_idwt_slice2(&d, y+16); /* decode */
 +                s->diracdsp.put_signed_rect_clamped(frame + y*p->stride, p->stride,
 +                                                    p->idwt_buf + y*p->idwt_stride, p->idwt_stride, p->width, 16);
 +            }
 +        } else { /* inter */
 +            int rowheight = p->ybsep*p->stride;
 +
 +            select_dsp_funcs(s, p->width, p->height, p->xblen, p->yblen);
 +
 +            for (i = 0; i < s->num_refs; i++)
 +                interpolate_refplane(s, s->ref_pics[i], comp, p->width, p->height);
 +
 +            memset(s->mctmp, 0, 4*p->yoffset*p->stride);
 +
 +            dsty = -p->yoffset;
 +            for (y = 0; y < s->blheight; y++) {
 +                int h     = 0,
 +                    start = FFMAX(dsty, 0);
 +                uint16_t *mctmp    = s->mctmp + y*rowheight;
 +                DiracBlock *blocks = s->blmotion + y*s->blwidth;
 +
 +                init_obmc_weights(s, p, y);
 +
 +                if (y == s->blheight-1 || start+p->ybsep > p->height)
 +                    h = p->height - start;
 +                else
 +                    h = p->ybsep - (start - dsty);
 +                if (h < 0)
 +                    break;
 +
 +                memset(mctmp+2*p->yoffset*p->stride, 0, 2*rowheight);
 +                mc_row(s, blocks, mctmp, comp, dsty);
 +
 +                mctmp += (start - dsty)*p->stride + p->xoffset;
 +                ff_spatial_idwt_slice2(&d, start + h); /* decode */
 +                s->diracdsp.add_rect_clamped(frame + start*p->stride, mctmp, p->stride,
 +                                             p->idwt_buf + start*p->idwt_stride, p->idwt_stride, p->width, h);
 +
 +                dsty += p->ybsep;
 +            }
 +        }
 +    }
 +
 +
 +    return 0;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 11.1.1 Picture Header. picture_header()
 + */
 +static int dirac_decode_picture_header(DiracContext *s)
 +{
 +    int retire, picnum;
 +    int i, j, refnum, refdist;
 +    GetBitContext *gb = &s->gb;
 +
 +    /* [DIRAC_STD] 11.1.1 Picture Header. picture_header() PICTURE_NUM */
 +    picnum = s->current_picture->avframe.display_picture_number = get_bits_long(gb, 32);
 +
 +
 +    av_log(s->avctx,AV_LOG_DEBUG,"PICTURE_NUM: %d\n",picnum);
 +
 +    /* if this is the first keyframe after a sequence header, start our
 +       reordering from here */
 +    if (s->frame_number < 0)
 +        s->frame_number = picnum;
 +
 +    s->ref_pics[0] = s->ref_pics[1] = NULL;
 +    for (i = 0; i < s->num_refs; i++) {
 +        refnum = picnum + dirac_get_se_golomb(gb);
 +        refdist = INT_MAX;
 +
 +        /* find the closest reference to the one we want */
 +        /* Jordi: this is needed if the referenced picture hasn't yet arrived */
 +        for (j = 0; j < MAX_REFERENCE_FRAMES && refdist; j++)
 +            if (s->ref_frames[j]
 +                && FFABS(s->ref_frames[j]->avframe.display_picture_number - refnum) < refdist) {
 +                s->ref_pics[i] = s->ref_frames[j];
 +                refdist = FFABS(s->ref_frames[j]->avframe.display_picture_number - refnum);
 +            }
 +
 +        if (!s->ref_pics[i] || refdist)
 +            av_log(s->avctx, AV_LOG_DEBUG, "Reference not found\n");
 +
 +        /* if there were no references at all, allocate one */
 +        if (!s->ref_pics[i])
 +            for (j = 0; j < MAX_FRAMES; j++)
 +                if (!s->all_frames[j].avframe.data[0]) {
 +                    s->ref_pics[i] = &s->all_frames[j];
 +                    ff_get_buffer(s->avctx, &s->ref_pics[i]->avframe, AV_GET_BUFFER_FLAG_REF);
 +                    break;
 +                }
 +    }
 +
 +    /* retire the reference frames that are not used anymore */
 +    if (s->current_picture->avframe.reference) {
 +        retire = picnum + dirac_get_se_golomb(gb);
 +        if (retire != picnum) {
 +            DiracFrame *retire_pic = remove_frame(s->ref_frames, retire);
 +
 +            if (retire_pic)
 +                retire_pic->avframe.reference &= DELAYED_PIC_REF;
 +            else
 +                av_log(s->avctx, AV_LOG_DEBUG, "Frame to retire not found\n");
 +        }
 +
 +        /* if reference array is full, remove the oldest as per the spec */
 +        while (add_frame(s->ref_frames, MAX_REFERENCE_FRAMES, s->current_picture)) {
 +            av_log(s->avctx, AV_LOG_ERROR, "Reference frame overflow\n");
 +            remove_frame(s->ref_frames, s->ref_frames[0]->avframe.display_picture_number)->avframe.reference &= DELAYED_PIC_REF;
 +        }
 +    }
 +
 +    if (s->num_refs) {
 +        if (dirac_unpack_prediction_parameters(s))  /* [DIRAC_STD] 11.2 Picture Prediction Data. picture_prediction() */
 +            return -1;
 +        if (dirac_unpack_block_motion_data(s))      /* [DIRAC_STD] 12. Block motion data syntax                       */
 +            return -1;
 +    }
 +    if (dirac_unpack_idwt_params(s))                /* [DIRAC_STD] 11.3 Wavelet transform data                        */
 +        return -1;
 +
 +    init_planes(s);
 +    return 0;
 +}
 +
 +static int get_delayed_pic(DiracContext *s, AVFrame *picture, int *got_frame)
 +{
 +    DiracFrame *out = s->delay_frames[0];
 +    int i, out_idx  = 0;
 +    int ret;
 +
 +    /* find frame with lowest picture number */
 +    for (i = 1; s->delay_frames[i]; i++)
 +        if (s->delay_frames[i]->avframe.display_picture_number < out->avframe.display_picture_number) {
 +            out     = s->delay_frames[i];
 +            out_idx = i;
 +        }
 +
 +    for (i = out_idx; s->delay_frames[i]; i++)
 +        s->delay_frames[i] = s->delay_frames[i+1];
 +
 +    if (out) {
 +        out->avframe.reference ^= DELAYED_PIC_REF;
 +        *got_frame = 1;
 +        if((ret = av_frame_ref(picture, &out->avframe)) < 0)
 +            return ret;
 +    }
 +
 +    return 0;
 +}
 +
 +/**
 + * Dirac Specification ->
 + * 9.6 Parse Info Header Syntax. parse_info()
 + * 4 byte start code + byte parse code + 4 byte size + 4 byte previous size
 + */
 +#define DATA_UNIT_HEADER_SIZE 13
 +
 +/* [DIRAC_STD] dirac_decode_data_unit makes reference to the while defined in 9.3
 +   inside the function parse_sequence() */
 +static int dirac_decode_data_unit(AVCodecContext *avctx, const uint8_t *buf, int size)
 +{
 +    DiracContext *s   = avctx->priv_data;
 +    DiracFrame *pic   = NULL;
 +    int ret, i, parse_code = buf[4];
 +    unsigned tmp;
 +
 +    if (size < DATA_UNIT_HEADER_SIZE)
 +        return -1;
 +
 +    init_get_bits(&s->gb, &buf[13], 8*(size - DATA_UNIT_HEADER_SIZE));
 +
 +    if (parse_code == pc_seq_header) {
 +        if (s->seen_sequence_header)
 +            return 0;
 +
 +        /* [DIRAC_STD] 10. Sequence header */
 +        if (avpriv_dirac_parse_sequence_header(avctx, &s->gb, &s->source))
 +            return -1;
 +
 +        avcodec_get_chroma_sub_sample(avctx->pix_fmt, &s->chroma_x_shift, &s->chroma_y_shift);
 +
 +        if (alloc_sequence_buffers(s))
 +            return -1;
 +
 +        s->seen_sequence_header = 1;
 +    } else if (parse_code == pc_eos) { /* [DIRAC_STD] End of Sequence */
 +        free_sequence_buffers(s);
 +        s->seen_sequence_header = 0;
 +    } else if (parse_code == pc_aux_data) {
 +        if (buf[13] == 1) {     /* encoder implementation/version */
 +            int ver[3];
 +            /* versions older than 1.0.8 don't store quant delta for
 +               subbands with only one codeblock */
 +            if (sscanf(buf+14, "Schroedinger %d.%d.%d", ver, ver+1, ver+2) == 3)
 +                if (ver[0] == 1 && ver[1] == 0 && ver[2] <= 7)
 +                    s->old_delta_quant = 1;
 +        }
 +    } else if (parse_code & 0x8) {  /* picture data unit */
 +        if (!s->seen_sequence_header) {
 +            av_log(avctx, AV_LOG_DEBUG, "Dropping frame without sequence header\n");
 +            return -1;
 +        }
 +
 +        /* find an unused frame */
 +        for (i = 0; i < MAX_FRAMES; i++)
 +            if (s->all_frames[i].avframe.data[0] == NULL)
 +                pic = &s->all_frames[i];
 +        if (!pic) {
 +            av_log(avctx, AV_LOG_ERROR, "framelist full\n");
 +            return -1;
 +        }
 +
 +        avcodec_get_frame_defaults(&pic->avframe);
 +
 +        /* [DIRAC_STD] Defined in 9.6.1 ... */
 +        tmp            =  parse_code & 0x03;                   /* [DIRAC_STD] num_refs()      */
 +        if (tmp > 2) {
 +            av_log(avctx, AV_LOG_ERROR, "num_refs of 3\n");
 +            return -1;
 +        }
 +        s->num_refs    = tmp;
 +        s->is_arith    = (parse_code & 0x48) == 0x08;          /* [DIRAC_STD] using_ac()      */
 +        s->low_delay   = (parse_code & 0x88) == 0x88;          /* [DIRAC_STD] is_low_delay()  */
 +        pic->avframe.reference = (parse_code & 0x0C) == 0x0C;  /* [DIRAC_STD]  is_reference() */
 +        pic->avframe.key_frame = s->num_refs == 0;             /* [DIRAC_STD] is_intra()      */
 +        pic->avframe.pict_type = s->num_refs + 1;              /* Definition of AVPictureType in avutil.h */
 +
 +        if ((ret = ff_get_buffer(avctx, &pic->avframe, (parse_code & 0x0C) == 0x0C ? AV_GET_BUFFER_FLAG_REF : 0)) < 0)
 +            return ret;
 +        s->current_picture = pic;
 +        s->plane[0].stride = pic->avframe.linesize[0];
 +        s->plane[1].stride = pic->avframe.linesize[1];
 +        s->plane[2].stride = pic->avframe.linesize[2];
 +
 +        /* [DIRAC_STD] 11.1 Picture parse. picture_parse() */
 +        if (dirac_decode_picture_header(s))
 +            return -1;
 +
 +        /* [DIRAC_STD] 13.0 Transform data syntax. transform_data() */
 +        if (dirac_decode_frame_internal(s))
 +            return -1;
 +    }
 +    return 0;
 +}
 +
 +static int dirac_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *pkt)
 +{
 +    DiracContext *s     = avctx->priv_data;
 +    DiracFrame *picture = data;
 +    uint8_t *buf        = pkt->data;
 +    int buf_size        = pkt->size;
 +    int i, data_unit_size, buf_idx = 0;
 +    int ret;
 +
 +    /* release unused frames */
 +    for (i = 0; i < MAX_FRAMES; i++)
 +        if (s->all_frames[i].avframe.data[0] && !s->all_frames[i].avframe.reference) {
 +            av_frame_unref(&s->all_frames[i].avframe);
 +            memset(s->all_frames[i].interpolated, 0, sizeof(s->all_frames[i].interpolated));
 +        }
 +
 +    s->current_picture = NULL;
 +    *got_frame = 0;
 +
 +    /* end of stream, so flush delayed pics */
 +    if (buf_size == 0)
 +        return get_delayed_pic(s, (AVFrame *)data, got_frame);
 +
 +    for (;;) {
 +        /*[DIRAC_STD] Here starts the code from parse_info() defined in 9.6
 +          [DIRAC_STD] PARSE_INFO_PREFIX = "BBCD" as defined in ISO/IEC 646
 +          BBCD start code search */
 +        for (; buf_idx + DATA_UNIT_HEADER_SIZE < buf_size; buf_idx++) {
 +            if (buf[buf_idx  ] == 'B' && buf[buf_idx+1] == 'B' &&
 +                buf[buf_idx+2] == 'C' && buf[buf_idx+3] == 'D')
 +                break;
 +        }
 +        /* BBCD found or end of data */
 +        if (buf_idx + DATA_UNIT_HEADER_SIZE >= buf_size)
 +            break;
 +
 +        data_unit_size = AV_RB32(buf+buf_idx+5);
 +        if (buf_idx + data_unit_size > buf_size || !data_unit_size) {
 +            if(buf_idx + data_unit_size > buf_size)
 +            av_log(s->avctx, AV_LOG_ERROR,
 +                   "Data unit with size %d is larger than input buffer, discarding\n",
 +                   data_unit_size);
 +            buf_idx += 4;
 +            continue;
 +        }
 +        /* [DIRAC_STD] dirac_decode_data_unit makes reference to the while defined in 9.3 inside the function parse_sequence() */
 +        if (dirac_decode_data_unit(avctx, buf+buf_idx, data_unit_size))
 +        {
 +            av_log(s->avctx, AV_LOG_ERROR,"Error in dirac_decode_data_unit\n");
 +            return -1;
 +        }
 +        buf_idx += data_unit_size;
 +    }
 +
 +    if (!s->current_picture)
 +        return buf_size;
 +
 +    if (s->current_picture->avframe.display_picture_number > s->frame_number) {
 +        DiracFrame *delayed_frame = remove_frame(s->delay_frames, s->frame_number);
 +
 +        s->current_picture->avframe.reference |= DELAYED_PIC_REF;
 +
 +        if (add_frame(s->delay_frames, MAX_DELAY, s->current_picture)) {
 +            int min_num = s->delay_frames[0]->avframe.display_picture_number;
 +            /* Too many delayed frames, so we display the frame with the lowest pts */
 +            av_log(avctx, AV_LOG_ERROR, "Delay frame overflow\n");
 +            delayed_frame = s->delay_frames[0];
 +
 +            for (i = 1; s->delay_frames[i]; i++)
 +                if (s->delay_frames[i]->avframe.display_picture_number < min_num)
 +                    min_num = s->delay_frames[i]->avframe.display_picture_number;
 +
 +            delayed_frame = remove_frame(s->delay_frames, min_num);
 +            add_frame(s->delay_frames, MAX_DELAY, s->current_picture);
 +        }
 +
 +        if (delayed_frame) {
 +            delayed_frame->avframe.reference ^= DELAYED_PIC_REF;
 +            if((ret=av_frame_ref(data, &delayed_frame->avframe)) < 0)
 +                return ret;
 +            *got_frame = 1;
 +        }
 +    } else if (s->current_picture->avframe.display_picture_number == s->frame_number) {
 +        /* The right frame at the right time :-) */
 +        if((ret=av_frame_ref(data, &s->current_picture->avframe)) < 0)
 +            return ret;
 +        *got_frame = 1;
 +    }
 +
 +    if (*got_frame)
 +        s->frame_number = picture->avframe.display_picture_number + 1;
 +
 +    return buf_idx;
 +}
 +
 +AVCodec ff_dirac_decoder = {
 +    .name           = "dirac",
 +    .long_name      = NULL_IF_CONFIG_SMALL("BBC Dirac VC-2"),
 +    .type           = AVMEDIA_TYPE_VIDEO,
 +    .id             = AV_CODEC_ID_DIRAC,
 +    .priv_data_size = sizeof(DiracContext),
 +    .init           = dirac_decode_init,
 +    .close          = dirac_decode_end,
 +    .decode         = dirac_decode_frame,
 +    .capabilities   = CODEC_CAP_DELAY,
 +    .flush          = dirac_decode_flush,
 +};
Simple merge
Simple merge
@@@ -1964,616 -1847,13 +1964,619 @@@ void ff_print_debug_info2(AVCodecContex
  
  
                      if (IS_INTERLACED(mb_type))
 -                        av_log(s->avctx, AV_LOG_DEBUG, "=");
 +                        av_log(avctx, AV_LOG_DEBUG, "=");
                      else
 -                        av_log(s->avctx, AV_LOG_DEBUG, " ");
 +                        av_log(avctx, AV_LOG_DEBUG, " ");
 +                }
 +            }
 +            av_log(avctx, AV_LOG_DEBUG, "\n");
 +        }
 +    }
 +
 +    if ((avctx->debug & (FF_DEBUG_VIS_QP | FF_DEBUG_VIS_MB_TYPE)) ||
 +        (avctx->debug_mv)) {
 +        const int shift = 1 + quarter_sample;
 +        int mb_y;
 +        uint8_t *ptr;
 +        int i;
 +        int h_chroma_shift, v_chroma_shift, block_height;
 +        const int width          = avctx->width;
 +        const int height         = avctx->height;
 +        const int mv_sample_log2 = avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_SVQ3 ? 2 : 1;
 +        const int mv_stride      = (mb_width << mv_sample_log2) +
 +                                   (avctx->codec->id == AV_CODEC_ID_H264 ? 0 : 1);
 +
 +        *low_delay = 0; // needed to see the vectors without trashing the buffers
 +
 +        avcodec_get_chroma_sub_sample(avctx->pix_fmt, &h_chroma_shift, &v_chroma_shift);
 +
 +        av_frame_make_writable(pict);
 +
 +        pict->opaque = NULL;
 +        ptr          = pict->data[0];
 +        block_height = 16 >> v_chroma_shift;
 +
 +        for (mb_y = 0; mb_y < mb_height; mb_y++) {
 +            int mb_x;
 +            for (mb_x = 0; mb_x < mb_width; mb_x++) {
 +                const int mb_index = mb_x + mb_y * mb_stride;
 +                if ((avctx->debug_mv) && p->motion_val[0]) {
 +                    int type;
 +                    for (type = 0; type < 3; type++) {
 +                        int direction = 0;
 +                        switch (type) {
 +                        case 0:
 +                            if ((!(avctx->debug_mv & FF_DEBUG_VIS_MV_P_FOR)) ||
 +                                (pict->pict_type!= AV_PICTURE_TYPE_P))
 +                                continue;
 +                            direction = 0;
 +                            break;
 +                        case 1:
 +                            if ((!(avctx->debug_mv & FF_DEBUG_VIS_MV_B_FOR)) ||
 +                                (pict->pict_type!= AV_PICTURE_TYPE_B))
 +                                continue;
 +                            direction = 0;
 +                            break;
 +                        case 2:
 +                            if ((!(avctx->debug_mv & FF_DEBUG_VIS_MV_B_BACK)) ||
 +                                (pict->pict_type!= AV_PICTURE_TYPE_B))
 +                                continue;
 +                            direction = 1;
 +                            break;
 +                        }
 +                        if (!USES_LIST(p->mb_type[mb_index], direction))
 +                            continue;
 +
 +                        if (IS_8X8(p->mb_type[mb_index])) {
 +                            int i;
 +                            for (i = 0; i < 4; i++) {
 +                                int sx = mb_x * 16 + 4 + 8 * (i & 1);
 +                                int sy = mb_y * 16 + 4 + 8 * (i >> 1);
 +                                int xy = (mb_x * 2 + (i & 1) +
 +                                          (mb_y * 2 + (i >> 1)) * mv_stride) << (mv_sample_log2 - 1);
 +                                int mx = (p->motion_val[direction][xy][0] >> shift) + sx;
 +                                int my = (p->motion_val[direction][xy][1] >> shift) + sy;
 +                                draw_arrow(ptr, sx, sy, mx, my, width,
 +                                           height, pict->linesize[0], 100);
 +                            }
 +                        } else if (IS_16X8(p->mb_type[mb_index])) {
 +                            int i;
 +                            for (i = 0; i < 2; i++) {
 +                                int sx = mb_x * 16 + 8;
 +                                int sy = mb_y * 16 + 4 + 8 * i;
 +                                int xy = (mb_x * 2 + (mb_y * 2 + i) * mv_stride) << (mv_sample_log2 - 1);
 +                                int mx = (p->motion_val[direction][xy][0] >> shift);
 +                                int my = (p->motion_val[direction][xy][1] >> shift);
 +
 +                                if (IS_INTERLACED(p->mb_type[mb_index]))
 +                                    my *= 2;
 +
 +                            draw_arrow(ptr, sx, sy, mx + sx, my + sy, width,
 +                                       height, pict->linesize[0], 100);
 +                            }
 +                        } else if (IS_8X16(p->mb_type[mb_index])) {
 +                            int i;
 +                            for (i = 0; i < 2; i++) {
 +                                int sx = mb_x * 16 + 4 + 8 * i;
 +                                int sy = mb_y * 16 + 8;
 +                                int xy = (mb_x * 2 + i + mb_y * 2 * mv_stride) << (mv_sample_log2 - 1);
 +                                int mx = p->motion_val[direction][xy][0] >> shift;
 +                                int my = p->motion_val[direction][xy][1] >> shift;
 +
 +                                if (IS_INTERLACED(p->mb_type[mb_index]))
 +                                    my *= 2;
 +
 +                                draw_arrow(ptr, sx, sy, mx + sx, my + sy, width,
 +                                           height, pict->linesize[0], 100);
 +                            }
 +                        } else {
 +                              int sx= mb_x * 16 + 8;
 +                              int sy= mb_y * 16 + 8;
 +                              int xy= (mb_x + mb_y * mv_stride) << mv_sample_log2;
 +                              int mx= (p->motion_val[direction][xy][0]>>shift) + sx;
 +                              int my= (p->motion_val[direction][xy][1]>>shift) + sy;
 +                              draw_arrow(ptr, sx, sy, mx, my, width, height, pict->linesize[0], 100);
 +                        }
 +                    }
 +                }
 +                if ((avctx->debug & FF_DEBUG_VIS_QP)) {
 +                    uint64_t c = (p->qscale_table[mb_index] * 128 / 31) *
 +                                 0x0101010101010101ULL;
 +                    int y;
 +                    for (y = 0; y < block_height; y++) {
 +                        *(uint64_t *)(pict->data[1] + 8 * mb_x +
 +                                      (block_height * mb_y + y) *
 +                                      pict->linesize[1]) = c;
 +                        *(uint64_t *)(pict->data[2] + 8 * mb_x +
 +                                      (block_height * mb_y + y) *
 +                                      pict->linesize[2]) = c;
 +                    }
 +                }
 +                if ((avctx->debug & FF_DEBUG_VIS_MB_TYPE) &&
 +                    p->motion_val[0]) {
 +                    int mb_type = p->mb_type[mb_index];
 +                    uint64_t u,v;
 +                    int y;
 +#define COLOR(theta, r) \
 +    u = (int)(128 + r * cos(theta * 3.141592 / 180)); \
 +    v = (int)(128 + r * sin(theta * 3.141592 / 180));
 +
 +
 +                    u = v = 128;
 +                    if (IS_PCM(mb_type)) {
 +                        COLOR(120, 48)
 +                    } else if ((IS_INTRA(mb_type) && IS_ACPRED(mb_type)) ||
 +                               IS_INTRA16x16(mb_type)) {
 +                        COLOR(30, 48)
 +                    } else if (IS_INTRA4x4(mb_type)) {
 +                        COLOR(90, 48)
 +                    } else if (IS_DIRECT(mb_type) && IS_SKIP(mb_type)) {
 +                        // COLOR(120, 48)
 +                    } else if (IS_DIRECT(mb_type)) {
 +                        COLOR(150, 48)
 +                    } else if (IS_GMC(mb_type) && IS_SKIP(mb_type)) {
 +                        COLOR(170, 48)
 +                    } else if (IS_GMC(mb_type)) {
 +                        COLOR(190, 48)
 +                    } else if (IS_SKIP(mb_type)) {
 +                        // COLOR(180, 48)
 +                    } else if (!USES_LIST(mb_type, 1)) {
 +                        COLOR(240, 48)
 +                    } else if (!USES_LIST(mb_type, 0)) {
 +                        COLOR(0, 48)
 +                    } else {
 +                        av_assert2(USES_LIST(mb_type, 0) && USES_LIST(mb_type, 1));
 +                        COLOR(300,48)
 +                    }
 +
 +                    u *= 0x0101010101010101ULL;
 +                    v *= 0x0101010101010101ULL;
 +                    for (y = 0; y < block_height; y++) {
 +                        *(uint64_t *)(pict->data[1] + 8 * mb_x +
 +                                      (block_height * mb_y + y) * pict->linesize[1]) = u;
 +                        *(uint64_t *)(pict->data[2] + 8 * mb_x +
 +                                      (block_height * mb_y + y) * pict->linesize[2]) = v;
 +                    }
 +
 +                    // segmentation
 +                    if (IS_8X8(mb_type) || IS_16X8(mb_type)) {
 +                        *(uint64_t *)(pict->data[0] + 16 * mb_x + 0 +
 +                                      (16 * mb_y + 8) * pict->linesize[0]) ^= 0x8080808080808080ULL;
 +                        *(uint64_t *)(pict->data[0] + 16 * mb_x + 8 +
 +                                      (16 * mb_y + 8) * pict->linesize[0]) ^= 0x8080808080808080ULL;
 +                    }
 +                    if (IS_8X8(mb_type) || IS_8X16(mb_type)) {
 +                        for (y = 0; y < 16; y++)
 +                            pict->data[0][16 * mb_x + 8 + (16 * mb_y + y) *
 +                                          pict->linesize[0]] ^= 0x80;
 +                    }
 +                    if (IS_8X8(mb_type) && mv_sample_log2 >= 2) {
 +                        int dm = 1 << (mv_sample_log2 - 2);
 +                        for (i = 0; i < 4; i++) {
 +                            int sx = mb_x * 16 + 8 * (i & 1);
 +                            int sy = mb_y * 16 + 8 * (i >> 1);
 +                            int xy = (mb_x * 2 + (i & 1) +
 +                                     (mb_y * 2 + (i >> 1)) * mv_stride) << (mv_sample_log2 - 1);
 +                            // FIXME bidir
 +                            int32_t *mv = (int32_t *) &p->motion_val[0][xy];
 +                            if (mv[0] != mv[dm] ||
 +                                mv[dm * mv_stride] != mv[dm * (mv_stride + 1)])
 +                                for (y = 0; y < 8; y++)
 +                                    pict->data[0][sx + 4 + (sy + y) * pict->linesize[0]] ^= 0x80;
 +                            if (mv[0] != mv[dm * mv_stride] || mv[dm] != mv[dm * (mv_stride + 1)])
 +                                *(uint64_t *)(pict->data[0] + sx + (sy + 4) *
 +                                              pict->linesize[0]) ^= 0x8080808080808080ULL;
 +                        }
 +                    }
 +
 +                    if (IS_INTERLACED(mb_type) &&
 +                        avctx->codec->id == AV_CODEC_ID_H264) {
 +                        // hmm
 +                    }
 +                }
 +                mbskip_table[mb_index] = 0;
 +            }
 +        }
 +    }
 +}
 +
 +void ff_print_debug_info(MpegEncContext *s, Picture *p, AVFrame *pict)
 +{
 +    ff_print_debug_info2(s->avctx, p, pict, s->mbskip_table, &s->low_delay,
 +                         s->mb_width, s->mb_height, s->mb_stride, s->quarter_sample);
 +}
 +
 +int ff_mpv_export_qp_table(MpegEncContext *s, AVFrame *f, Picture *p, int qp_type)
 +{
 +    AVBufferRef *ref = av_buffer_ref(p->qscale_table_buf);
 +    int offset = 2*s->mb_stride + 1;
 +    if(!ref)
 +        return AVERROR(ENOMEM);
 +    av_assert0(ref->size >= offset + s->mb_stride * ((f->height+15)/16));
 +    ref->size -= offset;
 +    ref->data += offset;
 +    return av_frame_set_qp_table(f, ref, s->mb_stride, qp_type);
 +}
 +
 +static inline int hpel_motion_lowres(MpegEncContext *s,
 +                                     uint8_t *dest, uint8_t *src,
 +                                     int field_based, int field_select,
 +                                     int src_x, int src_y,
 +                                     int width, int height, ptrdiff_t stride,
 +                                     int h_edge_pos, int v_edge_pos,
 +                                     int w, int h, h264_chroma_mc_func *pix_op,
 +                                     int motion_x, int motion_y)
 +{
 +    const int lowres   = s->avctx->lowres;
 +    const int op_index = FFMIN(lowres, 3);
 +    const int s_mask   = (2 << lowres) - 1;
 +    int emu = 0;
 +    int sx, sy;
 +
 +    if (s->quarter_sample) {
 +        motion_x /= 2;
 +        motion_y /= 2;
 +    }
 +
 +    sx = motion_x & s_mask;
 +    sy = motion_y & s_mask;
 +    src_x += motion_x >> lowres + 1;
 +    src_y += motion_y >> lowres + 1;
 +
 +    src   += src_y * stride + src_x;
 +
 +    if ((unsigned)src_x > FFMAX( h_edge_pos - (!!sx) - w,                 0) ||
 +        (unsigned)src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - h, 0)) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, s->linesize,
-                                 src, s->linesize, w + 1,
-                                 (h + 1) << field_based, src_x,
-                                 src_y   << field_based,
-                                 h_edge_pos,
-                                 v_edge_pos);
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer, src,
++                                 s->linesize, s->linesize,
++                                 w + 1, (h + 1) << field_based,
++                                 src_x, src_y   << field_based,
++                                 h_edge_pos, v_edge_pos);
 +        src = s->edge_emu_buffer;
 +        emu = 1;
 +    }
 +
 +    sx = (sx << 2) >> lowres;
 +    sy = (sy << 2) >> lowres;
 +    if (field_select)
 +        src += s->linesize;
 +    pix_op[op_index](dest, src, stride, h, sx, sy);
 +    return emu;
 +}
 +
 +/* apply one mpeg motion vector to the three components */
 +static av_always_inline void mpeg_motion_lowres(MpegEncContext *s,
 +                                                uint8_t *dest_y,
 +                                                uint8_t *dest_cb,
 +                                                uint8_t *dest_cr,
 +                                                int field_based,
 +                                                int bottom_field,
 +                                                int field_select,
 +                                                uint8_t **ref_picture,
 +                                                h264_chroma_mc_func *pix_op,
 +                                                int motion_x, int motion_y,
 +                                                int h, int mb_y)
 +{
 +    uint8_t *ptr_y, *ptr_cb, *ptr_cr;
 +    int mx, my, src_x, src_y, uvsrc_x, uvsrc_y, sx, sy, uvsx, uvsy;
 +    ptrdiff_t uvlinesize, linesize;
 +    const int lowres     = s->avctx->lowres;
 +    const int op_index   = FFMIN(lowres-1+s->chroma_x_shift, 3);
 +    const int block_s    = 8>>lowres;
 +    const int s_mask     = (2 << lowres) - 1;
 +    const int h_edge_pos = s->h_edge_pos >> lowres;
 +    const int v_edge_pos = s->v_edge_pos >> lowres;
 +    linesize   = s->current_picture.f.linesize[0] << field_based;
 +    uvlinesize = s->current_picture.f.linesize[1] << field_based;
 +
 +    // FIXME obviously not perfect but qpel will not work in lowres anyway
 +    if (s->quarter_sample) {
 +        motion_x /= 2;
 +        motion_y /= 2;
 +    }
 +
 +    if(field_based){
 +        motion_y += (bottom_field - field_select)*((1 << lowres)-1);
 +    }
 +
 +    sx = motion_x & s_mask;
 +    sy = motion_y & s_mask;
 +    src_x = s->mb_x * 2 * block_s + (motion_x >> lowres + 1);
 +    src_y = (mb_y * 2 * block_s >> field_based) + (motion_y >> lowres + 1);
 +
 +    if (s->out_format == FMT_H263) {
 +        uvsx    = ((motion_x >> 1) & s_mask) | (sx & 1);
 +        uvsy    = ((motion_y >> 1) & s_mask) | (sy & 1);
 +        uvsrc_x = src_x >> 1;
 +        uvsrc_y = src_y >> 1;
 +    } else if (s->out_format == FMT_H261) {
 +        // even chroma mv's are full pel in H261
 +        mx      = motion_x / 4;
 +        my      = motion_y / 4;
 +        uvsx    = (2 * mx) & s_mask;
 +        uvsy    = (2 * my) & s_mask;
 +        uvsrc_x = s->mb_x * block_s + (mx >> lowres);
 +        uvsrc_y =    mb_y * block_s + (my >> lowres);
 +    } else {
 +        if(s->chroma_y_shift){
 +            mx      = motion_x / 2;
 +            my      = motion_y / 2;
 +            uvsx    = mx & s_mask;
 +            uvsy    = my & s_mask;
 +            uvsrc_x = s->mb_x * block_s                 + (mx >> lowres + 1);
 +            uvsrc_y =   (mb_y * block_s >> field_based) + (my >> lowres + 1);
 +        } else {
 +            if(s->chroma_x_shift){
 +            //Chroma422
 +                mx = motion_x / 2;
 +                uvsx = mx & s_mask;
 +                uvsy = motion_y & s_mask;
 +                uvsrc_y = src_y;
 +                uvsrc_x = s->mb_x*block_s               + (mx >> (lowres+1));
 +            } else {
 +            //Chroma444
 +                uvsx = motion_x & s_mask;
 +                uvsy = motion_y & s_mask;
 +                uvsrc_x = src_x;
 +                uvsrc_y = src_y;
 +            }
 +        }
 +    }
 +
 +    ptr_y  = ref_picture[0] + src_y   * linesize   + src_x;
 +    ptr_cb = ref_picture[1] + uvsrc_y * uvlinesize + uvsrc_x;
 +    ptr_cr = ref_picture[2] + uvsrc_y * uvlinesize + uvsrc_x;
 +
 +    if ((unsigned) src_x > FFMAX( h_edge_pos - (!!sx) - 2 * block_s,       0) || uvsrc_y<0 ||
 +        (unsigned) src_y > FFMAX((v_edge_pos >> field_based) - (!!sy) - h, 0)) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, linesize >> field_based, ptr_y,
-                                 linesize >> field_based, 17, 17 + field_based,
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr_y,
++                                 linesize >> field_based, linesize >> field_based,
++                                 17, 17 + field_based,
 +                                src_x, src_y << field_based, h_edge_pos,
 +                                v_edge_pos);
 +        ptr_y = s->edge_emu_buffer;
 +        if (!CONFIG_GRAY || !(s->flags & CODEC_FLAG_GRAY)) {
 +            uint8_t *uvbuf = s->edge_emu_buffer + 18 * s->linesize;
-             s->vdsp.emulated_edge_mc(uvbuf, uvlinesize >> field_based,
-                                     ptr_cb, uvlinesize >> field_based, 9,
-                                     9 + field_based,
++            s->vdsp.emulated_edge_mc(uvbuf,  ptr_cb,
++                                     uvlinesize >> field_based, uvlinesize >> field_based,
++                                     9, 9 + field_based,
 +                                    uvsrc_x, uvsrc_y << field_based,
 +                                    h_edge_pos >> 1, v_edge_pos >> 1);
-             s->vdsp.emulated_edge_mc(uvbuf + 16, uvlinesize >> field_based,
-                                     ptr_cr, uvlinesize >> field_based, 9,
-                                     9 + field_based,
++            s->vdsp.emulated_edge_mc(uvbuf + 16,  ptr_cr,
++                                     uvlinesize >> field_based,uvlinesize >> field_based,
++                                     9, 9 + field_based,
 +                                    uvsrc_x, uvsrc_y << field_based,
 +                                    h_edge_pos >> 1, v_edge_pos >> 1);
 +            ptr_cb = uvbuf;
 +            ptr_cr = uvbuf + 16;
 +        }
 +    }
 +
 +    // FIXME use this for field pix too instead of the obnoxious hack which changes picture.f.data
 +    if (bottom_field) {
 +        dest_y  += s->linesize;
 +        dest_cb += s->uvlinesize;
 +        dest_cr += s->uvlinesize;
 +    }
 +
 +    if (field_select) {
 +        ptr_y   += s->linesize;
 +        ptr_cb  += s->uvlinesize;
 +        ptr_cr  += s->uvlinesize;
 +    }
 +
 +    sx = (sx << 2) >> lowres;
 +    sy = (sy << 2) >> lowres;
 +    pix_op[lowres - 1](dest_y, ptr_y, linesize, h, sx, sy);
 +
 +    if (!CONFIG_GRAY || !(s->flags & CODEC_FLAG_GRAY)) {
 +        int hc = s->chroma_y_shift ? (h+1-bottom_field)>>1 : h;
 +        uvsx = (uvsx << 2) >> lowres;
 +        uvsy = (uvsy << 2) >> lowres;
 +        if (hc) {
 +            pix_op[op_index](dest_cb, ptr_cb, uvlinesize, hc, uvsx, uvsy);
 +            pix_op[op_index](dest_cr, ptr_cr, uvlinesize, hc, uvsx, uvsy);
 +        }
 +    }
 +    // FIXME h261 lowres loop filter
 +}
 +
 +static inline void chroma_4mv_motion_lowres(MpegEncContext *s,
 +                                            uint8_t *dest_cb, uint8_t *dest_cr,
 +                                            uint8_t **ref_picture,
 +                                            h264_chroma_mc_func * pix_op,
 +                                            int mx, int my)
 +{
 +    const int lowres     = s->avctx->lowres;
 +    const int op_index   = FFMIN(lowres, 3);
 +    const int block_s    = 8 >> lowres;
 +    const int s_mask     = (2 << lowres) - 1;
 +    const int h_edge_pos = s->h_edge_pos >> lowres + 1;
 +    const int v_edge_pos = s->v_edge_pos >> lowres + 1;
 +    int emu = 0, src_x, src_y, sx, sy;
 +    ptrdiff_t offset;
 +    uint8_t *ptr;
 +
 +    if (s->quarter_sample) {
 +        mx /= 2;
 +        my /= 2;
 +    }
 +
 +    /* In case of 8X8, we construct a single chroma motion vector
 +       with a special rounding */
 +    mx = ff_h263_round_chroma(mx);
 +    my = ff_h263_round_chroma(my);
 +
 +    sx = mx & s_mask;
 +    sy = my & s_mask;
 +    src_x = s->mb_x * block_s + (mx >> lowres + 1);
 +    src_y = s->mb_y * block_s + (my >> lowres + 1);
 +
 +    offset = src_y * s->uvlinesize + src_x;
 +    ptr = ref_picture[1] + offset;
 +    if ((unsigned) src_x > FFMAX(h_edge_pos - (!!sx) - block_s, 0) ||
 +        (unsigned) src_y > FFMAX(v_edge_pos - (!!sy) - block_s, 0)) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, s->uvlinesize, ptr, s->uvlinesize,
-                                 9, 9, src_x, src_y, h_edge_pos, v_edge_pos);
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr,
++                                 s->uvlinesize, s->uvlinesize,
++                                 9, 9,
++                                 src_x, src_y, h_edge_pos, v_edge_pos);
 +        ptr = s->edge_emu_buffer;
 +        emu = 1;
 +    }
 +    sx = (sx << 2) >> lowres;
 +    sy = (sy << 2) >> lowres;
 +    pix_op[op_index](dest_cb, ptr, s->uvlinesize, block_s, sx, sy);
 +
 +    ptr = ref_picture[2] + offset;
 +    if (emu) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, s->uvlinesize,
-                                 ptr, s->uvlinesize, 9, 9,
-                                 src_x, src_y, h_edge_pos, v_edge_pos);
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr,
++                                 s->uvlinesize, s->uvlinesize,
++                                 9, 9,
++                                 src_x, src_y, h_edge_pos, v_edge_pos);
 +        ptr = s->edge_emu_buffer;
 +    }
 +    pix_op[op_index](dest_cr, ptr, s->uvlinesize, block_s, sx, sy);
 +}
 +
 +/**
 + * motion compensation of a single macroblock
 + * @param s context
 + * @param dest_y luma destination pointer
 + * @param dest_cb chroma cb/u destination pointer
 + * @param dest_cr chroma cr/v destination pointer
 + * @param dir direction (0->forward, 1->backward)
 + * @param ref_picture array[3] of pointers to the 3 planes of the reference picture
 + * @param pix_op halfpel motion compensation function (average or put normally)
 + * the motion vectors are taken from s->mv and the MV type from s->mv_type
 + */
 +static inline void MPV_motion_lowres(MpegEncContext *s,
 +                                     uint8_t *dest_y, uint8_t *dest_cb,
 +                                     uint8_t *dest_cr,
 +                                     int dir, uint8_t **ref_picture,
 +                                     h264_chroma_mc_func *pix_op)
 +{
 +    int mx, my;
 +    int mb_x, mb_y, i;
 +    const int lowres  = s->avctx->lowres;
 +    const int block_s = 8 >>lowres;
 +
 +    mb_x = s->mb_x;
 +    mb_y = s->mb_y;
 +
 +    switch (s->mv_type) {
 +    case MV_TYPE_16X16:
 +        mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                           0, 0, 0,
 +                           ref_picture, pix_op,
 +                           s->mv[dir][0][0], s->mv[dir][0][1],
 +                           2 * block_s, mb_y);
 +        break;
 +    case MV_TYPE_8X8:
 +        mx = 0;
 +        my = 0;
 +        for (i = 0; i < 4; i++) {
 +            hpel_motion_lowres(s, dest_y + ((i & 1) + (i >> 1) *
 +                               s->linesize) * block_s,
 +                               ref_picture[0], 0, 0,
 +                               (2 * mb_x + (i & 1)) * block_s,
 +                               (2 * mb_y + (i >> 1)) * block_s,
 +                               s->width, s->height, s->linesize,
 +                               s->h_edge_pos >> lowres, s->v_edge_pos >> lowres,
 +                               block_s, block_s, pix_op,
 +                               s->mv[dir][i][0], s->mv[dir][i][1]);
 +
 +            mx += s->mv[dir][i][0];
 +            my += s->mv[dir][i][1];
 +        }
 +
 +        if (!CONFIG_GRAY || !(s->flags & CODEC_FLAG_GRAY))
 +            chroma_4mv_motion_lowres(s, dest_cb, dest_cr, ref_picture,
 +                                     pix_op, mx, my);
 +        break;
 +    case MV_TYPE_FIELD:
 +        if (s->picture_structure == PICT_FRAME) {
 +            /* top field */
 +            mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                               1, 0, s->field_select[dir][0],
 +                               ref_picture, pix_op,
 +                               s->mv[dir][0][0], s->mv[dir][0][1],
 +                               block_s, mb_y);
 +            /* bottom field */
 +            mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                               1, 1, s->field_select[dir][1],
 +                               ref_picture, pix_op,
 +                               s->mv[dir][1][0], s->mv[dir][1][1],
 +                               block_s, mb_y);
 +        } else {
 +            if (s->picture_structure != s->field_select[dir][0] + 1 &&
 +                s->pict_type != AV_PICTURE_TYPE_B && !s->first_field) {
 +                ref_picture = s->current_picture_ptr->f.data;
 +
 +            }
 +            mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                               0, 0, s->field_select[dir][0],
 +                               ref_picture, pix_op,
 +                               s->mv[dir][0][0],
 +                               s->mv[dir][0][1], 2 * block_s, mb_y >> 1);
 +            }
 +        break;
 +    case MV_TYPE_16X8:
 +        for (i = 0; i < 2; i++) {
 +            uint8_t **ref2picture;
 +
 +            if (s->picture_structure == s->field_select[dir][i] + 1 ||
 +                s->pict_type == AV_PICTURE_TYPE_B || s->first_field) {
 +                ref2picture = ref_picture;
 +            } else {
 +                ref2picture = s->current_picture_ptr->f.data;
 +            }
 +
 +            mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                               0, 0, s->field_select[dir][i],
 +                               ref2picture, pix_op,
 +                               s->mv[dir][i][0], s->mv[dir][i][1] +
 +                               2 * block_s * i, block_s, mb_y >> 1);
 +
 +            dest_y  +=  2 * block_s *  s->linesize;
 +            dest_cb += (2 * block_s >> s->chroma_y_shift) * s->uvlinesize;
 +            dest_cr += (2 * block_s >> s->chroma_y_shift) * s->uvlinesize;
 +        }
 +        break;
 +    case MV_TYPE_DMV:
 +        if (s->picture_structure == PICT_FRAME) {
 +            for (i = 0; i < 2; i++) {
 +                int j;
 +                for (j = 0; j < 2; j++) {
 +                    mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                                       1, j, j ^ i,
 +                                       ref_picture, pix_op,
 +                                       s->mv[dir][2 * i + j][0],
 +                                       s->mv[dir][2 * i + j][1],
 +                                       block_s, mb_y);
 +                }
 +                pix_op = s->h264chroma.avg_h264_chroma_pixels_tab;
 +            }
 +        } else {
 +            for (i = 0; i < 2; i++) {
 +                mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr,
 +                                   0, 0, s->picture_structure != i + 1,
 +                                   ref_picture, pix_op,
 +                                   s->mv[dir][2 * i][0],s->mv[dir][2 * i][1],
 +                                   2 * block_s, mb_y >> 1);
 +
 +                // after put we make avg of the same block
 +                pix_op = s->h264chroma.avg_h264_chroma_pixels_tab;
 +
 +                // opposite parity is always in the same
 +                // frame if this is second field
 +                if (!s->first_field) {
 +                    ref_picture = s->current_picture_ptr->f.data;
                  }
              }
 -            av_log(s->avctx, AV_LOG_DEBUG, "\n");
          }
 +        break;
 +    default:
 +        av_assert2(0);
      }
  }
  
@@@ -1829,25 -1693,27 +1829,31 @@@ static av_always_inline void encode_mb_
      ptr_y  = s->new_picture.f.data[0] +
               (mb_y * 16 * wrap_y)              + mb_x * 16;
      ptr_cb = s->new_picture.f.data[1] +
 -             (mb_y * mb_block_height * wrap_c) + mb_x * 8;
 +             (mb_y * mb_block_height * wrap_c) + mb_x * mb_block_width;
      ptr_cr = s->new_picture.f.data[2] +
 -             (mb_y * mb_block_height * wrap_c) + mb_x * 8;
 +             (mb_y * mb_block_height * wrap_c) + mb_x * mb_block_width;
  
 -    if (mb_x * 16 + 16 > s->width || mb_y * 16 + 16 > s->height) {
 +    if((mb_x * 16 + 16 > s->width || mb_y * 16 + 16 > s->height) && s->codec_id != AV_CODEC_ID_AMV){
          uint8_t *ebuf = s->edge_emu_buffer + 32;
-         s->vdsp.emulated_edge_mc(ebuf, wrap_y, ptr_y, wrap_y, 16, 16, mb_x * 16,
-                                  mb_y * 16, s->width, s->height);
 +        int cw = (s->width  + s->chroma_x_shift) >> s->chroma_x_shift;
 +        int ch = (s->height + s->chroma_y_shift) >> s->chroma_y_shift;
+         s->vdsp.emulated_edge_mc(ebuf, ptr_y,
+                                  wrap_y, wrap_y,
+                                  16, 16, mb_x * 16, mb_y * 16,
+                                  s->width, s->height);
          ptr_y = ebuf;
-         s->vdsp.emulated_edge_mc(ebuf + 18 * wrap_y, wrap_c, ptr_cb, wrap_c, mb_block_width,
-                                  mb_block_height, mb_x * mb_block_width, mb_y * mb_block_height,
+         s->vdsp.emulated_edge_mc(ebuf + 18 * wrap_y, ptr_cb,
+                                  wrap_c, wrap_c,
 -                                 8, mb_block_height, mb_x * 8, mb_y * 8,
 -                                 s->width >> 1, s->height >> 1);
++                                 mb_block_width, mb_block_height,
++                                 mb_x * mb_block_width, mb_y * mb_block_height,
 +                                 cw, ch);
          ptr_cb = ebuf + 18 * wrap_y;
-         s->vdsp.emulated_edge_mc(ebuf + 18 * wrap_y + 16, wrap_c, ptr_cr, wrap_c, mb_block_width,
-                                  mb_block_height, mb_x * mb_block_width, mb_y * mb_block_height,
 -        s->vdsp.emulated_edge_mc(ebuf + 18 * wrap_y + 8, ptr_cr,
++        s->vdsp.emulated_edge_mc(ebuf + 18 * wrap_y + 16, ptr_cr,
+                                  wrap_c, wrap_c,
 -                                 8, mb_block_height, mb_x * 8, mb_y * 8,
 -                                 s->width >> 1, s->height >> 1);
 -        ptr_cr = ebuf + 18 * wrap_y + 8;
++                                 mb_block_width, mb_block_height,
++                                 mb_x * mb_block_width, mb_y * mb_block_height,
 +                                 cw, ch);
 +        ptr_cr = ebuf + 18 * wrap_y + 16;
      }
  
      if (s->mb_intra) {
@@@ -60,10 -59,11 +60,10 @@@ static void gmc1_motion(MpegEncContext 
  
      ptr = ref_picture[0] + src_y * linesize + src_x;
  
 -    if (s->flags & CODEC_FLAG_EMU_EDGE) {
          if ((unsigned)src_x >= FFMAX(s->h_edge_pos - 17, 0) ||
              (unsigned)src_y >= FFMAX(s->v_edge_pos - 17, 0)) {
-             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, linesize, ptr,
-                                      linesize,
+             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr,
+                                      linesize, linesize,
                                       17, 17,
                                       src_x, src_y,
                                       s->h_edge_pos, s->v_edge_pos);
  
      offset = (src_y * uvlinesize) + src_x;
      ptr    = ref_picture[1] + offset;
 -    if (s->flags & CODEC_FLAG_EMU_EDGE) {
          if ((unsigned)src_x >= FFMAX((s->h_edge_pos >> 1) - 9, 0) ||
              (unsigned)src_y >= FFMAX((s->v_edge_pos >> 1) - 9, 0)) {
-             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, uvlinesize,
-                                      ptr, uvlinesize,
+             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr,
+                                      uvlinesize, uvlinesize,
                                       9, 9,
                                       src_x, src_y,
                                       s->h_edge_pos >> 1, s->v_edge_pos >> 1);
@@@ -209,14 -212,14 +209,14 @@@ static inline int hpel_motion(MpegEncCo
          dxy |= (motion_y & 1) << 1;
      src += src_y * s->linesize + src_x;
  
 -    if (s->unrestricted_mv && (s->flags & CODEC_FLAG_EMU_EDGE)) {
 +    if (s->flags & CODEC_FLAG_EMU_EDGE) {
          if ((unsigned)src_x > FFMAX(s->h_edge_pos - (motion_x & 1) - 8, 0) ||
              (unsigned)src_y > FFMAX(s->v_edge_pos - (motion_y & 1) - 8, 0)) {
-             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, s->linesize, src,
-                                      s->linesize,
+             s->vdsp.emulated_edge_mc(s->edge_emu_buffer, src,
+                                      s->linesize, s->linesize,
                                       9, 9,
 -                                     src_x, src_y, s->h_edge_pos,
 -                                     s->v_edge_pos);
 +                                     src_x, src_y,
 +                                     s->h_edge_pos, s->v_edge_pos);
              src = s->edge_emu_buffer;
              emu = 1;
          }
@@@ -537,10 -540,9 +537,9 @@@ static inline void qpel_motion(MpegEncC
      ptr_cr = ref_picture[2] + uvsrc_y * uvlinesize + uvsrc_x;
  
      if ((unsigned)src_x > FFMAX(s->h_edge_pos - (motion_x & 3) - 16, 0) ||
 -        (unsigned)src_y > FFMAX(v_edge_pos - (motion_y & 3) - h, 0)) {
 +        (unsigned)src_y > FFMAX(   v_edge_pos - (motion_y & 3) - h, 0)) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer,
-                                  s->linesize,
-                                  ptr_y, s->linesize,
+         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, ptr_y,
+                                  s->linesize, s->linesize,
                                   17, 17 + field_based,
                                   src_x, src_y << field_based,
                                   s->h_edge_pos, s->v_edge_pos);
@@@ -724,16 -723,19 +724,22 @@@ static inline void rv34_mc(RV34DecConte
          uint8_t *uvbuf = s->edge_emu_buffer + 22 * s->linesize;
  
          srcY -= 2 + 2*s->linesize;
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, s->linesize, srcY, s->linesize,
-                                  (width<<3)+6, (height<<3)+6, src_x - 2, src_y - 2,
+         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, srcY,
+                                  s->linesize, s->linesize,
+                                  (width << 3) + 6, (height << 3) + 6,
 -                            src_x - 2, src_y - 2, s->h_edge_pos, s->v_edge_pos);
++                                 src_x - 2, src_y - 2,
 +                                 s->h_edge_pos, s->v_edge_pos);
          srcY = s->edge_emu_buffer + 2 + 2*s->linesize;
-         s->vdsp.emulated_edge_mc(uvbuf, s->uvlinesize, srcU, s->uvlinesize,
-                                  (width<<2)+1, (height<<2)+1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf, srcU,
 -                                 s->uvlinesize,s->uvlinesize,
++                                 s->uvlinesize, s->uvlinesize,
+                                  (width << 2) + 1, (height << 2) + 1,
 -                            uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, s->v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, s->v_edge_pos >> 1);
-         s->vdsp.emulated_edge_mc(uvbuf + 16, s->uvlinesize, srcV, s->uvlinesize,
-                                  (width<<2)+1, (height<<2)+1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf + 16, srcV,
+                                  s->uvlinesize, s->uvlinesize,
+                                  (width << 2) + 1, (height << 2) + 1,
 -                            uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, s->v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, s->v_edge_pos >> 1);
          srcU = uvbuf;
          srcV = uvbuf + 16;
      }
index b54c491,0000000..c645b12
mode 100644,000000..100644
--- /dev/null
@@@ -1,708 -1,0 +1,709 @@@
-             s->vdsp.emulated_edge_mc(tmp + MB_SIZE, stride, src, stride,
 +/*
 + * Copyright (C) 2004 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 "libavutil/intmath.h"
 +#include "libavutil/log.h"
 +#include "libavutil/opt.h"
 +#include "avcodec.h"
 +#include "dsputil.h"
 +#include "snow_dwt.h"
 +#include "internal.h"
 +#include "snow.h"
 +#include "snowdata.h"
 +
 +#include "rangecoder.h"
 +#include "mathops.h"
 +#include "h263.h"
 +
 +
 +void ff_snow_inner_add_yblock(const uint8_t *obmc, const int obmc_stride, uint8_t * * block, int b_w, int b_h,
 +                              int src_x, int src_y, int src_stride, slice_buffer * sb, int add, uint8_t * dst8){
 +    int y, x;
 +    IDWTELEM * dst;
 +    for(y=0; y<b_h; y++){
 +        //FIXME ugly misuse of obmc_stride
 +        const uint8_t *obmc1= obmc + y*obmc_stride;
 +        const uint8_t *obmc2= obmc1+ (obmc_stride>>1);
 +        const uint8_t *obmc3= obmc1+ obmc_stride*(obmc_stride>>1);
 +        const uint8_t *obmc4= obmc3+ (obmc_stride>>1);
 +        dst = slice_buffer_get_line(sb, src_y + y);
 +        for(x=0; x<b_w; x++){
 +            int v=   obmc1[x] * block[3][x + y*src_stride]
 +                    +obmc2[x] * block[2][x + y*src_stride]
 +                    +obmc3[x] * block[1][x + y*src_stride]
 +                    +obmc4[x] * block[0][x + y*src_stride];
 +
 +            v <<= 8 - LOG2_OBMC_MAX;
 +            if(FRAC_BITS != 8){
 +                v >>= 8 - FRAC_BITS;
 +            }
 +            if(add){
 +                v += dst[x + src_x];
 +                v = (v + (1<<(FRAC_BITS-1))) >> FRAC_BITS;
 +                if(v&(~255)) v= ~(v>>31);
 +                dst8[x + y*src_stride] = v;
 +            }else{
 +                dst[x + src_x] -= v;
 +            }
 +        }
 +    }
 +}
 +
 +void ff_snow_reset_contexts(SnowContext *s){ //FIXME better initial contexts
 +    int plane_index, level, orientation;
 +
 +    for(plane_index=0; plane_index<3; plane_index++){
 +        for(level=0; level<MAX_DECOMPOSITIONS; level++){
 +            for(orientation=level ? 1:0; orientation<4; orientation++){
 +                memset(s->plane[plane_index].band[level][orientation].state, MID_STATE, sizeof(s->plane[plane_index].band[level][orientation].state));
 +            }
 +        }
 +    }
 +    memset(s->header_state, MID_STATE, sizeof(s->header_state));
 +    memset(s->block_state, MID_STATE, sizeof(s->block_state));
 +}
 +
 +int ff_snow_alloc_blocks(SnowContext *s){
 +    int w= FF_CEIL_RSHIFT(s->avctx->width,  LOG2_MB_SIZE);
 +    int h= FF_CEIL_RSHIFT(s->avctx->height, LOG2_MB_SIZE);
 +
 +    s->b_width = w;
 +    s->b_height= h;
 +
 +    av_free(s->block);
 +    s->block= av_mallocz(w * h * sizeof(BlockNode) << (s->block_max_depth*2));
 +    if (!s->block)
 +        return AVERROR(ENOMEM);
 +
 +    return 0;
 +}
 +
 +static av_cold void init_qexp(void){
 +    int i;
 +    double v=128;
 +
 +    for(i=0; i<QROOT; i++){
 +        ff_qexp[i]= lrintf(v);
 +        v *= pow(2, 1.0 / QROOT);
 +    }
 +}
 +static void mc_block(Plane *p, uint8_t *dst, const uint8_t *src, int stride, int b_w, int b_h, int dx, int dy){
 +    static const uint8_t weight[64]={
 +    8,7,6,5,4,3,2,1,
 +    7,7,0,0,0,0,0,1,
 +    6,0,6,0,0,0,2,0,
 +    5,0,0,5,0,3,0,0,
 +    4,0,0,0,4,0,0,0,
 +    3,0,0,5,0,3,0,0,
 +    2,0,6,0,0,0,2,0,
 +    1,7,0,0,0,0,0,1,
 +    };
 +
 +    static const uint8_t brane[256]={
 +    0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x12,
 +    0x04,0x05,0xcc,0xcc,0xcc,0xcc,0xcc,0x41,0x15,0x16,0xcc,0xcc,0xcc,0xcc,0xcc,0x52,
 +    0x04,0xcc,0x05,0xcc,0xcc,0xcc,0x41,0xcc,0x15,0xcc,0x16,0xcc,0xcc,0xcc,0x52,0xcc,
 +    0x04,0xcc,0xcc,0x05,0xcc,0x41,0xcc,0xcc,0x15,0xcc,0xcc,0x16,0xcc,0x52,0xcc,0xcc,
 +    0x04,0xcc,0xcc,0xcc,0x41,0xcc,0xcc,0xcc,0x15,0xcc,0xcc,0xcc,0x16,0xcc,0xcc,0xcc,
 +    0x04,0xcc,0xcc,0x41,0xcc,0x05,0xcc,0xcc,0x15,0xcc,0xcc,0x52,0xcc,0x16,0xcc,0xcc,
 +    0x04,0xcc,0x41,0xcc,0xcc,0xcc,0x05,0xcc,0x15,0xcc,0x52,0xcc,0xcc,0xcc,0x16,0xcc,
 +    0x04,0x41,0xcc,0xcc,0xcc,0xcc,0xcc,0x05,0x15,0x52,0xcc,0xcc,0xcc,0xcc,0xcc,0x16,
 +    0x44,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x55,0x56,0x56,0x56,0x56,0x56,0x56,0x56,
 +    0x48,0x49,0xcc,0xcc,0xcc,0xcc,0xcc,0x85,0x59,0x5A,0xcc,0xcc,0xcc,0xcc,0xcc,0x96,
 +    0x48,0xcc,0x49,0xcc,0xcc,0xcc,0x85,0xcc,0x59,0xcc,0x5A,0xcc,0xcc,0xcc,0x96,0xcc,
 +    0x48,0xcc,0xcc,0x49,0xcc,0x85,0xcc,0xcc,0x59,0xcc,0xcc,0x5A,0xcc,0x96,0xcc,0xcc,
 +    0x48,0xcc,0xcc,0xcc,0x49,0xcc,0xcc,0xcc,0x59,0xcc,0xcc,0xcc,0x96,0xcc,0xcc,0xcc,
 +    0x48,0xcc,0xcc,0x85,0xcc,0x49,0xcc,0xcc,0x59,0xcc,0xcc,0x96,0xcc,0x5A,0xcc,0xcc,
 +    0x48,0xcc,0x85,0xcc,0xcc,0xcc,0x49,0xcc,0x59,0xcc,0x96,0xcc,0xcc,0xcc,0x5A,0xcc,
 +    0x48,0x85,0xcc,0xcc,0xcc,0xcc,0xcc,0x49,0x59,0x96,0xcc,0xcc,0xcc,0xcc,0xcc,0x5A,
 +    };
 +
 +    static const uint8_t needs[16]={
 +    0,1,0,0,
 +    2,4,2,0,
 +    0,1,0,0,
 +    15
 +    };
 +
 +    int x, y, b, r, l;
 +    int16_t tmpIt   [64*(32+HTAPS_MAX)];
 +    uint8_t tmp2t[3][64*(32+HTAPS_MAX)];
 +    int16_t *tmpI= tmpIt;
 +    uint8_t *tmp2= tmp2t[0];
 +    const uint8_t *hpel[11];
 +    av_assert2(dx<16 && dy<16);
 +    r= brane[dx + 16*dy]&15;
 +    l= brane[dx + 16*dy]>>4;
 +
 +    b= needs[l] | needs[r];
 +    if(p && !p->diag_mc)
 +        b= 15;
 +
 +    if(b&5){
 +        for(y=0; y < b_h+HTAPS_MAX-1; y++){
 +            for(x=0; x < b_w; x++){
 +                int a_1=src[x + HTAPS_MAX/2-4];
 +                int a0= src[x + HTAPS_MAX/2-3];
 +                int a1= src[x + HTAPS_MAX/2-2];
 +                int a2= src[x + HTAPS_MAX/2-1];
 +                int a3= src[x + HTAPS_MAX/2+0];
 +                int a4= src[x + HTAPS_MAX/2+1];
 +                int a5= src[x + HTAPS_MAX/2+2];
 +                int a6= src[x + HTAPS_MAX/2+3];
 +                int am=0;
 +                if(!p || p->fast_mc){
 +                    am= 20*(a2+a3) - 5*(a1+a4) + (a0+a5);
 +                    tmpI[x]= am;
 +                    am= (am+16)>>5;
 +                }else{
 +                    am= p->hcoeff[0]*(a2+a3) + p->hcoeff[1]*(a1+a4) + p->hcoeff[2]*(a0+a5) + p->hcoeff[3]*(a_1+a6);
 +                    tmpI[x]= am;
 +                    am= (am+32)>>6;
 +                }
 +
 +                if(am&(~255)) am= ~(am>>31);
 +                tmp2[x]= am;
 +            }
 +            tmpI+= 64;
 +            tmp2+= 64;
 +            src += stride;
 +        }
 +        src -= stride*y;
 +    }
 +    src += HTAPS_MAX/2 - 1;
 +    tmp2= tmp2t[1];
 +
 +    if(b&2){
 +        for(y=0; y < b_h; y++){
 +            for(x=0; x < b_w+1; x++){
 +                int a_1=src[x + (HTAPS_MAX/2-4)*stride];
 +                int a0= src[x + (HTAPS_MAX/2-3)*stride];
 +                int a1= src[x + (HTAPS_MAX/2-2)*stride];
 +                int a2= src[x + (HTAPS_MAX/2-1)*stride];
 +                int a3= src[x + (HTAPS_MAX/2+0)*stride];
 +                int a4= src[x + (HTAPS_MAX/2+1)*stride];
 +                int a5= src[x + (HTAPS_MAX/2+2)*stride];
 +                int a6= src[x + (HTAPS_MAX/2+3)*stride];
 +                int am=0;
 +                if(!p || p->fast_mc)
 +                    am= (20*(a2+a3) - 5*(a1+a4) + (a0+a5) + 16)>>5;
 +                else
 +                    am= (p->hcoeff[0]*(a2+a3) + p->hcoeff[1]*(a1+a4) + p->hcoeff[2]*(a0+a5) + p->hcoeff[3]*(a_1+a6) + 32)>>6;
 +
 +                if(am&(~255)) am= ~(am>>31);
 +                tmp2[x]= am;
 +            }
 +            src += stride;
 +            tmp2+= 64;
 +        }
 +        src -= stride*y;
 +    }
 +    src += stride*(HTAPS_MAX/2 - 1);
 +    tmp2= tmp2t[2];
 +    tmpI= tmpIt;
 +    if(b&4){
 +        for(y=0; y < b_h; y++){
 +            for(x=0; x < b_w; x++){
 +                int a_1=tmpI[x + (HTAPS_MAX/2-4)*64];
 +                int a0= tmpI[x + (HTAPS_MAX/2-3)*64];
 +                int a1= tmpI[x + (HTAPS_MAX/2-2)*64];
 +                int a2= tmpI[x + (HTAPS_MAX/2-1)*64];
 +                int a3= tmpI[x + (HTAPS_MAX/2+0)*64];
 +                int a4= tmpI[x + (HTAPS_MAX/2+1)*64];
 +                int a5= tmpI[x + (HTAPS_MAX/2+2)*64];
 +                int a6= tmpI[x + (HTAPS_MAX/2+3)*64];
 +                int am=0;
 +                if(!p || p->fast_mc)
 +                    am= (20*(a2+a3) - 5*(a1+a4) + (a0+a5) + 512)>>10;
 +                else
 +                    am= (p->hcoeff[0]*(a2+a3) + p->hcoeff[1]*(a1+a4) + p->hcoeff[2]*(a0+a5) + p->hcoeff[3]*(a_1+a6) + 2048)>>12;
 +                if(am&(~255)) am= ~(am>>31);
 +                tmp2[x]= am;
 +            }
 +            tmpI+= 64;
 +            tmp2+= 64;
 +        }
 +    }
 +
 +    hpel[ 0]= src;
 +    hpel[ 1]= tmp2t[0] + 64*(HTAPS_MAX/2-1);
 +    hpel[ 2]= src + 1;
 +
 +    hpel[ 4]= tmp2t[1];
 +    hpel[ 5]= tmp2t[2];
 +    hpel[ 6]= tmp2t[1] + 1;
 +
 +    hpel[ 8]= src + stride;
 +    hpel[ 9]= hpel[1] + 64;
 +    hpel[10]= hpel[8] + 1;
 +
 +#define MC_STRIDE(x) (needs[x] ? 64 : stride)
 +
 +    if(b==15){
 +        int dxy = dx / 8 + dy / 8 * 4;
 +        const uint8_t *src1 = hpel[dxy    ];
 +        const uint8_t *src2 = hpel[dxy + 1];
 +        const uint8_t *src3 = hpel[dxy + 4];
 +        const uint8_t *src4 = hpel[dxy + 5];
 +        int stride1 = MC_STRIDE(dxy);
 +        int stride2 = MC_STRIDE(dxy + 1);
 +        int stride3 = MC_STRIDE(dxy + 4);
 +        int stride4 = MC_STRIDE(dxy + 5);
 +        dx&=7;
 +        dy&=7;
 +        for(y=0; y < b_h; y++){
 +            for(x=0; x < b_w; x++){
 +                dst[x]= ((8-dx)*(8-dy)*src1[x] + dx*(8-dy)*src2[x]+
 +                         (8-dx)*   dy *src3[x] + dx*   dy *src4[x]+32)>>6;
 +            }
 +            src1+=stride1;
 +            src2+=stride2;
 +            src3+=stride3;
 +            src4+=stride4;
 +            dst +=stride;
 +        }
 +    }else{
 +        const uint8_t *src1= hpel[l];
 +        const uint8_t *src2= hpel[r];
 +        int stride1 = MC_STRIDE(l);
 +        int stride2 = MC_STRIDE(r);
 +        int a= weight[((dx&7) + (8*(dy&7)))];
 +        int b= 8-a;
 +        for(y=0; y < b_h; y++){
 +            for(x=0; x < b_w; x++){
 +                dst[x]= (a*src1[x] + b*src2[x] + 4)>>3;
 +            }
 +            src1+=stride1;
 +            src2+=stride2;
 +            dst +=stride;
 +        }
 +    }
 +}
 +
 +void ff_snow_pred_block(SnowContext *s, uint8_t *dst, uint8_t *tmp, ptrdiff_t stride, int sx, int sy, int b_w, int b_h, BlockNode *block, int plane_index, int w, int h){
 +    if(block->type & BLOCK_INTRA){
 +        int x, y;
 +        const unsigned color  = block->color[plane_index];
 +        const unsigned color4 = color*0x01010101;
 +        if(b_w==32){
 +            for(y=0; y < b_h; y++){
 +                *(uint32_t*)&dst[0 + y*stride]= color4;
 +                *(uint32_t*)&dst[4 + y*stride]= color4;
 +                *(uint32_t*)&dst[8 + y*stride]= color4;
 +                *(uint32_t*)&dst[12+ y*stride]= color4;
 +                *(uint32_t*)&dst[16+ y*stride]= color4;
 +                *(uint32_t*)&dst[20+ y*stride]= color4;
 +                *(uint32_t*)&dst[24+ y*stride]= color4;
 +                *(uint32_t*)&dst[28+ y*stride]= color4;
 +            }
 +        }else if(b_w==16){
 +            for(y=0; y < b_h; y++){
 +                *(uint32_t*)&dst[0 + y*stride]= color4;
 +                *(uint32_t*)&dst[4 + y*stride]= color4;
 +                *(uint32_t*)&dst[8 + y*stride]= color4;
 +                *(uint32_t*)&dst[12+ y*stride]= color4;
 +            }
 +        }else if(b_w==8){
 +            for(y=0; y < b_h; y++){
 +                *(uint32_t*)&dst[0 + y*stride]= color4;
 +                *(uint32_t*)&dst[4 + y*stride]= color4;
 +            }
 +        }else if(b_w==4){
 +            for(y=0; y < b_h; y++){
 +                *(uint32_t*)&dst[0 + y*stride]= color4;
 +            }
 +        }else{
 +            for(y=0; y < b_h; y++){
 +                for(x=0; x < b_w; x++){
 +                    dst[x + y*stride]= color;
 +                }
 +            }
 +        }
 +    }else{
 +        uint8_t *src= s->last_picture[block->ref]->data[plane_index];
 +        const int scale= plane_index ?  (2*s->mv_scale)>>s->chroma_h_shift : 2*s->mv_scale;
 +        int mx= block->mx*scale;
 +        int my= block->my*scale;
 +        const int dx= mx&15;
 +        const int dy= my&15;
 +        const int tab_index= 3 - (b_w>>2) + (b_w>>4);
 +        sx += (mx>>4) - (HTAPS_MAX/2-1);
 +        sy += (my>>4) - (HTAPS_MAX/2-1);
 +        src += sx + sy*stride;
 +        if(   (unsigned)sx >= FFMAX(w - b_w - (HTAPS_MAX-2), 0)
 +           || (unsigned)sy >= FFMAX(h - b_h - (HTAPS_MAX-2), 0)){
++            s->vdsp.emulated_edge_mc(tmp + MB_SIZE, src,
++                                     stride, stride,
 +                                     b_w+HTAPS_MAX-1, b_h+HTAPS_MAX-1,
 +                                     sx, sy, w, h);
 +            src= tmp + MB_SIZE;
 +        }
 +
 +        av_assert2(s->chroma_h_shift == s->chroma_v_shift); // only one mv_scale
 +
 +        av_assert2(b_w>1 && b_h>1);
 +        av_assert2((tab_index>=0 && tab_index<4) || b_w==32);
 +        if((dx&3) || (dy&3) || !(b_w == b_h || 2*b_w == b_h || b_w == 2*b_h) || (b_w&(b_w-1)) || !s->plane[plane_index].fast_mc )
 +            mc_block(&s->plane[plane_index], dst, src, stride, b_w, b_h, dx, dy);
 +        else if(b_w==32){
 +            int y;
 +            for(y=0; y<b_h; y+=16){
 +                s->h264qpel.put_h264_qpel_pixels_tab[0][dy+(dx>>2)](dst + y*stride, src + 3 + (y+3)*stride,stride);
 +                s->h264qpel.put_h264_qpel_pixels_tab[0][dy+(dx>>2)](dst + 16 + y*stride, src + 19 + (y+3)*stride,stride);
 +            }
 +        }else if(b_w==b_h)
 +            s->h264qpel.put_h264_qpel_pixels_tab[tab_index  ][dy+(dx>>2)](dst,src + 3 + 3*stride,stride);
 +        else if(b_w==2*b_h){
 +            s->h264qpel.put_h264_qpel_pixels_tab[tab_index+1][dy+(dx>>2)](dst    ,src + 3       + 3*stride,stride);
 +            s->h264qpel.put_h264_qpel_pixels_tab[tab_index+1][dy+(dx>>2)](dst+b_h,src + 3 + b_h + 3*stride,stride);
 +        }else{
 +            av_assert2(2*b_w==b_h);
 +            s->h264qpel.put_h264_qpel_pixels_tab[tab_index  ][dy+(dx>>2)](dst           ,src + 3 + 3*stride           ,stride);
 +            s->h264qpel.put_h264_qpel_pixels_tab[tab_index  ][dy+(dx>>2)](dst+b_w*stride,src + 3 + 3*stride+b_w*stride,stride);
 +        }
 +    }
 +}
 +
 +#define mca(dx,dy,b_w)\
 +static void mc_block_hpel ## dx ## dy ## b_w(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, int h){\
 +    av_assert2(h==b_w);\
 +    mc_block(NULL, dst, src-(HTAPS_MAX/2-1)-(HTAPS_MAX/2-1)*stride, stride, b_w, b_w, dx, dy);\
 +}
 +
 +mca( 0, 0,16)
 +mca( 8, 0,16)
 +mca( 0, 8,16)
 +mca( 8, 8,16)
 +mca( 0, 0,8)
 +mca( 8, 0,8)
 +mca( 0, 8,8)
 +mca( 8, 8,8)
 +
 +av_cold int ff_snow_common_init(AVCodecContext *avctx){
 +    SnowContext *s = avctx->priv_data;
 +    int width, height;
 +    int i, j;
 +
 +    s->avctx= avctx;
 +    s->max_ref_frames=1; //just make sure it's not an invalid value in case of no initial keyframe
 +
 +    ff_dsputil_init(&s->dsp, avctx);
 +    ff_hpeldsp_init(&s->hdsp, avctx->flags);
 +    ff_videodsp_init(&s->vdsp, 8);
 +    ff_dwt_init(&s->dwt);
 +    ff_h264qpel_init(&s->h264qpel, 8);
 +
 +#define mcf(dx,dy)\
 +    s->dsp.put_qpel_pixels_tab       [0][dy+dx/4]=\
 +    s->dsp.put_no_rnd_qpel_pixels_tab[0][dy+dx/4]=\
 +        s->h264qpel.put_h264_qpel_pixels_tab[0][dy+dx/4];\
 +    s->dsp.put_qpel_pixels_tab       [1][dy+dx/4]=\
 +    s->dsp.put_no_rnd_qpel_pixels_tab[1][dy+dx/4]=\
 +        s->h264qpel.put_h264_qpel_pixels_tab[1][dy+dx/4];
 +
 +    mcf( 0, 0)
 +    mcf( 4, 0)
 +    mcf( 8, 0)
 +    mcf(12, 0)
 +    mcf( 0, 4)
 +    mcf( 4, 4)
 +    mcf( 8, 4)
 +    mcf(12, 4)
 +    mcf( 0, 8)
 +    mcf( 4, 8)
 +    mcf( 8, 8)
 +    mcf(12, 8)
 +    mcf( 0,12)
 +    mcf( 4,12)
 +    mcf( 8,12)
 +    mcf(12,12)
 +
 +#define mcfh(dx,dy)\
 +    s->hdsp.put_pixels_tab       [0][dy/4+dx/8]=\
 +    s->hdsp.put_no_rnd_pixels_tab[0][dy/4+dx/8]=\
 +        mc_block_hpel ## dx ## dy ## 16;\
 +    s->hdsp.put_pixels_tab       [1][dy/4+dx/8]=\
 +    s->hdsp.put_no_rnd_pixels_tab[1][dy/4+dx/8]=\
 +        mc_block_hpel ## dx ## dy ## 8;
 +
 +    mcfh(0, 0)
 +    mcfh(8, 0)
 +    mcfh(0, 8)
 +    mcfh(8, 8)
 +
 +    init_qexp();
 +
 +//    dec += FFMAX(s->chroma_h_shift, s->chroma_v_shift);
 +
 +    width= s->avctx->width;
 +    height= s->avctx->height;
 +
 +    FF_ALLOCZ_OR_GOTO(avctx, s->spatial_idwt_buffer, width * height * sizeof(IDWTELEM), fail);
 +    FF_ALLOCZ_OR_GOTO(avctx, s->spatial_dwt_buffer,  width * height * sizeof(DWTELEM),  fail); //FIXME this does not belong here
 +    FF_ALLOCZ_OR_GOTO(avctx, s->temp_dwt_buffer,     width * sizeof(DWTELEM),  fail);
 +    FF_ALLOCZ_OR_GOTO(avctx, s->temp_idwt_buffer,    width * sizeof(IDWTELEM), fail);
 +    FF_ALLOC_OR_GOTO(avctx,  s->run_buffer,          ((width + 1) >> 1) * ((height + 1) >> 1) * sizeof(*s->run_buffer), fail);
 +
 +    for(i=0; i<MAX_REF_FRAMES; i++) {
 +        for(j=0; j<MAX_REF_FRAMES; j++)
 +            ff_scale_mv_ref[i][j] = 256*(i+1)/(j+1);
 +        s->last_picture[i] = av_frame_alloc();
 +        if (!s->last_picture[i])
 +            goto fail;
 +    }
 +
 +    s->mconly_picture = av_frame_alloc();
 +    s->current_picture = av_frame_alloc();
 +    if (!s->mconly_picture || !s->current_picture)
 +        goto fail;
 +
 +    return 0;
 +fail:
 +    return AVERROR(ENOMEM);
 +}
 +
 +int ff_snow_common_init_after_header(AVCodecContext *avctx) {
 +    SnowContext *s = avctx->priv_data;
 +    int plane_index, level, orientation;
 +    int ret, emu_buf_size;
 +
 +    if(!s->scratchbuf) {
 +        if ((ret = ff_get_buffer(s->avctx, s->mconly_picture,
 +                                 AV_GET_BUFFER_FLAG_REF)) < 0)
 +            return ret;
 +        FF_ALLOCZ_OR_GOTO(avctx, s->scratchbuf, FFMAX(s->mconly_picture->linesize[0], 2*avctx->width+256)*7*MB_SIZE, fail);
 +        emu_buf_size = FFMAX(s->mconly_picture->linesize[0], 2*avctx->width+256) * (2 * MB_SIZE + HTAPS_MAX - 1);
 +        FF_ALLOC_OR_GOTO(avctx, s->emu_edge_buffer, emu_buf_size, fail);
 +    }
 +
 +    if(s->mconly_picture->format != avctx->pix_fmt) {
 +        av_log(avctx, AV_LOG_ERROR, "pixel format changed\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    for(plane_index=0; plane_index < s->nb_planes; plane_index++){
 +        int w= s->avctx->width;
 +        int h= s->avctx->height;
 +
 +        if(plane_index){
 +            w>>= s->chroma_h_shift;
 +            h>>= s->chroma_v_shift;
 +        }
 +        s->plane[plane_index].width = w;
 +        s->plane[plane_index].height= h;
 +
 +        for(level=s->spatial_decomposition_count-1; level>=0; level--){
 +            for(orientation=level ? 1 : 0; orientation<4; orientation++){
 +                SubBand *b= &s->plane[plane_index].band[level][orientation];
 +
 +                b->buf= s->spatial_dwt_buffer;
 +                b->level= level;
 +                b->stride= s->plane[plane_index].width << (s->spatial_decomposition_count - level);
 +                b->width = (w + !(orientation&1))>>1;
 +                b->height= (h + !(orientation>1))>>1;
 +
 +                b->stride_line = 1 << (s->spatial_decomposition_count - level);
 +                b->buf_x_offset = 0;
 +                b->buf_y_offset = 0;
 +
 +                if(orientation&1){
 +                    b->buf += (w+1)>>1;
 +                    b->buf_x_offset = (w+1)>>1;
 +                }
 +                if(orientation>1){
 +                    b->buf += b->stride>>1;
 +                    b->buf_y_offset = b->stride_line >> 1;
 +                }
 +                b->ibuf= s->spatial_idwt_buffer + (b->buf - s->spatial_dwt_buffer);
 +
 +                if(level)
 +                    b->parent= &s->plane[plane_index].band[level-1][orientation];
 +                //FIXME avoid this realloc
 +                av_freep(&b->x_coeff);
 +                b->x_coeff=av_mallocz(((b->width+1) * b->height+1)*sizeof(x_and_coeff));
 +                if (!b->x_coeff)
 +                    goto fail;
 +            }
 +            w= (w+1)>>1;
 +            h= (h+1)>>1;
 +        }
 +    }
 +
 +    return 0;
 +fail:
 +    return AVERROR(ENOMEM);
 +}
 +
 +#define USE_HALFPEL_PLANE 0
 +
 +static int halfpel_interpol(SnowContext *s, uint8_t *halfpel[4][4], AVFrame *frame){
 +    int p,x,y;
 +
 +    for(p=0; p < s->nb_planes; p++){
 +        int is_chroma= !!p;
 +        int w= is_chroma ? s->avctx->width >>s->chroma_h_shift : s->avctx->width;
 +        int h= is_chroma ? s->avctx->height>>s->chroma_v_shift : s->avctx->height;
 +        int ls= frame->linesize[p];
 +        uint8_t *src= frame->data[p];
 +
 +        halfpel[1][p] = (uint8_t*) av_malloc(ls * (h + 2 * EDGE_WIDTH)) + EDGE_WIDTH * (1 + ls);
 +        halfpel[2][p] = (uint8_t*) av_malloc(ls * (h + 2 * EDGE_WIDTH)) + EDGE_WIDTH * (1 + ls);
 +        halfpel[3][p] = (uint8_t*) av_malloc(ls * (h + 2 * EDGE_WIDTH)) + EDGE_WIDTH * (1 + ls);
 +        if (!halfpel[1][p] || !halfpel[2][p] || !halfpel[3][p])
 +            return AVERROR(ENOMEM);
 +
 +        halfpel[0][p]= src;
 +        for(y=0; y<h; y++){
 +            for(x=0; x<w; x++){
 +                int i= y*ls + x;
 +
 +                halfpel[1][p][i]= (20*(src[i] + src[i+1]) - 5*(src[i-1] + src[i+2]) + (src[i-2] + src[i+3]) + 16 )>>5;
 +            }
 +        }
 +        for(y=0; y<h; y++){
 +            for(x=0; x<w; x++){
 +                int i= y*ls + x;
 +
 +                halfpel[2][p][i]= (20*(src[i] + src[i+ls]) - 5*(src[i-ls] + src[i+2*ls]) + (src[i-2*ls] + src[i+3*ls]) + 16 )>>5;
 +            }
 +        }
 +        src= halfpel[1][p];
 +        for(y=0; y<h; y++){
 +            for(x=0; x<w; x++){
 +                int i= y*ls + x;
 +
 +                halfpel[3][p][i]= (20*(src[i] + src[i+ls]) - 5*(src[i-ls] + src[i+2*ls]) + (src[i-2*ls] + src[i+3*ls]) + 16 )>>5;
 +            }
 +        }
 +
 +//FIXME border!
 +    }
 +    return 0;
 +}
 +
 +void ff_snow_release_buffer(AVCodecContext *avctx)
 +{
 +    SnowContext *s = avctx->priv_data;
 +    int i;
 +
 +    if(s->last_picture[s->max_ref_frames-1]->data[0]){
 +        av_frame_unref(s->last_picture[s->max_ref_frames-1]);
 +        for(i=0; i<9; i++)
 +            if(s->halfpel_plane[s->max_ref_frames-1][1+i/3][i%3])
 +                av_free(s->halfpel_plane[s->max_ref_frames-1][1+i/3][i%3] - EDGE_WIDTH*(1+s->current_picture->linesize[i%3]));
 +    }
 +}
 +
 +int ff_snow_frame_start(SnowContext *s){
 +   AVFrame *tmp;
 +   int i, ret;
 +   int w= s->avctx->width; //FIXME round up to x16 ?
 +   int h= s->avctx->height;
 +
 +    if (s->current_picture->data[0] && !(s->avctx->flags&CODEC_FLAG_EMU_EDGE)) {
 +        s->dsp.draw_edges(s->current_picture->data[0],
 +                          s->current_picture->linesize[0], w   , h   ,
 +                          EDGE_WIDTH  , EDGE_WIDTH  , EDGE_TOP | EDGE_BOTTOM);
 +        if (s->current_picture->data[2]) {
 +            s->dsp.draw_edges(s->current_picture->data[1],
 +                            s->current_picture->linesize[1], w>>s->chroma_h_shift, h>>s->chroma_v_shift,
 +                            EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM);
 +            s->dsp.draw_edges(s->current_picture->data[2],
 +                            s->current_picture->linesize[2], w>>s->chroma_h_shift, h>>s->chroma_v_shift,
 +                            EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM);
 +        }
 +    }
 +
 +    ff_snow_release_buffer(s->avctx);
 +
 +    tmp= s->last_picture[s->max_ref_frames-1];
 +    for(i=s->max_ref_frames-1; i>0; i--)
 +        s->last_picture[i] = s->last_picture[i-1];
 +    memmove(s->halfpel_plane+1, s->halfpel_plane, (s->max_ref_frames-1)*sizeof(void*)*4*4);
 +    if(USE_HALFPEL_PLANE && s->current_picture->data[0]) {
 +        if((ret = halfpel_interpol(s, s->halfpel_plane[0], s->current_picture)) < 0)
 +            return ret;
 +    }
 +    s->last_picture[0] = s->current_picture;
 +    s->current_picture = tmp;
 +
 +    if(s->keyframe){
 +        s->ref_frames= 0;
 +    }else{
 +        int i;
 +        for(i=0; i<s->max_ref_frames && s->last_picture[i]->data[0]; i++)
 +            if(i && s->last_picture[i-1]->key_frame)
 +                break;
 +        s->ref_frames= i;
 +        if(s->ref_frames==0){
 +            av_log(s->avctx,AV_LOG_ERROR, "No reference frames\n");
 +            return -1;
 +        }
 +    }
 +
 +    if ((ret = ff_get_buffer(s->avctx, s->current_picture, AV_GET_BUFFER_FLAG_REF)) < 0)
 +        return ret;
 +
 +    s->current_picture->key_frame= s->keyframe;
 +
 +    return 0;
 +}
 +
 +av_cold void ff_snow_common_end(SnowContext *s)
 +{
 +    int plane_index, level, orientation, i;
 +
 +    av_freep(&s->spatial_dwt_buffer);
 +    av_freep(&s->temp_dwt_buffer);
 +    av_freep(&s->spatial_idwt_buffer);
 +    av_freep(&s->temp_idwt_buffer);
 +    av_freep(&s->run_buffer);
 +
 +    s->m.me.temp= NULL;
 +    av_freep(&s->m.me.scratchpad);
 +    av_freep(&s->m.me.map);
 +    av_freep(&s->m.me.score_map);
 +    av_freep(&s->m.obmc_scratchpad);
 +
 +    av_freep(&s->block);
 +    av_freep(&s->scratchbuf);
 +    av_freep(&s->emu_edge_buffer);
 +
 +    for(i=0; i<MAX_REF_FRAMES; i++){
 +        av_freep(&s->ref_mvs[i]);
 +        av_freep(&s->ref_scores[i]);
 +        if(s->last_picture[i]->data[0]) {
 +            av_assert0(s->last_picture[i]->data[0] != s->current_picture->data[0]);
 +        }
 +        av_frame_free(&s->last_picture[i]);
 +    }
 +
 +    for(plane_index=0; plane_index < s->nb_planes; plane_index++){
 +        for(level=s->spatial_decomposition_count-1; level>=0; level--){
 +            for(orientation=level ? 1 : 0; orientation<4; orientation++){
 +                SubBand *b= &s->plane[plane_index].band[level][orientation];
 +
 +                av_freep(&b->x_coeff);
 +            }
 +        }
 +    }
 +    av_frame_free(&s->mconly_picture);
 +    av_frame_free(&s->current_picture);
 +}
Simple merge
@@@ -460,12 -458,14 +460,16 @@@ static void vc1_mc_1mv(VC1Context *v, i
                                   src_x - s->mspel, src_y - s->mspel,
                                   s->h_edge_pos, v_edge_pos);
          srcY = s->edge_emu_buffer;
-         s->vdsp.emulated_edge_mc(uvbuf, s->uvlinesize, srcU, s->uvlinesize,
-                                  8 + 1, 8 + 1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf, srcU,
+                                  s->uvlinesize, s->uvlinesize,
+                                  8 + 1, 8 + 1,
 -                                 uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, v_edge_pos >> 1);
-         s->vdsp.emulated_edge_mc(uvbuf + 16, s->uvlinesize, srcV, s->uvlinesize,
-                                  8 + 1, 8 + 1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf + 16, srcV,
+                                  s->uvlinesize, s->uvlinesize,
+                                  8 + 1, 8 + 1,
 -                                 uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, v_edge_pos >> 1);
          srcU = uvbuf;
          srcV = uvbuf + 16;
          /* if we deal with range reduction we need to scale source blocks */
@@@ -1973,12 -1971,14 +1983,16 @@@ static void vc1_interp_mc(VC1Context *v
                                   src_x - s->mspel, src_y - s->mspel,
                                   s->h_edge_pos, v_edge_pos);
          srcY = s->edge_emu_buffer;
-         s->vdsp.emulated_edge_mc(uvbuf, s->uvlinesize, srcU, s->uvlinesize,
-                                  8 + 1, 8 + 1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf, srcU,
+                                  s->uvlinesize, s->uvlinesize,
+                                  8 + 1, 8 + 1,
 -                                 uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, v_edge_pos >> 1);
-         s->vdsp.emulated_edge_mc(uvbuf + 16, s->uvlinesize, srcV, s->uvlinesize,
-                                  8 + 1, 8 + 1, uvsrc_x, uvsrc_y,
+         s->vdsp.emulated_edge_mc(uvbuf + 16, srcV,
+                                  s->uvlinesize, s->uvlinesize,
+                                  8 + 1, 8 + 1,
 -                                 uvsrc_x, uvsrc_y, s->h_edge_pos >> 1, v_edge_pos >> 1);
++                                 uvsrc_x, uvsrc_y,
 +                                 s->h_edge_pos >> 1, v_edge_pos >> 1);
          srcU = uvbuf;
          srcV = uvbuf + 16;
          /* if we deal with range reduction we need to scale source blocks */
  #include <stddef.h>
  #include <stdint.h>
  
- void ff_emulated_edge_mc_ ## depth(uint8_t *dst, ptrdiff_t dst_stride, \
-                                    const uint8_t *src, ptrdiff_t src_stride, \
 +#define EMULATED_EDGE(depth) \
++void ff_emulated_edge_mc_ ## depth(uint8_t *dst, const uint8_t *src, \
++                                   ptrdiff_t dst_stride, ptrdiff_t src_stride, \
 +                                   int block_w, int block_h,\
 +                                   int src_x, int src_y, int w, int h);
 +
 +EMULATED_EDGE(8)
 +EMULATED_EDGE(16)
 +
  typedef struct VideoDSPContext {
      /**
       * Copy a rectangular area of samples to a temporary buffer and replicate
       * the border samples.
       *
 -     * @param buf destination buffer
 +     * @param dst destination buffer
 +     * @param dst_stride number of bytes between 2 vertically adjacent samples
 +     *                   in destination buffer
       * @param src source buffer
-      * @param src_stride number of bytes between 2 vertically adjacent samples
-      *                   in source buffer
 -     * @param buf_linesize number of bytes between 2 vertically adjacent
++     * @param dst_linesize number of bytes between 2 vertically adjacent
+      *                     samples in the destination buffer
+      * @param src_linesize number of bytes between 2 vertically adjacent
+      *                     samples in both the source buffer
       * @param block_w width of block
       * @param block_h height of block
       * @param src_x x coordinate of the top left sample of the block in the
@@@ -58,8 -49,9 +60,9 @@@
       * @param w width of the source buffer
       * @param h height of the source buffer
       */
-     void (*emulated_edge_mc)(uint8_t *dst, ptrdiff_t dst_stride,
-                              const uint8_t *src, ptrdiff_t src_stride,
 -    void (*emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
 -                             ptrdiff_t buf_linesize,
++    void (*emulated_edge_mc)(uint8_t *dst, const uint8_t *src,
++                             ptrdiff_t dst_linesize,
+                              ptrdiff_t src_linesize,
                               int block_w, int block_h,
                               int src_x, int src_y, int w, int h);
  
   */
  
  #include "bit_depth_template.c"
- void FUNC(ff_emulated_edge_mc)(uint8_t *buf, ptrdiff_t buf_stride,
-                                const uint8_t *src, ptrdiff_t src_stride,
 -
 -static void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
 -                                      ptrdiff_t buf_linesize,
 -                                      ptrdiff_t src_linesize,
 -                                      int block_w, int block_h,
 -                                      int src_x, int src_y, int w, int h)
++void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
++                               ptrdiff_t buf_linesize,
++                               ptrdiff_t src_linesize,
 +                               int block_w, int block_h,
 +                               int src_x, int src_y, int w, int h)
  {
      int x, y;
      int start_y, start_x, end_y, end_x;
  
 +    if (!w || !h)
 +        return;
 +
      if (src_y >= h) {
-         src -= src_y * src_stride;
-         src += (h - 1) * src_stride;
 -        src  += (h - 1 - src_y) * src_linesize;
++        src -= src_y * src_linesize;
++        src += (h - 1) * src_linesize;
          src_y = h - 1;
      } else if (src_y <= -block_h) {
-         src -= src_y * src_stride;
-         src += (1 - block_h) * src_stride;
 -        src  += (1 - block_h - src_y) * src_linesize;
++        src -= src_y * src_linesize;
++        src += (1 - block_h) * src_linesize;
          src_y = 1 - block_h;
      }
      if (src_x >= w) {
      start_x = FFMAX(0, -src_x);
      end_y = FFMIN(block_h, h-src_y);
      end_x = FFMIN(block_w, w-src_x);
 -    assert(start_y < end_y && block_h);
 -    assert(start_x < end_x && block_w);
 +    av_assert2(start_y < end_y && block_h);
 +    av_assert2(start_x < end_x && block_w);
  
      w    = end_x - start_x;
-     src += start_y * src_stride + start_x * sizeof(pixel);
+     src += start_y * src_linesize + start_x * sizeof(pixel);
      buf += start_x * sizeof(pixel);
  
      // top
Simple merge
@@@ -339,11 -339,12 +339,12 @@@ static void vp56_mc(VP56Context *s, in
  
      if (x<0 || x+12>=s->plane_width[plane] ||
          y<0 || y+12>=s->plane_height[plane]) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, stride,
-                             src + s->block_offset[b] + (dy-2)*stride + (dx-2),
-                             stride, 12, 12, x, y,
-                             s->plane_width[plane],
-                             s->plane_height[plane]);
+         s->vdsp.emulated_edge_mc(s->edge_emu_buffer,
 -                            src + s->block_offset[b] + (dy-2)*stride + (dx-2),
 -                            stride, stride,
 -                            12, 12, x, y,
 -                            s->plane_width[plane],
 -                            s->plane_height[plane]);
++                                 src + s->block_offset[b] + (dy-2)*stride + (dx-2),
++                                 stride, stride,
++                                 12, 12, x, y,
++                                 s->plane_width[plane],
++                                 s->plane_height[plane]);
          src_block = s->edge_emu_buffer;
          src_offset = 2 + 2*stride;
      } else if (deblock_filtering) {
@@@ -1196,15 -1196,14 +1196,16 @@@ void vp8_mc_luma(VP8Context *s, VP8Thre
          src += y_off * linesize + x_off;
          if (x_off < mx_idx || x_off >= width  - block_w - subpel_idx[2][mx] ||
              y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) {
-             s->vdsp.emulated_edge_mc(td->edge_emu_buffer, 32,
-                                      src - my_idx * linesize - mx_idx, linesize,
+             s->vdsp.emulated_edge_mc(td->edge_emu_buffer,
+                                      src - my_idx * linesize - mx_idx,
 -                                     linesize, linesize,
 -                                     block_w + subpel_idx[1][mx], block_h + subpel_idx[1][my],
++                                     32, linesize,
 +                                     block_w + subpel_idx[1][mx],
 +                                     block_h + subpel_idx[1][my],
                                       x_off - mx_idx, y_off - my_idx, width, height);
 -            src = td->edge_emu_buffer + mx_idx + linesize * my_idx;
 +            src = td->edge_emu_buffer + mx_idx + 32 * my_idx;
 +            src_linesize = 32;
          }
 -        mc_func[my_idx][mx_idx](dst, linesize, src, linesize, block_h, mx, my);
 +        mc_func[my_idx][mx_idx](dst, linesize, src, src_linesize, block_h, mx, my);
      } else {
          ff_thread_await_progress(ref, (3 + y_off + block_h) >> 4, 0);
          mc_func[0][0](dst, linesize, src + y_off * linesize + x_off, linesize, block_h, 0, 0);
@@@ -1249,21 -1248,21 +1250,23 @@@ void vp8_mc_chroma(VP8Context *s, VP8Th
          ff_thread_await_progress(ref, (3 + y_off + block_h + subpel_idx[2][my]) >> 3, 0);
          if (x_off < mx_idx || x_off >= width  - block_w - subpel_idx[2][mx] ||
              y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) {
-             s->vdsp.emulated_edge_mc(td->edge_emu_buffer, 32,
-                                      src1 - my_idx * linesize - mx_idx, linesize,
+             s->vdsp.emulated_edge_mc(td->edge_emu_buffer,
+                                      src1 - my_idx * linesize - mx_idx,
 -                                     linesize, linesize,
 -                                     block_w + subpel_idx[1][mx], block_h + subpel_idx[1][my],
++                                     32, linesize,
 +                                     block_w + subpel_idx[1][mx],
 +                                     block_h + subpel_idx[1][my],
                                       x_off - mx_idx, y_off - my_idx, width, height);
 -            src1 = td->edge_emu_buffer + mx_idx + linesize * my_idx;
 -            mc_func[my_idx][mx_idx](dst1, linesize, src1, linesize, block_h, mx, my);
 +            src1 = td->edge_emu_buffer + mx_idx + 32 * my_idx;
 +            mc_func[my_idx][mx_idx](dst1, linesize, src1, 32, block_h, mx, my);
  
-             s->vdsp.emulated_edge_mc(td->edge_emu_buffer, 32,
-                                      src2 - my_idx * linesize - mx_idx, linesize,
+             s->vdsp.emulated_edge_mc(td->edge_emu_buffer,
+                                      src2 - my_idx * linesize - mx_idx,
 -                                     linesize, linesize,
 -                                     block_w + subpel_idx[1][mx], block_h + subpel_idx[1][my],
++                                     32, linesize,
 +                                     block_w + subpel_idx[1][mx],
 +                                     block_h + subpel_idx[1][my],
                                       x_off - mx_idx, y_off - my_idx, width, height);
 -            src2 = td->edge_emu_buffer + mx_idx + linesize * my_idx;
 -            mc_func[my_idx][mx_idx](dst2, linesize, src2, linesize, block_h, mx, my);
 +            src2 = td->edge_emu_buffer + mx_idx + 32 * my_idx;
 +            mc_func[my_idx][mx_idx](dst2, linesize, src2, 32, block_h, mx, my);
          } else {
              mc_func[my_idx][mx_idx](dst1, linesize, src1, linesize, block_h, mx, my);
              mc_func[my_idx][mx_idx](dst2, linesize, src2, linesize, block_h, mx, my);
index 4f9c1ad,0000000..bd288ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,3584 -1,0 +1,3586 @@@
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, 80,
 +/*
 + * VP9 compatible video decoder
 + *
 + * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com>
 + * Copyright (C) 2013 Clément Bœsch <u pkh me>
 + *
 + * 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 "avcodec.h"
 +#include "get_bits.h"
 +#include "internal.h"
 +#include "videodsp.h"
 +#include "vp56.h"
 +#include "vp9.h"
 +#include "vp9data.h"
 +#include "vp9dsp.h"
 +#include "libavutil/avassert.h"
 +
 +#define VP9_SYNCCODE 0x498342
 +
 +enum CompPredMode {
 +    PRED_SINGLEREF,
 +    PRED_COMPREF,
 +    PRED_SWITCHABLE,
 +};
 +
 +enum BlockLevel {
 +    BL_64X64,
 +    BL_32X32,
 +    BL_16X16,
 +    BL_8X8,
 +};
 +
 +enum BlockSize {
 +    BS_64x64,
 +    BS_64x32,
 +    BS_32x64,
 +    BS_32x32,
 +    BS_32x16,
 +    BS_16x32,
 +    BS_16x16,
 +    BS_16x8,
 +    BS_8x16,
 +    BS_8x8,
 +    BS_8x4,
 +    BS_4x8,
 +    BS_4x4,
 +    N_BS_SIZES,
 +};
 +
 +struct VP9mvrefPair {
 +    VP56mv mv[2];
 +    int8_t ref[2];
 +};
 +
 +struct VP9Filter {
 +    uint8_t level[8 * 8];
 +    uint8_t /* bit=col */ mask[2 /* 0=y, 1=uv */][2 /* 0=col, 1=row */]
 +                              [8 /* rows */][4 /* 0=16, 1=8, 2=4, 3=inner4 */];
 +};
 +
 +typedef struct VP9Block {
 +    uint8_t seg_id, intra, comp, ref[2], mode[4], uvmode, skip;
 +    enum FilterMode filter;
 +    VP56mv mv[4 /* b_idx */][2 /* ref */];
 +    enum BlockSize bs;
 +    enum TxfmMode tx, uvtx;
 +
 +    int row, row7, col, col7;
 +    uint8_t *dst[3];
 +    ptrdiff_t y_stride, uv_stride;
 +} VP9Block;
 +
 +typedef struct VP9Context {
 +    VP9DSPContext dsp;
 +    VideoDSPContext vdsp;
 +    GetBitContext gb;
 +    VP56RangeCoder c;
 +    VP56RangeCoder *c_b;
 +    unsigned c_b_size;
 +    VP9Block b;
 +
 +    // bitstream header
 +    uint8_t profile;
 +    uint8_t keyframe, last_keyframe;
 +    uint8_t invisible, last_invisible;
 +    uint8_t use_last_frame_mvs;
 +    uint8_t errorres;
 +    uint8_t colorspace;
 +    uint8_t fullrange;
 +    uint8_t intraonly;
 +    uint8_t resetctx;
 +    uint8_t refreshrefmask;
 +    uint8_t highprecisionmvs;
 +    enum FilterMode filtermode;
 +    uint8_t allowcompinter;
 +    uint8_t fixcompref;
 +    uint8_t refreshctx;
 +    uint8_t parallelmode;
 +    uint8_t framectxid;
 +    uint8_t refidx[3];
 +    uint8_t signbias[3];
 +    uint8_t varcompref[2];
 +    AVFrame *refs[8], *f, *fb[10];
 +
 +    struct {
 +        uint8_t level;
 +        int8_t sharpness;
 +        uint8_t lim_lut[64];
 +        uint8_t mblim_lut[64];
 +    } filter;
 +    struct {
 +        uint8_t enabled;
 +        int8_t mode[2];
 +        int8_t ref[4];
 +    } lf_delta;
 +    uint8_t yac_qi;
 +    int8_t ydc_qdelta, uvdc_qdelta, uvac_qdelta;
 +    uint8_t lossless;
 +    struct {
 +        uint8_t enabled;
 +        uint8_t temporal;
 +        uint8_t absolute_vals;
 +        uint8_t update_map;
 +        struct {
 +            uint8_t q_enabled;
 +            uint8_t lf_enabled;
 +            uint8_t ref_enabled;
 +            uint8_t skip_enabled;
 +            uint8_t ref_val;
 +            int16_t q_val;
 +            int8_t lf_val;
 +            int16_t qmul[2][2];
 +            uint8_t lflvl[4][2];
 +        } feat[8];
 +    } segmentation;
 +    struct {
 +        unsigned log2_tile_cols, log2_tile_rows;
 +        unsigned tile_cols, tile_rows;
 +        unsigned tile_row_start, tile_row_end, tile_col_start, tile_col_end;
 +    } tiling;
 +    unsigned sb_cols, sb_rows, rows, cols;
 +    struct {
 +        prob_context p;
 +        uint8_t coef[4][2][2][6][6][3];
 +    } prob_ctx[4];
 +    struct {
 +        prob_context p;
 +        uint8_t coef[4][2][2][6][6][11];
 +        uint8_t seg[7];
 +        uint8_t segpred[3];
 +    } prob;
 +    struct {
 +        unsigned y_mode[4][10];
 +        unsigned uv_mode[10][10];
 +        unsigned filter[4][3];
 +        unsigned mv_mode[7][4];
 +        unsigned intra[4][2];
 +        unsigned comp[5][2];
 +        unsigned single_ref[5][2][2];
 +        unsigned comp_ref[5][2];
 +        unsigned tx32p[2][4];
 +        unsigned tx16p[2][3];
 +        unsigned tx8p[2][2];
 +        unsigned skip[3][2];
 +        unsigned mv_joint[4];
 +        struct {
 +            unsigned sign[2];
 +            unsigned classes[11];
 +            unsigned class0[2];
 +            unsigned bits[10][2];
 +            unsigned class0_fp[2][4];
 +            unsigned fp[4];
 +            unsigned class0_hp[2];
 +            unsigned hp[2];
 +        } mv_comp[2];
 +        unsigned partition[4][4][4];
 +        unsigned coef[4][2][2][6][6][3];
 +        unsigned eob[4][2][2][6][6][2];
 +    } counts;
 +    enum TxfmMode txfmmode;
 +    enum CompPredMode comppredmode;
 +
 +    // contextual (left/above) cache
 +    uint8_t left_partition_ctx[8], *above_partition_ctx;
 +    uint8_t left_mode_ctx[16], *above_mode_ctx;
 +    // FIXME maybe merge some of the below in a flags field?
 +    uint8_t left_y_nnz_ctx[16], *above_y_nnz_ctx;
 +    uint8_t left_uv_nnz_ctx[2][8], *above_uv_nnz_ctx[2];
 +    uint8_t left_skip_ctx[8], *above_skip_ctx; // 1bit
 +    uint8_t left_txfm_ctx[8], *above_txfm_ctx; // 2bit
 +    uint8_t left_segpred_ctx[8], *above_segpred_ctx; // 1bit
 +    uint8_t left_intra_ctx[8], *above_intra_ctx; // 1bit
 +    uint8_t left_comp_ctx[8], *above_comp_ctx; // 1bit
 +    uint8_t left_ref_ctx[8], *above_ref_ctx; // 2bit
 +    uint8_t left_filter_ctx[8], *above_filter_ctx;
 +    VP56mv left_mv_ctx[16][2], (*above_mv_ctx)[2];
 +
 +    // whole-frame cache
 +    uint8_t *intra_pred_data[3];
 +    uint8_t *segmentation_map;
 +    struct VP9mvrefPair *mv[2];
 +    struct VP9Filter *lflvl;
 +    DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer)[71*80];
 +
 +    // block reconstruction intermediates
 +    DECLARE_ALIGNED(32, int16_t, block)[4096];
 +    DECLARE_ALIGNED(32, int16_t, uvblock)[2][1024];
 +    uint8_t eob[256];
 +    uint8_t uveob[2][64];
 +    VP56mv min_mv, max_mv;
 +    DECLARE_ALIGNED(32, uint8_t, tmp_y)[64*64];
 +    DECLARE_ALIGNED(32, uint8_t, tmp_uv)[2][32*32];
 +} VP9Context;
 +
 +static const uint8_t bwh_tab[2][N_BS_SIZES][2] = {
 +    {
 +        { 16, 16 }, { 16, 8 }, { 8, 16 }, { 8, 8 }, { 8, 4 }, { 4, 8 },
 +        { 4, 4 }, { 4, 2 }, { 2, 4 }, { 2, 2 }, { 2, 1 }, { 1, 2 }, { 1, 1 },
 +    }, {
 +        { 8, 8 }, { 8, 4 }, { 4, 8 }, { 4, 4 }, { 4, 2 }, { 2, 4 },
 +        { 2, 2 }, { 2, 1 }, { 1, 2 }, { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
 +    }
 +};
 +
 +static int update_size(AVCodecContext *ctx, int w, int h)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    uint8_t *p;
 +
 +    if (s->above_partition_ctx && w == ctx->width && h == ctx->height)
 +        return 0;
 +
 +    ctx->width  = w;
 +    ctx->height = h;
 +    s->sb_cols  = (w + 63) >> 6;
 +    s->sb_rows  = (h + 63) >> 6;
 +    s->cols     = (w + 7) >> 3;
 +    s->rows     = (h + 7) >> 3;
 +
 +#define assign(var, type, n) var = (type) p; p += s->sb_cols * n * sizeof(*var)
 +    av_freep(&s->above_partition_ctx);
 +    p = av_malloc(s->sb_cols * (240 + sizeof(*s->lflvl) + 16 * sizeof(*s->above_mv_ctx) +
 +                                64 * s->sb_rows * (1 + sizeof(*s->mv[0]) * 2)));
 +    if (!p)
 +        return AVERROR(ENOMEM);
 +    assign(s->above_partition_ctx, uint8_t *,              8);
 +    assign(s->above_skip_ctx,      uint8_t *,              8);
 +    assign(s->above_txfm_ctx,      uint8_t *,              8);
 +    assign(s->above_mode_ctx,      uint8_t *,             16);
 +    assign(s->above_y_nnz_ctx,     uint8_t *,             16);
 +    assign(s->above_uv_nnz_ctx[0], uint8_t *,              8);
 +    assign(s->above_uv_nnz_ctx[1], uint8_t *,              8);
 +    assign(s->intra_pred_data[0],  uint8_t *,             64);
 +    assign(s->intra_pred_data[1],  uint8_t *,             32);
 +    assign(s->intra_pred_data[2],  uint8_t *,             32);
 +    assign(s->above_segpred_ctx,   uint8_t *,              8);
 +    assign(s->above_intra_ctx,     uint8_t *,              8);
 +    assign(s->above_comp_ctx,      uint8_t *,              8);
 +    assign(s->above_ref_ctx,       uint8_t *,              8);
 +    assign(s->above_filter_ctx,    uint8_t *,              8);
 +    assign(s->lflvl,               struct VP9Filter *,     1);
 +    assign(s->above_mv_ctx,        VP56mv(*)[2],          16);
 +    assign(s->segmentation_map,    uint8_t *,             64 * s->sb_rows);
 +    assign(s->mv[0],               struct VP9mvrefPair *, 64 * s->sb_rows);
 +    assign(s->mv[1],               struct VP9mvrefPair *, 64 * s->sb_rows);
 +#undef assign
 +
 +    return 0;
 +}
 +
 +// for some reason the sign bit is at the end, not the start, of a bit sequence
 +static av_always_inline int get_sbits_inv(GetBitContext *gb, int n)
 +{
 +    int v = get_bits(gb, n);
 +    return get_bits1(gb) ? -v : v;
 +}
 +
 +static av_always_inline int inv_recenter_nonneg(int v, int m)
 +{
 +    return v > 2 * m ? v : v & 1 ? m - ((v + 1) >> 1) : m + (v >> 1);
 +}
 +
 +// differential forward probability updates
 +static int update_prob(VP56RangeCoder *c, int p)
 +{
 +    static const int inv_map_table[254] = {
 +          7,  20,  33,  46,  59,  72,  85,  98, 111, 124, 137, 150, 163, 176,
 +        189, 202, 215, 228, 241, 254,   1,   2,   3,   4,   5,   6,   8,   9,
 +         10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  21,  22,  23,  24,
 +         25,  26,  27,  28,  29,  30,  31,  32,  34,  35,  36,  37,  38,  39,
 +         40,  41,  42,  43,  44,  45,  47,  48,  49,  50,  51,  52,  53,  54,
 +         55,  56,  57,  58,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
 +         70,  71,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,
 +         86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  99, 100,
 +        101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115,
 +        116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130,
 +        131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 143, 144, 145,
 +        146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
 +        161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
 +        177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191,
 +        192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206,
 +        207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221,
 +        222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236,
 +        237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
 +        252, 253,
 +    };
 +    int d;
 +
 +    /* This code is trying to do a differential probability update. For a
 +     * current probability A in the range [1, 255], the difference to a new
 +     * probability of any value can be expressed differentially as 1-A,255-A
 +     * where some part of this (absolute range) exists both in positive as
 +     * well as the negative part, whereas another part only exists in one
 +     * half. We're trying to code this shared part differentially, i.e.
 +     * times two where the value of the lowest bit specifies the sign, and
 +     * the single part is then coded on top of this. This absolute difference
 +     * then again has a value of [0,254], but a bigger value in this range
 +     * indicates that we're further away from the original value A, so we
 +     * can code this as a VLC code, since higher values are increasingly
 +     * unlikely. The first 20 values in inv_map_table[] allow 'cheap, rough'
 +     * updates vs. the 'fine, exact' updates further down the range, which
 +     * adds one extra dimension to this differential update model. */
 +
 +    if (!vp8_rac_get(c)) {
 +        d = vp8_rac_get_uint(c, 4) + 0;
 +    } else if (!vp8_rac_get(c)) {
 +        d = vp8_rac_get_uint(c, 4) + 16;
 +    } else if (!vp8_rac_get(c)) {
 +        d = vp8_rac_get_uint(c, 5) + 32;
 +    } else {
 +        d = vp8_rac_get_uint(c, 7);
 +        if (d >= 65)
 +            d = (d << 1) - 65 + vp8_rac_get(c);
 +        d += 64;
 +    }
 +
 +    return p <= 128 ? 1 + inv_recenter_nonneg(inv_map_table[d], p - 1) :
 +                    255 - inv_recenter_nonneg(inv_map_table[d], 255 - p);
 +}
 +
 +static int decode_frame_header(AVCodecContext *ctx,
 +                               const uint8_t *data, int size, int *ref)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    int c, i, j, k, l, m, n, w, h, max, size2, res, sharp;
 +    const uint8_t *data2;
 +
 +    /* general header */
 +    if ((res = init_get_bits8(&s->gb, data, size)) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Failed to intialize bitstream reader\n");
 +        return res;
 +    }
 +    if (get_bits(&s->gb, 2) != 0x2) { // frame marker
 +        av_log(ctx, AV_LOG_ERROR, "Invalid frame marker\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +    s->profile = get_bits1(&s->gb);
 +    if (get_bits1(&s->gb)) { // reserved bit
 +        av_log(ctx, AV_LOG_ERROR, "Reserved bit should be zero\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +    if (get_bits1(&s->gb)) {
 +        *ref = get_bits(&s->gb, 3);
 +        return 0;
 +    }
 +    s->last_keyframe  = s->keyframe;
 +    s->keyframe       = !get_bits1(&s->gb);
 +    s->last_invisible = s->invisible;
 +    s->invisible      = !get_bits1(&s->gb);
 +    s->errorres       = get_bits1(&s->gb);
 +    // FIXME disable this upon resolution change
 +    s->use_last_frame_mvs = !s->errorres && !s->last_invisible;
 +    if (s->keyframe) {
 +        if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode
 +            av_log(ctx, AV_LOG_ERROR, "Invalid sync code\n");
 +            return AVERROR_INVALIDDATA;
 +        }
 +        s->colorspace = get_bits(&s->gb, 3);
 +        if (s->colorspace == 7) { // RGB = profile 1
 +            av_log(ctx, AV_LOG_ERROR, "RGB not supported in profile 0\n");
 +            return AVERROR_INVALIDDATA;
 +        }
 +        s->fullrange  = get_bits1(&s->gb);
 +        // for profile 1, here follows the subsampling bits
 +        s->refreshrefmask = 0xff;
 +        w = get_bits(&s->gb, 16) + 1;
 +        h = get_bits(&s->gb, 16) + 1;
 +        if (get_bits1(&s->gb)) // display size
 +            skip_bits(&s->gb, 32);
 +    } else {
 +        s->intraonly  = s->invisible ? get_bits1(&s->gb) : 0;
 +        s->resetctx   = s->errorres ? 0 : get_bits(&s->gb, 2);
 +        if (s->intraonly) {
 +            if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode
 +                av_log(ctx, AV_LOG_ERROR, "Invalid sync code\n");
 +                return AVERROR_INVALIDDATA;
 +            }
 +            s->refreshrefmask = get_bits(&s->gb, 8);
 +            w = get_bits(&s->gb, 16) + 1;
 +            h = get_bits(&s->gb, 16) + 1;
 +            if (get_bits1(&s->gb)) // display size
 +                skip_bits(&s->gb, 32);
 +        } else {
 +            s->refreshrefmask = get_bits(&s->gb, 8);
 +            s->refidx[0]      = get_bits(&s->gb, 3);
 +            s->signbias[0]    = get_bits1(&s->gb);
 +            s->refidx[1]      = get_bits(&s->gb, 3);
 +            s->signbias[1]    = get_bits1(&s->gb);
 +            s->refidx[2]      = get_bits(&s->gb, 3);
 +            s->signbias[2]    = get_bits1(&s->gb);
 +            if (!s->refs[s->refidx[0]] || !s->refs[s->refidx[1]] ||
 +                !s->refs[s->refidx[2]]) {
 +                av_log(ctx, AV_LOG_ERROR, "Not all references are available\n");
 +                return AVERROR_INVALIDDATA;
 +            }
 +            if (get_bits1(&s->gb)) {
 +                w = s->refs[s->refidx[0]]->width;
 +                h = s->refs[s->refidx[0]]->height;
 +            } else if (get_bits1(&s->gb)) {
 +                w = s->refs[s->refidx[1]]->width;
 +                h = s->refs[s->refidx[1]]->height;
 +            } else if (get_bits1(&s->gb)) {
 +                w = s->refs[s->refidx[2]]->width;
 +                h = s->refs[s->refidx[2]]->height;
 +            } else {
 +                w = get_bits(&s->gb, 16) + 1;
 +                h = get_bits(&s->gb, 16) + 1;
 +            }
 +            if (get_bits1(&s->gb)) // display size
 +                skip_bits(&s->gb, 32);
 +            s->highprecisionmvs = get_bits1(&s->gb);
 +            s->filtermode = get_bits1(&s->gb) ? FILTER_SWITCHABLE :
 +                                                get_bits(&s->gb, 2);
 +            s->allowcompinter = s->signbias[0] != s->signbias[1] ||
 +                                s->signbias[0] != s->signbias[2];
 +            if (s->allowcompinter) {
 +                if (s->signbias[0] == s->signbias[1]) {
 +                    s->fixcompref    = 2;
 +                    s->varcompref[0] = 0;
 +                    s->varcompref[1] = 1;
 +                } else if (s->signbias[0] == s->signbias[2]) {
 +                    s->fixcompref    = 1;
 +                    s->varcompref[0] = 0;
 +                    s->varcompref[1] = 2;
 +                } else {
 +                    s->fixcompref    = 0;
 +                    s->varcompref[0] = 1;
 +                    s->varcompref[1] = 2;
 +                }
 +            }
 +        }
 +    }
 +    s->refreshctx   = s->errorres ? 0 : get_bits1(&s->gb);
 +    s->parallelmode = s->errorres ? 1 : get_bits1(&s->gb);
 +    s->framectxid   = c = get_bits(&s->gb, 2);
 +
 +    /* loopfilter header data */
 +    s->filter.level = get_bits(&s->gb, 6);
 +    sharp = get_bits(&s->gb, 3);
 +    // if sharpness changed, reinit lim/mblim LUTs. if it didn't change, keep
 +    // the old cache values since they are still valid
 +    if (s->filter.sharpness != sharp)
 +        memset(s->filter.lim_lut, 0, sizeof(s->filter.lim_lut));
 +    s->filter.sharpness = sharp;
 +    if ((s->lf_delta.enabled = get_bits1(&s->gb))) {
 +        if (get_bits1(&s->gb)) {
 +            for (i = 0; i < 4; i++)
 +                if (get_bits1(&s->gb))
 +                    s->lf_delta.ref[i] = get_sbits_inv(&s->gb, 6);
 +            for (i = 0; i < 2; i++)
 +                if (get_bits1(&s->gb))
 +                    s->lf_delta.mode[i] = get_sbits_inv(&s->gb, 6);
 +        }
 +    } else {
 +        memset(&s->lf_delta, 0, sizeof(s->lf_delta));
 +    }
 +
 +    /* quantization header data */
 +    s->yac_qi      = get_bits(&s->gb, 8);
 +    s->ydc_qdelta  = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0;
 +    s->uvdc_qdelta = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0;
 +    s->uvac_qdelta = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0;
 +    s->lossless    = s->yac_qi == 0 && s->ydc_qdelta == 0 &&
 +                     s->uvdc_qdelta == 0 && s->uvac_qdelta == 0;
 +
 +    /* segmentation header info */
 +    if ((s->segmentation.enabled = get_bits1(&s->gb))) {
 +        if ((s->segmentation.update_map = get_bits1(&s->gb))) {
 +            for (i = 0; i < 7; i++)
 +                s->prob.seg[i] = get_bits1(&s->gb) ?
 +                                 get_bits(&s->gb, 8) : 255;
 +            if ((s->segmentation.temporal = get_bits1(&s->gb)))
 +                for (i = 0; i < 3; i++)
 +                    s->prob.segpred[i] = get_bits1(&s->gb) ?
 +                                         get_bits(&s->gb, 8) : 255;
 +        }
 +
 +        if (get_bits1(&s->gb)) {
 +            s->segmentation.absolute_vals = get_bits1(&s->gb);
 +            for (i = 0; i < 8; i++) {
 +                if ((s->segmentation.feat[i].q_enabled = get_bits1(&s->gb)))
 +                    s->segmentation.feat[i].q_val = get_sbits_inv(&s->gb, 8);
 +                if ((s->segmentation.feat[i].lf_enabled = get_bits1(&s->gb)))
 +                    s->segmentation.feat[i].lf_val = get_sbits_inv(&s->gb, 6);
 +                if ((s->segmentation.feat[i].ref_enabled = get_bits1(&s->gb)))
 +                    s->segmentation.feat[i].ref_val = get_bits(&s->gb, 2);
 +                s->segmentation.feat[i].skip_enabled = get_bits1(&s->gb);
 +            }
 +        }
 +    } else {
 +        s->segmentation.feat[0].q_enabled    = 0;
 +        s->segmentation.feat[0].lf_enabled   = 0;
 +        s->segmentation.feat[0].skip_enabled = 0;
 +        s->segmentation.feat[0].ref_enabled  = 0;
 +    }
 +
 +    // set qmul[] based on Y/UV, AC/DC and segmentation Q idx deltas
 +    for (i = 0; i < (s->segmentation.enabled ? 8 : 1); i++) {
 +        int qyac, qydc, quvac, quvdc, lflvl, sh;
 +
 +        if (s->segmentation.feat[i].q_enabled) {
 +            if (s->segmentation.absolute_vals)
 +                qyac = s->segmentation.feat[i].q_val;
 +            else
 +                qyac = s->yac_qi + s->segmentation.feat[i].q_val;
 +        } else {
 +            qyac  = s->yac_qi;
 +        }
 +        qydc  = av_clip_uintp2(qyac + s->ydc_qdelta, 8);
 +        quvdc = av_clip_uintp2(qyac + s->uvdc_qdelta, 8);
 +        quvac = av_clip_uintp2(qyac + s->uvac_qdelta, 8);
 +        qyac  = av_clip_uintp2(qyac, 8);
 +
 +        s->segmentation.feat[i].qmul[0][0] = vp9_dc_qlookup[qydc];
 +        s->segmentation.feat[i].qmul[0][1] = vp9_ac_qlookup[qyac];
 +        s->segmentation.feat[i].qmul[1][0] = vp9_dc_qlookup[quvdc];
 +        s->segmentation.feat[i].qmul[1][1] = vp9_ac_qlookup[quvac];
 +
 +        sh = s->filter.level >= 32;
 +        if (s->segmentation.feat[i].lf_enabled) {
 +            if (s->segmentation.absolute_vals)
 +                lflvl = s->segmentation.feat[i].lf_val;
 +            else
 +                lflvl = s->filter.level + s->segmentation.feat[i].lf_val;
 +        } else {
 +            lflvl  = s->filter.level;
 +        }
 +        s->segmentation.feat[i].lflvl[0][0] =
 +        s->segmentation.feat[i].lflvl[0][1] =
 +            av_clip_uintp2(lflvl + (s->lf_delta.ref[0] << sh), 6);
 +        for (j = 1; j < 4; j++) {
 +            s->segmentation.feat[i].lflvl[j][0] =
 +                av_clip_uintp2(lflvl + ((s->lf_delta.ref[j] +
 +                                         s->lf_delta.mode[0]) << sh), 6);
 +            s->segmentation.feat[i].lflvl[j][1] =
 +                av_clip_uintp2(lflvl + ((s->lf_delta.ref[j] +
 +                                         s->lf_delta.mode[1]) << sh), 6);
 +        }
 +    }
 +
 +    /* tiling info */
 +    if ((res = update_size(ctx, w, h)) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Failed to initialize decoder for %dx%d\n", w, h);
 +        return res;
 +    }
 +    for (s->tiling.log2_tile_cols = 0;
 +         (s->sb_cols >> s->tiling.log2_tile_cols) > 64;
 +         s->tiling.log2_tile_cols++) ;
 +    for (max = 0; (s->sb_cols >> max) >= 4; max++) ;
 +    max = FFMAX(0, max - 1);
 +    while (max > s->tiling.log2_tile_cols) {
 +        if (get_bits1(&s->gb))
 +            s->tiling.log2_tile_cols++;
 +        else
 +            break;
 +    }
 +    s->tiling.log2_tile_rows = decode012(&s->gb);
 +    s->tiling.tile_rows = 1 << s->tiling.log2_tile_rows;
 +    if (s->tiling.tile_cols != (1 << s->tiling.log2_tile_cols)) {
 +        s->tiling.tile_cols = 1 << s->tiling.log2_tile_cols;
 +        s->c_b = av_fast_realloc(s->c_b, &s->c_b_size,
 +                                 sizeof(VP56RangeCoder) * s->tiling.tile_cols);
 +        if (!s->c_b) {
 +            av_log(ctx, AV_LOG_ERROR, "Ran out of memory during range coder init\n");
 +            return AVERROR(ENOMEM);
 +        }
 +    }
 +
 +    if (s->keyframe || s->errorres || s->intraonly) {
 +        s->prob_ctx[0].p = s->prob_ctx[1].p = s->prob_ctx[2].p =
 +                           s->prob_ctx[3].p = vp9_default_probs;
 +        memcpy(s->prob_ctx[0].coef, vp9_default_coef_probs,
 +               sizeof(vp9_default_coef_probs));
 +        memcpy(s->prob_ctx[1].coef, vp9_default_coef_probs,
 +               sizeof(vp9_default_coef_probs));
 +        memcpy(s->prob_ctx[2].coef, vp9_default_coef_probs,
 +               sizeof(vp9_default_coef_probs));
 +        memcpy(s->prob_ctx[3].coef, vp9_default_coef_probs,
 +               sizeof(vp9_default_coef_probs));
 +    }
 +
 +    // next 16 bits is size of the rest of the header (arith-coded)
 +    size2 = get_bits(&s->gb, 16);
 +    data2 = align_get_bits(&s->gb);
 +    if (size2 > size - (data2 - data)) {
 +        av_log(ctx, AV_LOG_ERROR, "Invalid compressed header size\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +    ff_vp56_init_range_decoder(&s->c, data2, size2);
 +    if (vp56_rac_get_prob_branchy(&s->c, 128)) { // marker bit
 +        av_log(ctx, AV_LOG_ERROR, "Marker bit was set\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    if (s->keyframe || s->intraonly) {
 +        memset(s->counts.coef, 0, sizeof(s->counts.coef) + sizeof(s->counts.eob));
 +    } else {
 +        memset(&s->counts, 0, sizeof(s->counts));
 +    }
 +    // FIXME is it faster to not copy here, but do it down in the fw updates
 +    // as explicit copies if the fw update is missing (and skip the copy upon
 +    // fw update)?
 +    s->prob.p = s->prob_ctx[c].p;
 +
 +    // txfm updates
 +    if (s->lossless) {
 +        s->txfmmode = TX_4X4;
 +    } else {
 +        s->txfmmode = vp8_rac_get_uint(&s->c, 2);
 +        if (s->txfmmode == 3)
 +            s->txfmmode += vp8_rac_get(&s->c);
 +
 +        if (s->txfmmode == TX_SWITCHABLE) {
 +            for (i = 0; i < 2; i++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.tx8p[i] = update_prob(&s->c, s->prob.p.tx8p[i]);
 +            for (i = 0; i < 2; i++)
 +                for (j = 0; j < 2; j++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.tx16p[i][j] =
 +                            update_prob(&s->c, s->prob.p.tx16p[i][j]);
 +            for (i = 0; i < 2; i++)
 +                for (j = 0; j < 3; j++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.tx32p[i][j] =
 +                            update_prob(&s->c, s->prob.p.tx32p[i][j]);
 +        }
 +    }
 +
 +    // coef updates
 +    for (i = 0; i < 4; i++) {
 +        uint8_t (*ref)[2][6][6][3] = s->prob_ctx[c].coef[i];
 +        if (vp8_rac_get(&s->c)) {
 +            for (j = 0; j < 2; j++)
 +                for (k = 0; k < 2; k++)
 +                    for (l = 0; l < 6; l++)
 +                        for (m = 0; m < 6; m++) {
 +                            uint8_t *p = s->prob.coef[i][j][k][l][m];
 +                            uint8_t *r = ref[j][k][l][m];
 +                            if (m >= 3 && l == 0) // dc only has 3 pt
 +                                break;
 +                            for (n = 0; n < 3; n++) {
 +                                if (vp56_rac_get_prob_branchy(&s->c, 252)) {
 +                                    p[n] = update_prob(&s->c, r[n]);
 +                                } else {
 +                                    p[n] = r[n];
 +                                }
 +                            }
 +                            p[3] = 0;
 +                        }
 +        } else {
 +            for (j = 0; j < 2; j++)
 +                for (k = 0; k < 2; k++)
 +                    for (l = 0; l < 6; l++)
 +                        for (m = 0; m < 6; m++) {
 +                            uint8_t *p = s->prob.coef[i][j][k][l][m];
 +                            uint8_t *r = ref[j][k][l][m];
 +                            if (m > 3 && l == 0) // dc only has 3 pt
 +                                break;
 +                            memcpy(p, r, 3);
 +                            p[3] = 0;
 +                        }
 +        }
 +        if (s->txfmmode == i)
 +            break;
 +    }
 +
 +    // mode updates
 +    for (i = 0; i < 3; i++)
 +        if (vp56_rac_get_prob_branchy(&s->c, 252))
 +            s->prob.p.skip[i] = update_prob(&s->c, s->prob.p.skip[i]);
 +    if (!s->keyframe && !s->intraonly) {
 +        for (i = 0; i < 7; i++)
 +            for (j = 0; j < 3; j++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_mode[i][j] =
 +                        update_prob(&s->c, s->prob.p.mv_mode[i][j]);
 +
 +        if (s->filtermode == FILTER_SWITCHABLE)
 +            for (i = 0; i < 4; i++)
 +                for (j = 0; j < 2; j++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.filter[i][j] =
 +                            update_prob(&s->c, s->prob.p.filter[i][j]);
 +
 +        for (i = 0; i < 4; i++)
 +            if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                s->prob.p.intra[i] = update_prob(&s->c, s->prob.p.intra[i]);
 +
 +        if (s->allowcompinter) {
 +            s->comppredmode = vp8_rac_get(&s->c);
 +            if (s->comppredmode)
 +                s->comppredmode += vp8_rac_get(&s->c);
 +            if (s->comppredmode == PRED_SWITCHABLE)
 +                for (i = 0; i < 5; i++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.comp[i] =
 +                            update_prob(&s->c, s->prob.p.comp[i]);
 +        } else {
 +            s->comppredmode = PRED_SINGLEREF;
 +        }
 +
 +        if (s->comppredmode != PRED_COMPREF) {
 +            for (i = 0; i < 5; i++) {
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.single_ref[i][0] =
 +                        update_prob(&s->c, s->prob.p.single_ref[i][0]);
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.single_ref[i][1] =
 +                        update_prob(&s->c, s->prob.p.single_ref[i][1]);
 +            }
 +        }
 +
 +        if (s->comppredmode != PRED_SINGLEREF) {
 +            for (i = 0; i < 5; i++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.comp_ref[i] =
 +                        update_prob(&s->c, s->prob.p.comp_ref[i]);
 +        }
 +
 +        for (i = 0; i < 4; i++)
 +            for (j = 0; j < 9; j++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.y_mode[i][j] =
 +                        update_prob(&s->c, s->prob.p.y_mode[i][j]);
 +
 +        for (i = 0; i < 4; i++)
 +            for (j = 0; j < 4; j++)
 +                for (k = 0; k < 3; k++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.partition[3 - i][j][k] =
 +                            update_prob(&s->c, s->prob.p.partition[3 - i][j][k]);
 +
 +        // mv fields don't use the update_prob subexp model for some reason
 +        for (i = 0; i < 3; i++)
 +            if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                s->prob.p.mv_joint[i] = (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +        for (i = 0; i < 2; i++) {
 +            if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                s->prob.p.mv_comp[i].sign = (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +            for (j = 0; j < 10; j++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_comp[i].classes[j] =
 +                        (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +            if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                s->prob.p.mv_comp[i].class0 = (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +            for (j = 0; j < 10; j++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_comp[i].bits[j] =
 +                        (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +        }
 +
 +        for (i = 0; i < 2; i++) {
 +            for (j = 0; j < 2; j++)
 +                for (k = 0; k < 3; k++)
 +                    if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                        s->prob.p.mv_comp[i].class0_fp[j][k] =
 +                            (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +            for (j = 0; j < 3; j++)
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_comp[i].fp[j] =
 +                        (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +        }
 +
 +        if (s->highprecisionmvs) {
 +            for (i = 0; i < 2; i++) {
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_comp[i].class0_hp =
 +                        (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +
 +                if (vp56_rac_get_prob_branchy(&s->c, 252))
 +                    s->prob.p.mv_comp[i].hp =
 +                        (vp8_rac_get_uint(&s->c, 7) << 1) | 1;
 +            }
 +        }
 +    }
 +
 +    return (data2 - data) + size2;
 +}
 +
 +static av_always_inline void clamp_mv(VP56mv *dst, const VP56mv *src,
 +                                      VP9Context *s)
 +{
 +    dst->x = av_clip(src->x, s->min_mv.x, s->max_mv.x);
 +    dst->y = av_clip(src->y, s->min_mv.y, s->max_mv.y);
 +}
 +
 +static void find_ref_mvs(VP9Context *s,
 +                         VP56mv *pmv, int ref, int z, int idx, int sb)
 +{
 +    static const int8_t mv_ref_blk_off[N_BS_SIZES][8][2] = {
 +        [BS_64x64] = {{  3, -1 }, { -1,  3 }, {  4, -1 }, { -1,  4 },
 +                      { -1, -1 }, {  0, -1 }, { -1,  0 }, {  6, -1 }},
 +        [BS_64x32] = {{  0, -1 }, { -1,  0 }, {  4, -1 }, { -1,  2 },
 +                      { -1, -1 }, {  0, -3 }, { -3,  0 }, {  2, -1 }},
 +        [BS_32x64] = {{ -1,  0 }, {  0, -1 }, { -1,  4 }, {  2, -1 },
 +                      { -1, -1 }, { -3,  0 }, {  0, -3 }, { -1,  2 }},
 +        [BS_32x32] = {{  1, -1 }, { -1,  1 }, {  2, -1 }, { -1,  2 },
 +                      { -1, -1 }, {  0, -3 }, { -3,  0 }, { -3, -3 }},
 +        [BS_32x16] = {{  0, -1 }, { -1,  0 }, {  2, -1 }, { -1, -1 },
 +                      { -1,  1 }, {  0, -3 }, { -3,  0 }, { -3, -3 }},
 +        [BS_16x32] = {{ -1,  0 }, {  0, -1 }, { -1,  2 }, { -1, -1 },
 +                      {  1, -1 }, { -3,  0 }, {  0, -3 }, { -3, -3 }},
 +        [BS_16x16] = {{  0, -1 }, { -1,  0 }, {  1, -1 }, { -1,  1 },
 +                      { -1, -1 }, {  0, -3 }, { -3,  0 }, { -3, -3 }},
 +        [BS_16x8]  = {{  0, -1 }, { -1,  0 }, {  1, -1 }, { -1, -1 },
 +                      {  0, -2 }, { -2,  0 }, { -2, -1 }, { -1, -2 }},
 +        [BS_8x16]  = {{ -1,  0 }, {  0, -1 }, { -1,  1 }, { -1, -1 },
 +                      { -2,  0 }, {  0, -2 }, { -1, -2 }, { -2, -1 }},
 +        [BS_8x8]   = {{  0, -1 }, { -1,  0 }, { -1, -1 }, {  0, -2 },
 +                      { -2,  0 }, { -1, -2 }, { -2, -1 }, { -2, -2 }},
 +        [BS_8x4]   = {{  0, -1 }, { -1,  0 }, { -1, -1 }, {  0, -2 },
 +                      { -2,  0 }, { -1, -2 }, { -2, -1 }, { -2, -2 }},
 +        [BS_4x8]   = {{  0, -1 }, { -1,  0 }, { -1, -1 }, {  0, -2 },
 +                      { -2,  0 }, { -1, -2 }, { -2, -1 }, { -2, -2 }},
 +        [BS_4x4]   = {{  0, -1 }, { -1,  0 }, { -1, -1 }, {  0, -2 },
 +                      { -2,  0 }, { -1, -2 }, { -2, -1 }, { -2, -2 }},
 +    };
 +    VP9Block *const b = &s->b;
 +    int row = b->row, col = b->col, row7 = b->row7;
 +    const int8_t (*p)[2] = mv_ref_blk_off[b->bs];
 +#define INVALID_MV 0x80008000U
 +    uint32_t mem = INVALID_MV;
 +    int i;
 +
 +#define RETURN_DIRECT_MV(mv) \
 +    do { \
 +        uint32_t m = AV_RN32A(&mv); \
 +        if (!idx) { \
 +            AV_WN32A(pmv, m); \
 +            return; \
 +        } else if (mem == INVALID_MV) { \
 +            mem = m; \
 +        } else if (m != mem) { \
 +            AV_WN32A(pmv, m); \
 +            return; \
 +        } \
 +    } while (0)
 +
 +    if (sb >= 0) {
 +        if (sb == 2 || sb == 1) {
 +            RETURN_DIRECT_MV(b->mv[0][z]);
 +        } else if (sb == 3) {
 +            RETURN_DIRECT_MV(b->mv[2][z]);
 +            RETURN_DIRECT_MV(b->mv[1][z]);
 +            RETURN_DIRECT_MV(b->mv[0][z]);
 +        }
 +
 +#define RETURN_MV(mv) \
 +    do { \
 +        if (sb > 0) { \
 +            VP56mv tmp; \
 +            uint32_t m; \
 +            clamp_mv(&tmp, &mv, s); \
 +            m = AV_RN32A(&tmp); \
 +            if (!idx) { \
 +                AV_WN32A(pmv, m); \
 +                return; \
 +            } else if (mem == INVALID_MV) { \
 +                mem = m; \
 +            } else if (m != mem) { \
 +                AV_WN32A(pmv, m); \
 +                return; \
 +            } \
 +        } else { \
 +            uint32_t m = AV_RN32A(&mv); \
 +            if (!idx) { \
 +                clamp_mv(pmv, &mv, s); \
 +                return; \
 +            } else if (mem == INVALID_MV) { \
 +                mem = m; \
 +            } else if (m != mem) { \
 +                clamp_mv(pmv, &mv, s); \
 +                return; \
 +            } \
 +        } \
 +    } while (0)
 +
 +        if (row > 0) {
 +            struct VP9mvrefPair *mv = &s->mv[0][(row - 1) * s->sb_cols * 8 + col];
 +            if (mv->ref[0] == ref) {
 +                RETURN_MV(s->above_mv_ctx[2 * col + (sb & 1)][0]);
 +            } else if (mv->ref[1] == ref) {
 +                RETURN_MV(s->above_mv_ctx[2 * col + (sb & 1)][1]);
 +            }
 +        }
 +        if (col > s->tiling.tile_col_start) {
 +            struct VP9mvrefPair *mv = &s->mv[0][row * s->sb_cols * 8 + col - 1];
 +            if (mv->ref[0] == ref) {
 +                RETURN_MV(s->left_mv_ctx[2 * row7 + (sb >> 1)][0]);
 +            } else if (mv->ref[1] == ref) {
 +                RETURN_MV(s->left_mv_ctx[2 * row7 + (sb >> 1)][1]);
 +            }
 +        }
 +        i = 2;
 +    } else {
 +        i = 0;
 +    }
 +
 +    // previously coded MVs in this neighbourhood, using same reference frame
 +    for (; i < 8; i++) {
 +        int c = p[i][0] + col, r = p[i][1] + row;
 +
 +        if (c >= s->tiling.tile_col_start && c < s->cols && r >= 0 && r < s->rows) {
 +            struct VP9mvrefPair *mv = &s->mv[0][r * s->sb_cols * 8 + c];
 +
 +            if (mv->ref[0] == ref) {
 +                RETURN_MV(mv->mv[0]);
 +            } else if (mv->ref[1] == ref) {
 +                RETURN_MV(mv->mv[1]);
 +            }
 +        }
 +    }
 +
 +    // MV at this position in previous frame, using same reference frame
 +    if (s->use_last_frame_mvs) {
 +        struct VP9mvrefPair *mv = &s->mv[1][row * s->sb_cols * 8 + col];
 +
 +        if (mv->ref[0] == ref) {
 +            RETURN_MV(mv->mv[0]);
 +        } else if (mv->ref[1] == ref) {
 +            RETURN_MV(mv->mv[1]);
 +        }
 +    }
 +
 +#define RETURN_SCALE_MV(mv, scale) \
 +    do { \
 +        if (scale) { \
 +            VP56mv mv_temp = { -mv.x, -mv.y }; \
 +            RETURN_MV(mv_temp); \
 +        } else { \
 +            RETURN_MV(mv); \
 +        } \
 +    } while (0)
 +
 +    // previously coded MVs in this neighbourhood, using different reference frame
 +    for (i = 0; i < 8; i++) {
 +        int c = p[i][0] + col, r = p[i][1] + row;
 +
 +        if (c >= s->tiling.tile_col_start && c < s->cols && r >= 0 && r < s->rows) {
 +            struct VP9mvrefPair *mv = &s->mv[0][r * s->sb_cols * 8 + c];
 +
 +            if (mv->ref[0] != ref && mv->ref[0] >= 0) {
 +                RETURN_SCALE_MV(mv->mv[0], s->signbias[mv->ref[0]] != s->signbias[ref]);
 +            }
 +            if (mv->ref[1] != ref && mv->ref[1] >= 0 &&
 +                // BUG - libvpx has this condition regardless of whether
 +                // we used the first ref MV and pre-scaling
 +                AV_RN32A(&mv->mv[0]) != AV_RN32A(&mv->mv[1])) {
 +                RETURN_SCALE_MV(mv->mv[1], s->signbias[mv->ref[1]] != s->signbias[ref]);
 +            }
 +        }
 +    }
 +
 +    // MV at this position in previous frame, using different reference frame
 +    if (s->use_last_frame_mvs) {
 +        struct VP9mvrefPair *mv = &s->mv[1][row * s->sb_cols * 8 + col];
 +
 +        if (mv->ref[0] != ref && mv->ref[0] >= 0) {
 +            RETURN_SCALE_MV(mv->mv[0], s->signbias[mv->ref[0]] != s->signbias[ref]);
 +        }
 +        if (mv->ref[1] != ref && mv->ref[1] >= 0 &&
 +            // BUG - libvpx has this condition regardless of whether
 +            // we used the first ref MV and pre-scaling
 +            AV_RN32A(&mv->mv[0]) != AV_RN32A(&mv->mv[1])) {
 +            RETURN_SCALE_MV(mv->mv[1], s->signbias[mv->ref[1]] != s->signbias[ref]);
 +        }
 +    }
 +
 +    AV_ZERO32(pmv);
 +#undef INVALID_MV
 +#undef RETURN_MV
 +#undef RETURN_SCALE_MV
 +}
 +
 +static av_always_inline int read_mv_component(VP9Context *s, int idx, int hp)
 +{
 +    int bit, sign = vp56_rac_get_prob(&s->c, s->prob.p.mv_comp[idx].sign);
 +    int n, c = vp8_rac_get_tree(&s->c, vp9_mv_class_tree,
 +                                s->prob.p.mv_comp[idx].classes);
 +
 +    s->counts.mv_comp[idx].sign[sign]++;
 +    s->counts.mv_comp[idx].classes[c]++;
 +    if (c) {
 +        int m;
 +
 +        for (n = 0, m = 0; m < c; m++) {
 +            bit = vp56_rac_get_prob(&s->c, s->prob.p.mv_comp[idx].bits[m]);
 +            n |= bit << m;
 +            s->counts.mv_comp[idx].bits[m][bit]++;
 +        }
 +        n <<= 3;
 +        bit = vp8_rac_get_tree(&s->c, vp9_mv_fp_tree, s->prob.p.mv_comp[idx].fp);
 +        n |= bit << 1;
 +        s->counts.mv_comp[idx].fp[bit]++;
 +        if (hp) {
 +            bit = vp56_rac_get_prob(&s->c, s->prob.p.mv_comp[idx].hp);
 +            s->counts.mv_comp[idx].hp[bit]++;
 +            n |= bit;
 +        } else {
 +            n |= 1;
 +            // bug in libvpx - we count for bw entropy purposes even if the
 +            // bit wasn't coded
 +            s->counts.mv_comp[idx].hp[1]++;
 +        }
 +        n += 8 << c;
 +    } else {
 +        n = vp56_rac_get_prob(&s->c, s->prob.p.mv_comp[idx].class0);
 +        s->counts.mv_comp[idx].class0[n]++;
 +        bit = vp8_rac_get_tree(&s->c, vp9_mv_fp_tree,
 +                               s->prob.p.mv_comp[idx].class0_fp[n]);
 +        s->counts.mv_comp[idx].class0_fp[n][bit]++;
 +        n = (n << 3) | (bit << 1);
 +        if (hp) {
 +            bit = vp56_rac_get_prob(&s->c, s->prob.p.mv_comp[idx].class0_hp);
 +            s->counts.mv_comp[idx].class0_hp[bit]++;
 +            n |= bit;
 +        } else {
 +            n |= 1;
 +            // bug in libvpx - we count for bw entropy purposes even if the
 +            // bit wasn't coded
 +            s->counts.mv_comp[idx].class0_hp[1]++;
 +        }
 +    }
 +
 +    return sign ? -(n + 1) : (n + 1);
 +}
 +
 +static void fill_mv(VP9Context *s,
 +                    VP56mv *mv, int mode, int sb)
 +{
 +    VP9Block *const b = &s->b;
 +
 +    if (mode == ZEROMV) {
 +        memset(mv, 0, sizeof(*mv) * 2);
 +    } else {
 +        int hp;
 +
 +        // FIXME cache this value and reuse for other subblocks
 +        find_ref_mvs(s, &mv[0], b->ref[0], 0, mode == NEARMV,
 +                     mode == NEWMV ? -1 : sb);
 +        // FIXME maybe move this code into find_ref_mvs()
 +        if ((mode == NEWMV || sb == -1) &&
 +            !(hp = s->highprecisionmvs && abs(mv[0].x) < 64 && abs(mv[0].y) < 64)) {
 +            if (mv[0].y & 1) {
 +                if (mv[0].y < 0)
 +                    mv[0].y++;
 +                else
 +                    mv[0].y--;
 +            }
 +            if (mv[0].x & 1) {
 +                if (mv[0].x < 0)
 +                    mv[0].x++;
 +                else
 +                    mv[0].x--;
 +            }
 +        }
 +        if (mode == NEWMV) {
 +            enum MVJoint j = vp8_rac_get_tree(&s->c, vp9_mv_joint_tree,
 +                                              s->prob.p.mv_joint);
 +
 +            s->counts.mv_joint[j]++;
 +            if (j >= MV_JOINT_V)
 +                mv[0].y += read_mv_component(s, 0, hp);
 +            if (j & 1)
 +                mv[0].x += read_mv_component(s, 1, hp);
 +        }
 +
 +        if (b->comp) {
 +            // FIXME cache this value and reuse for other subblocks
 +            find_ref_mvs(s, &mv[1], b->ref[1], 1, mode == NEARMV,
 +                         mode == NEWMV ? -1 : sb);
 +            if ((mode == NEWMV || sb == -1) &&
 +                !(hp = s->highprecisionmvs && abs(mv[1].x) < 64 && abs(mv[1].y) < 64)) {
 +                if (mv[1].y & 1) {
 +                    if (mv[1].y < 0)
 +                        mv[1].y++;
 +                    else
 +                        mv[1].y--;
 +                }
 +                if (mv[1].x & 1) {
 +                    if (mv[1].x < 0)
 +                        mv[1].x++;
 +                    else
 +                        mv[1].x--;
 +                }
 +            }
 +            if (mode == NEWMV) {
 +                enum MVJoint j = vp8_rac_get_tree(&s->c, vp9_mv_joint_tree,
 +                                                  s->prob.p.mv_joint);
 +
 +                s->counts.mv_joint[j]++;
 +                if (j >= MV_JOINT_V)
 +                    mv[1].y += read_mv_component(s, 0, hp);
 +                if (j & 1)
 +                    mv[1].x += read_mv_component(s, 1, hp);
 +            }
 +        }
 +    }
 +}
 +
 +static void decode_mode(AVCodecContext *ctx)
 +{
 +    static const uint8_t left_ctx[N_BS_SIZES] = {
 +        0x0, 0x8, 0x0, 0x8, 0xc, 0x8, 0xc, 0xe, 0xc, 0xe, 0xf, 0xe, 0xf
 +    };
 +    static const uint8_t above_ctx[N_BS_SIZES] = {
 +        0x0, 0x0, 0x8, 0x8, 0x8, 0xc, 0xc, 0xc, 0xe, 0xe, 0xe, 0xf, 0xf
 +    };
 +    static const uint8_t max_tx_for_bl_bp[N_BS_SIZES] = {
 +        TX_32X32, TX_32X32, TX_32X32, TX_32X32, TX_16X16, TX_16X16,
 +        TX_16X16, TX_8X8, TX_8X8, TX_8X8, TX_4X4, TX_4X4, TX_4X4
 +    };
 +    VP9Context *s = ctx->priv_data;
 +    VP9Block *const b = &s->b;
 +    int row = b->row, col = b->col, row7 = b->row7;
 +    enum TxfmMode max_tx = max_tx_for_bl_bp[b->bs];
 +    int w4 = FFMIN(s->cols - col, bwh_tab[1][b->bs][0]);
 +    int h4 = FFMIN(s->rows - row, bwh_tab[1][b->bs][1]), y;
 +    int have_a = row > 0, have_l = col > s->tiling.tile_col_start;
 +
 +    if (!s->segmentation.enabled) {
 +        b->seg_id = 0;
 +    } else if (s->keyframe || s->intraonly) {
 +        b->seg_id = s->segmentation.update_map ?
 +            vp8_rac_get_tree(&s->c, vp9_segmentation_tree, s->prob.seg) : 0;
 +    } else if (!s->segmentation.update_map ||
 +               (s->segmentation.temporal &&
 +                vp56_rac_get_prob_branchy(&s->c,
 +                    s->prob.segpred[s->above_segpred_ctx[col] +
 +                                    s->left_segpred_ctx[row7]]))) {
 +        int pred = 8, x;
 +
 +        for (y = 0; y < h4; y++)
 +            for (x = 0; x < w4; x++)
 +                pred = FFMIN(pred, s->segmentation_map[(y + row) * 8 * s->sb_cols + x + col]);
 +        av_assert1(pred < 8);
 +        b->seg_id = pred;
 +
 +        memset(&s->above_segpred_ctx[col], 1, w4);
 +        memset(&s->left_segpred_ctx[row7], 1, h4);
 +    } else {
 +        b->seg_id = vp8_rac_get_tree(&s->c, vp9_segmentation_tree,
 +                                     s->prob.seg);
 +
 +        memset(&s->above_segpred_ctx[col], 0, w4);
 +        memset(&s->left_segpred_ctx[row7], 0, h4);
 +    }
 +    if ((s->segmentation.enabled && s->segmentation.update_map) || s->keyframe) {
 +        for (y = 0; y < h4; y++)
 +            memset(&s->segmentation_map[(y + row) * 8 * s->sb_cols + col],
 +                   b->seg_id, w4);
 +    }
 +
 +    b->skip = s->segmentation.enabled &&
 +        s->segmentation.feat[b->seg_id].skip_enabled;
 +    if (!b->skip) {
 +        int c = s->left_skip_ctx[row7] + s->above_skip_ctx[col];
 +        b->skip = vp56_rac_get_prob(&s->c, s->prob.p.skip[c]);
 +        s->counts.skip[c][b->skip]++;
 +    }
 +
 +    if (s->keyframe || s->intraonly) {
 +        b->intra = 1;
 +    } else if (s->segmentation.feat[b->seg_id].ref_enabled) {
 +        b->intra = !s->segmentation.feat[b->seg_id].ref_val;
 +    } else {
 +        int c, bit;
 +
 +        if (have_a && have_l) {
 +            c = s->above_intra_ctx[col] + s->left_intra_ctx[row7];
 +            c += (c == 2);
 +        } else {
 +            c = have_a ? 2 * s->above_intra_ctx[col] :
 +                have_l ? 2 * s->left_intra_ctx[row7] : 0;
 +        }
 +        bit = vp56_rac_get_prob(&s->c, s->prob.p.intra[c]);
 +        s->counts.intra[c][bit]++;
 +        b->intra = !bit;
 +    }
 +
 +    if ((b->intra || !b->skip) && s->txfmmode == TX_SWITCHABLE) {
 +        int c;
 +        if (have_a) {
 +            if (have_l) {
 +                c = (s->above_skip_ctx[col] ? max_tx :
 +                     s->above_txfm_ctx[col]) +
 +                    (s->left_skip_ctx[row7] ? max_tx :
 +                     s->left_txfm_ctx[row7]) > max_tx;
 +            } else {
 +                c = s->above_skip_ctx[col] ? 1 :
 +                    (s->above_txfm_ctx[col] * 2 > max_tx);
 +            }
 +        } else if (have_l) {
 +            c = s->left_skip_ctx[row7] ? 1 :
 +                (s->left_txfm_ctx[row7] * 2 > max_tx);
 +        } else {
 +            c = 1;
 +        }
 +        switch (max_tx) {
 +        case TX_32X32:
 +            b->tx = vp56_rac_get_prob(&s->c, s->prob.p.tx32p[c][0]);
 +            if (b->tx) {
 +                b->tx += vp56_rac_get_prob(&s->c, s->prob.p.tx32p[c][1]);
 +                if (b->tx == 2)
 +                    b->tx += vp56_rac_get_prob(&s->c, s->prob.p.tx32p[c][2]);
 +            }
 +            s->counts.tx32p[c][b->tx]++;
 +            break;
 +        case TX_16X16:
 +            b->tx = vp56_rac_get_prob(&s->c, s->prob.p.tx16p[c][0]);
 +            if (b->tx)
 +                b->tx += vp56_rac_get_prob(&s->c, s->prob.p.tx16p[c][1]);
 +            s->counts.tx16p[c][b->tx]++;
 +            break;
 +        case TX_8X8:
 +            b->tx = vp56_rac_get_prob(&s->c, s->prob.p.tx8p[c]);
 +            s->counts.tx8p[c][b->tx]++;
 +            break;
 +        case TX_4X4:
 +            b->tx = TX_4X4;
 +            break;
 +        }
 +    } else {
 +        b->tx = FFMIN(max_tx, s->txfmmode);
 +    }
 +
 +    if (s->keyframe || s->intraonly) {
 +        uint8_t *a = &s->above_mode_ctx[col * 2];
 +        uint8_t *l = &s->left_mode_ctx[(row7) << 1];
 +
 +        b->comp = 0;
 +        if (b->bs > BS_8x8) {
 +            // FIXME the memory storage intermediates here aren't really
 +            // necessary, they're just there to make the code slightly
 +            // simpler for now
 +            b->mode[0] = a[0] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                    vp9_default_kf_ymode_probs[a[0]][l[0]]);
 +            if (b->bs != BS_8x4) {
 +                b->mode[1] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                 vp9_default_kf_ymode_probs[a[1]][b->mode[0]]);
 +                l[0] = a[1] = b->mode[1];
 +            } else {
 +                l[0] = a[1] = b->mode[1] = b->mode[0];
 +            }
 +            if (b->bs != BS_4x8) {
 +                b->mode[2] = a[0] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                        vp9_default_kf_ymode_probs[a[0]][l[1]]);
 +                if (b->bs != BS_8x4) {
 +                    b->mode[3] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                  vp9_default_kf_ymode_probs[a[1]][b->mode[2]]);
 +                    l[1] = a[1] = b->mode[3];
 +                } else {
 +                    l[1] = a[1] = b->mode[3] = b->mode[2];
 +                }
 +            } else {
 +                b->mode[2] = b->mode[0];
 +                l[1] = a[1] = b->mode[3] = b->mode[1];
 +            }
 +        } else {
 +            b->mode[0] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                          vp9_default_kf_ymode_probs[*a][*l]);
 +            b->mode[3] = b->mode[2] = b->mode[1] = b->mode[0];
 +            // FIXME this can probably be optimized
 +            memset(a, b->mode[0], bwh_tab[0][b->bs][0]);
 +            memset(l, b->mode[0], bwh_tab[0][b->bs][1]);
 +        }
 +        b->uvmode = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                     vp9_default_kf_uvmode_probs[b->mode[3]]);
 +    } else if (b->intra) {
 +        b->comp = 0;
 +        if (b->bs > BS_8x8) {
 +            b->mode[0] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                          s->prob.p.y_mode[0]);
 +            s->counts.y_mode[0][b->mode[0]]++;
 +            if (b->bs != BS_8x4) {
 +                b->mode[1] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                              s->prob.p.y_mode[0]);
 +                s->counts.y_mode[0][b->mode[1]]++;
 +            } else {
 +                b->mode[1] = b->mode[0];
 +            }
 +            if (b->bs != BS_4x8) {
 +                b->mode[2] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                              s->prob.p.y_mode[0]);
 +                s->counts.y_mode[0][b->mode[2]]++;
 +                if (b->bs != BS_8x4) {
 +                    b->mode[3] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                                  s->prob.p.y_mode[0]);
 +                    s->counts.y_mode[0][b->mode[3]]++;
 +                } else {
 +                    b->mode[3] = b->mode[2];
 +                }
 +            } else {
 +                b->mode[2] = b->mode[0];
 +                b->mode[3] = b->mode[1];
 +            }
 +        } else {
 +            static const uint8_t size_group[10] = {
 +                3, 3, 3, 3, 2, 2, 2, 1, 1, 1
 +            };
 +            int sz = size_group[b->bs];
 +
 +            b->mode[0] = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                          s->prob.p.y_mode[sz]);
 +            b->mode[1] = b->mode[2] = b->mode[3] = b->mode[0];
 +            s->counts.y_mode[sz][b->mode[3]]++;
 +        }
 +        b->uvmode = vp8_rac_get_tree(&s->c, vp9_intramode_tree,
 +                                     s->prob.p.uv_mode[b->mode[3]]);
 +        s->counts.uv_mode[b->mode[3]][b->uvmode]++;
 +    } else {
 +        static const uint8_t inter_mode_ctx_lut[14][14] = {
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5 },
 +            { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 1, 3 },
 +            { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 1, 3 },
 +            { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 0, 3 },
 +            { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 4 },
 +        };
 +
 +        if (s->segmentation.feat[b->seg_id].ref_enabled) {
 +            av_assert2(s->segmentation.feat[b->seg_id].ref_val != 0);
 +            b->comp = 0;
 +            b->ref[0] = s->segmentation.feat[b->seg_id].ref_val - 1;
 +        } else {
 +            // read comp_pred flag
 +            if (s->comppredmode != PRED_SWITCHABLE) {
 +                b->comp = s->comppredmode == PRED_COMPREF;
 +            } else {
 +                int c;
 +
 +                // FIXME add intra as ref=0xff (or -1) to make these easier?
 +                if (have_a) {
 +                    if (have_l) {
 +                        if (s->above_comp_ctx[col] && s->left_comp_ctx[row7]) {
 +                            c = 4;
 +                        } else if (s->above_comp_ctx[col]) {
 +                            c = 2 + (s->left_intra_ctx[row7] ||
 +                                     s->left_ref_ctx[row7] == s->fixcompref);
 +                        } else if (s->left_comp_ctx[row7]) {
 +                            c = 2 + (s->above_intra_ctx[col] ||
 +                                     s->above_ref_ctx[col] == s->fixcompref);
 +                        } else {
 +                            c = (!s->above_intra_ctx[col] &&
 +                                 s->above_ref_ctx[col] == s->fixcompref) ^
 +                            (!s->left_intra_ctx[row7] &&
 +                             s->left_ref_ctx[row & 7] == s->fixcompref);
 +                        }
 +                    } else {
 +                        c = s->above_comp_ctx[col] ? 3 :
 +                        (!s->above_intra_ctx[col] && s->above_ref_ctx[col] == s->fixcompref);
 +                    }
 +                } else if (have_l) {
 +                    c = s->left_comp_ctx[row7] ? 3 :
 +                    (!s->left_intra_ctx[row7] && s->left_ref_ctx[row7] == s->fixcompref);
 +                } else {
 +                    c = 1;
 +                }
 +                b->comp = vp56_rac_get_prob(&s->c, s->prob.p.comp[c]);
 +                s->counts.comp[c][b->comp]++;
 +            }
 +
 +            // read actual references
 +            // FIXME probably cache a few variables here to prevent repetitive
 +            // memory accesses below
 +            if (b->comp) /* two references */ {
 +                int fix_idx = s->signbias[s->fixcompref], var_idx = !fix_idx, c, bit;
 +
 +                b->ref[fix_idx] = s->fixcompref;
 +                // FIXME can this codeblob be replaced by some sort of LUT?
 +                if (have_a) {
 +                    if (have_l) {
 +                        if (s->above_intra_ctx[col]) {
 +                            if (s->left_intra_ctx[row7]) {
 +                                c = 2;
 +                            } else {
 +                                c = 1 + 2 * (s->left_ref_ctx[row7] != s->varcompref[1]);
 +                            }
 +                        } else if (s->left_intra_ctx[row7]) {
 +                            c = 1 + 2 * (s->above_ref_ctx[col] != s->varcompref[1]);
 +                        } else {
 +                            int refl = s->left_ref_ctx[row7], refa = s->above_ref_ctx[col];
 +
 +                            if (refl == refa && refa == s->varcompref[1]) {
 +                                c = 0;
 +                            } else if (!s->left_comp_ctx[row7] && !s->above_comp_ctx[col]) {
 +                                if ((refa == s->fixcompref && refl == s->varcompref[0]) ||
 +                                    (refl == s->fixcompref && refa == s->varcompref[0])) {
 +                                    c = 4;
 +                                } else {
 +                                    c = (refa == refl) ? 3 : 1;
 +                                }
 +                            } else if (!s->left_comp_ctx[row7]) {
 +                                if (refa == s->varcompref[1] && refl != s->varcompref[1]) {
 +                                    c = 1;
 +                                } else {
 +                                    c = (refl == s->varcompref[1] &&
 +                                         refa != s->varcompref[1]) ? 2 : 4;
 +                                }
 +                            } else if (!s->above_comp_ctx[col]) {
 +                                if (refl == s->varcompref[1] && refa != s->varcompref[1]) {
 +                                    c = 1;
 +                                } else {
 +                                    c = (refa == s->varcompref[1] &&
 +                                         refl != s->varcompref[1]) ? 2 : 4;
 +                                }
 +                            } else {
 +                                c = (refl == refa) ? 4 : 2;
 +                            }
 +                        }
 +                    } else {
 +                        if (s->above_intra_ctx[col]) {
 +                            c = 2;
 +                        } else if (s->above_comp_ctx[col]) {
 +                            c = 4 * (s->above_ref_ctx[col] != s->varcompref[1]);
 +                        } else {
 +                            c = 3 * (s->above_ref_ctx[col] != s->varcompref[1]);
 +                        }
 +                    }
 +                } else if (have_l) {
 +                    if (s->left_intra_ctx[row7]) {
 +                        c = 2;
 +                    } else if (s->left_comp_ctx[row7]) {
 +                        c = 4 * (s->left_ref_ctx[row7] != s->varcompref[1]);
 +                    } else {
 +                        c = 3 * (s->left_ref_ctx[row7] != s->varcompref[1]);
 +                    }
 +                } else {
 +                    c = 2;
 +                }
 +                bit = vp56_rac_get_prob(&s->c, s->prob.p.comp_ref[c]);
 +                b->ref[var_idx] = s->varcompref[bit];
 +                s->counts.comp_ref[c][bit]++;
 +            } else /* single reference */ {
 +                int bit, c;
 +
 +                if (have_a && !s->above_intra_ctx[col]) {
 +                    if (have_l && !s->left_intra_ctx[row7]) {
 +                        if (s->left_comp_ctx[row7]) {
 +                            if (s->above_comp_ctx[col]) {
 +                                c = 1 + (!s->fixcompref || !s->left_ref_ctx[row7] ||
 +                                         !s->above_ref_ctx[col]);
 +                            } else {
 +                                c = (3 * !s->above_ref_ctx[col]) +
 +                                    (!s->fixcompref || !s->left_ref_ctx[row7]);
 +                            }
 +                        } else if (s->above_comp_ctx[col]) {
 +                            c = (3 * !s->left_ref_ctx[row7]) +
 +                                (!s->fixcompref || !s->above_ref_ctx[col]);
 +                        } else {
 +                            c = 2 * !s->left_ref_ctx[row7] + 2 * !s->above_ref_ctx[col];
 +                        }
 +                    } else if (s->above_intra_ctx[col]) {
 +                        c = 2;
 +                    } else if (s->above_comp_ctx[col]) {
 +                        c = 1 + (!s->fixcompref || !s->above_ref_ctx[col]);
 +                    } else {
 +                        c = 4 * (!s->above_ref_ctx[col]);
 +                    }
 +                } else if (have_l && !s->left_intra_ctx[row7]) {
 +                    if (s->left_intra_ctx[row7]) {
 +                        c = 2;
 +                    } else if (s->left_comp_ctx[row7]) {
 +                        c = 1 + (!s->fixcompref || !s->left_ref_ctx[row7]);
 +                    } else {
 +                        c = 4 * (!s->left_ref_ctx[row7]);
 +                    }
 +                } else {
 +                    c = 2;
 +                }
 +                bit = vp56_rac_get_prob(&s->c, s->prob.p.single_ref[c][0]);
 +                s->counts.single_ref[c][0][bit]++;
 +                if (!bit) {
 +                    b->ref[0] = 0;
 +                } else {
 +                    // FIXME can this codeblob be replaced by some sort of LUT?
 +                    if (have_a) {
 +                        if (have_l) {
 +                            if (s->left_intra_ctx[row7]) {
 +                                if (s->above_intra_ctx[col]) {
 +                                    c = 2;
 +                                } else if (s->above_comp_ctx[col]) {
 +                                    c = 1 + 2 * (s->fixcompref == 1 ||
 +                                                 s->above_ref_ctx[col] == 1);
 +                                } else if (!s->above_ref_ctx[col]) {
 +                                    c = 3;
 +                                } else {
 +                                    c = 4 * (s->above_ref_ctx[col] == 1);
 +                                }
 +                            } else if (s->above_intra_ctx[col]) {
 +                                if (s->left_intra_ctx[row7]) {
 +                                    c = 2;
 +                                } else if (s->left_comp_ctx[row7]) {
 +                                    c = 1 + 2 * (s->fixcompref == 1 ||
 +                                                 s->left_ref_ctx[row7] == 1);
 +                                } else if (!s->left_ref_ctx[row7]) {
 +                                    c = 3;
 +                                } else {
 +                                    c = 4 * (s->left_ref_ctx[row7] == 1);
 +                                }
 +                            } else if (s->above_comp_ctx[col]) {
 +                                if (s->left_comp_ctx[row7]) {
 +                                    if (s->left_ref_ctx[row7] == s->above_ref_ctx[col]) {
 +                                        c = 3 * (s->fixcompref == 1 ||
 +                                                 s->left_ref_ctx[row7] == 1);
 +                                    } else {
 +                                        c = 2;
 +                                    }
 +                                } else if (!s->left_ref_ctx[row7]) {
 +                                    c = 1 + 2 * (s->fixcompref == 1 ||
 +                                                 s->above_ref_ctx[col] == 1);
 +                                } else {
 +                                    c = 3 * (s->left_ref_ctx[row7] == 1) +
 +                                    (s->fixcompref == 1 || s->above_ref_ctx[col] == 1);
 +                                }
 +                            } else if (s->left_comp_ctx[row7]) {
 +                                if (!s->above_ref_ctx[col]) {
 +                                    c = 1 + 2 * (s->fixcompref == 1 ||
 +                                                 s->left_ref_ctx[row7] == 1);
 +                                } else {
 +                                    c = 3 * (s->above_ref_ctx[col] == 1) +
 +                                    (s->fixcompref == 1 || s->left_ref_ctx[row7] == 1);
 +                                }
 +                            } else if (!s->above_ref_ctx[col]) {
 +                                if (!s->left_ref_ctx[row7]) {
 +                                    c = 3;
 +                                } else {
 +                                    c = 4 * (s->left_ref_ctx[row7] == 1);
 +                                }
 +                            } else if (!s->left_ref_ctx[row7]) {
 +                                c = 4 * (s->above_ref_ctx[col] == 1);
 +                            } else {
 +                                c = 2 * (s->left_ref_ctx[row7] == 1) +
 +                                2 * (s->above_ref_ctx[col] == 1);
 +                            }
 +                        } else {
 +                            if (s->above_intra_ctx[col] ||
 +                                (!s->above_comp_ctx[col] && !s->above_ref_ctx[col])) {
 +                                c = 2;
 +                            } else if (s->above_comp_ctx[col]) {
 +                                c = 3 * (s->fixcompref == 1 || s->above_ref_ctx[col] == 1);
 +                            } else {
 +                                c = 4 * (s->above_ref_ctx[col] == 1);
 +                            }
 +                        }
 +                    } else if (have_l) {
 +                        if (s->left_intra_ctx[row7] ||
 +                            (!s->left_comp_ctx[row7] && !s->left_ref_ctx[row7])) {
 +                            c = 2;
 +                        } else if (s->left_comp_ctx[row7]) {
 +                            c = 3 * (s->fixcompref == 1 || s->left_ref_ctx[row7] == 1);
 +                        } else {
 +                            c = 4 * (s->left_ref_ctx[row7] == 1);
 +                        }
 +                    } else {
 +                        c = 2;
 +                    }
 +                    bit = vp56_rac_get_prob(&s->c, s->prob.p.single_ref[c][1]);
 +                    s->counts.single_ref[c][1][bit]++;
 +                    b->ref[0] = 1 + bit;
 +                }
 +            }
 +        }
 +
 +        if (b->bs <= BS_8x8) {
 +            if (s->segmentation.feat[b->seg_id].skip_enabled) {
 +                b->mode[0] = b->mode[1] = b->mode[2] = b->mode[3] = ZEROMV;
 +            } else {
 +                static const uint8_t off[10] = {
 +                    3, 0, 0, 1, 0, 0, 0, 0, 0, 0
 +                };
 +
 +                // FIXME this needs to use the LUT tables from find_ref_mvs
 +                // because not all are -1,0/0,-1
 +                int c = inter_mode_ctx_lut[s->above_mode_ctx[col + off[b->bs]]]
 +                                          [s->left_mode_ctx[row7 + off[b->bs]]];
 +
 +                b->mode[0] = vp8_rac_get_tree(&s->c, vp9_inter_mode_tree,
 +                                              s->prob.p.mv_mode[c]);
 +                b->mode[1] = b->mode[2] = b->mode[3] = b->mode[0];
 +                s->counts.mv_mode[c][b->mode[0] - 10]++;
 +            }
 +        }
 +
 +        if (s->filtermode == FILTER_SWITCHABLE) {
 +            int c;
 +
 +            if (have_a && s->above_mode_ctx[col] >= NEARESTMV) {
 +                if (have_l && s->left_mode_ctx[row7] >= NEARESTMV) {
 +                    c = s->above_filter_ctx[col] == s->left_filter_ctx[row7] ?
 +                        s->left_filter_ctx[row7] : 3;
 +                } else {
 +                    c = s->above_filter_ctx[col];
 +                }
 +            } else if (have_l && s->left_mode_ctx[row7] >= NEARESTMV) {
 +                c = s->left_filter_ctx[row7];
 +            } else {
 +                c = 3;
 +            }
 +
 +            b->filter = vp8_rac_get_tree(&s->c, vp9_filter_tree,
 +                                         s->prob.p.filter[c]);
 +            s->counts.filter[c][b->filter]++;
 +        } else {
 +            b->filter = s->filtermode;
 +        }
 +
 +        if (b->bs > BS_8x8) {
 +            int c = inter_mode_ctx_lut[s->above_mode_ctx[col]][s->left_mode_ctx[row7]];
 +
 +            b->mode[0] = vp8_rac_get_tree(&s->c, vp9_inter_mode_tree,
 +                                          s->prob.p.mv_mode[c]);
 +            s->counts.mv_mode[c][b->mode[0] - 10]++;
 +            fill_mv(s, b->mv[0], b->mode[0], 0);
 +
 +            if (b->bs != BS_8x4) {
 +                b->mode[1] = vp8_rac_get_tree(&s->c, vp9_inter_mode_tree,
 +                                              s->prob.p.mv_mode[c]);
 +                s->counts.mv_mode[c][b->mode[1] - 10]++;
 +                fill_mv(s, b->mv[1], b->mode[1], 1);
 +            } else {
 +                b->mode[1] = b->mode[0];
 +                AV_COPY32(&b->mv[1][0], &b->mv[0][0]);
 +                AV_COPY32(&b->mv[1][1], &b->mv[0][1]);
 +            }
 +
 +            if (b->bs != BS_4x8) {
 +                b->mode[2] = vp8_rac_get_tree(&s->c, vp9_inter_mode_tree,
 +                                              s->prob.p.mv_mode[c]);
 +                s->counts.mv_mode[c][b->mode[2] - 10]++;
 +                fill_mv(s, b->mv[2], b->mode[2], 2);
 +
 +                if (b->bs != BS_8x4) {
 +                    b->mode[3] = vp8_rac_get_tree(&s->c, vp9_inter_mode_tree,
 +                                                  s->prob.p.mv_mode[c]);
 +                    s->counts.mv_mode[c][b->mode[3] - 10]++;
 +                    fill_mv(s, b->mv[3], b->mode[3], 3);
 +                } else {
 +                    b->mode[3] = b->mode[2];
 +                    AV_COPY32(&b->mv[3][0], &b->mv[2][0]);
 +                    AV_COPY32(&b->mv[3][1], &b->mv[2][1]);
 +                }
 +            } else {
 +                b->mode[2] = b->mode[0];
 +                AV_COPY32(&b->mv[2][0], &b->mv[0][0]);
 +                AV_COPY32(&b->mv[2][1], &b->mv[0][1]);
 +                b->mode[3] = b->mode[1];
 +                AV_COPY32(&b->mv[3][0], &b->mv[1][0]);
 +                AV_COPY32(&b->mv[3][1], &b->mv[1][1]);
 +            }
 +        } else {
 +            fill_mv(s, b->mv[0], b->mode[0], -1);
 +            AV_COPY32(&b->mv[1][0], &b->mv[0][0]);
 +            AV_COPY32(&b->mv[2][0], &b->mv[0][0]);
 +            AV_COPY32(&b->mv[3][0], &b->mv[0][0]);
 +            AV_COPY32(&b->mv[1][1], &b->mv[0][1]);
 +            AV_COPY32(&b->mv[2][1], &b->mv[0][1]);
 +            AV_COPY32(&b->mv[3][1], &b->mv[0][1]);
 +        }
 +    }
 +
 +    // FIXME this can probably be optimized
 +    memset(&s->above_skip_ctx[col], b->skip, w4);
 +    memset(&s->left_skip_ctx[row7], b->skip, h4);
 +    memset(&s->above_txfm_ctx[col], b->tx, w4);
 +    memset(&s->left_txfm_ctx[row7], b->tx, h4);
 +    memset(&s->above_partition_ctx[col], above_ctx[b->bs], w4);
 +    memset(&s->left_partition_ctx[row7], left_ctx[b->bs], h4);
 +    if (!s->keyframe && !s->intraonly) {
 +        memset(&s->above_intra_ctx[col], b->intra, w4);
 +        memset(&s->left_intra_ctx[row7], b->intra, h4);
 +        memset(&s->above_comp_ctx[col], b->comp, w4);
 +        memset(&s->left_comp_ctx[row7], b->comp, h4);
 +        memset(&s->above_mode_ctx[col], b->mode[3], w4);
 +        memset(&s->left_mode_ctx[row7], b->mode[3], h4);
 +        if (s->filtermode == FILTER_SWITCHABLE && !b->intra ) {
 +            memset(&s->above_filter_ctx[col], b->filter, w4);
 +            memset(&s->left_filter_ctx[row7], b->filter, h4);
 +            b->filter = vp9_filter_lut[b->filter];
 +        }
 +        if (b->bs > BS_8x8) {
 +            int mv0 = AV_RN32A(&b->mv[3][0]), mv1 = AV_RN32A(&b->mv[3][1]);
 +
 +            AV_COPY32(&s->left_mv_ctx[row7 * 2 + 0][0], &b->mv[1][0]);
 +            AV_COPY32(&s->left_mv_ctx[row7 * 2 + 0][1], &b->mv[1][1]);
 +            AV_WN32A(&s->left_mv_ctx[row7 * 2 + 1][0], mv0);
 +            AV_WN32A(&s->left_mv_ctx[row7 * 2 + 1][1], mv1);
 +            AV_COPY32(&s->above_mv_ctx[col * 2 + 0][0], &b->mv[2][0]);
 +            AV_COPY32(&s->above_mv_ctx[col * 2 + 0][1], &b->mv[2][1]);
 +            AV_WN32A(&s->above_mv_ctx[col * 2 + 1][0], mv0);
 +            AV_WN32A(&s->above_mv_ctx[col * 2 + 1][1], mv1);
 +        } else {
 +            int n, mv0 = AV_RN32A(&b->mv[3][0]), mv1 = AV_RN32A(&b->mv[3][1]);
 +
 +            for (n = 0; n < w4 * 2; n++) {
 +                AV_WN32A(&s->above_mv_ctx[col * 2 + n][0], mv0);
 +                AV_WN32A(&s->above_mv_ctx[col * 2 + n][1], mv1);
 +            }
 +            for (n = 0; n < h4 * 2; n++) {
 +                AV_WN32A(&s->left_mv_ctx[row7 * 2 + n][0], mv0);
 +                AV_WN32A(&s->left_mv_ctx[row7 * 2 + n][1], mv1);
 +            }
 +        }
 +
 +        if (!b->intra) { // FIXME write 0xff or -1 if intra, so we can use this
 +                         // as a direct check in above branches
 +            int vref = b->ref[b->comp ? s->signbias[s->varcompref[0]] : 0];
 +
 +            memset(&s->above_ref_ctx[col], vref, w4);
 +            memset(&s->left_ref_ctx[row7], vref, h4);
 +        }
 +    }
 +
 +    // FIXME kinda ugly
 +    for (y = 0; y < h4; y++) {
 +        int x, o = (row + y) * s->sb_cols * 8 + col;
 +
 +        if (b->intra) {
 +            for (x = 0; x < w4; x++) {
 +                s->mv[0][o + x].ref[0] =
 +                s->mv[0][o + x].ref[1] = -1;
 +            }
 +        } else if (b->comp) {
 +            for (x = 0; x < w4; x++) {
 +                s->mv[0][o + x].ref[0] = b->ref[0];
 +                s->mv[0][o + x].ref[1] = b->ref[1];
 +                AV_COPY32(&s->mv[0][o + x].mv[0], &b->mv[3][0]);
 +                AV_COPY32(&s->mv[0][o + x].mv[1], &b->mv[3][1]);
 +            }
 +        } else {
 +            for (x = 0; x < w4; x++) {
 +                s->mv[0][o + x].ref[0] = b->ref[0];
 +                s->mv[0][o + x].ref[1] = -1;
 +                AV_COPY32(&s->mv[0][o + x].mv[0], &b->mv[3][0]);
 +            }
 +        }
 +    }
 +}
 +
 +// FIXME remove tx argument, and merge cnt/eob arguments?
 +static int decode_coeffs_b(VP56RangeCoder *c, int16_t *coef, int n_coeffs,
 +                           enum TxfmMode tx, unsigned (*cnt)[6][3],
 +                           unsigned (*eob)[6][2], uint8_t (*p)[6][11],
 +                           int nnz, const int16_t *scan, const int16_t (*nb)[2],
 +                           const int16_t *band_counts, const int16_t *qmul)
 +{
 +    int i = 0, band = 0, band_left = band_counts[band];
 +    uint8_t *tp = p[0][nnz];
 +    uint8_t cache[1024];
 +
 +    do {
 +        int val, rc;
 +
 +        val = vp56_rac_get_prob_branchy(c, tp[0]); // eob
 +        eob[band][nnz][val]++;
 +        if (!val)
 +            break;
 +
 +    skip_eob:
 +        if (!vp56_rac_get_prob_branchy(c, tp[1])) { // zero
 +            cnt[band][nnz][0]++;
 +            if (!--band_left)
 +                band_left = band_counts[++band];
 +            cache[scan[i]] = 0;
 +            nnz = (1 + cache[nb[i][0]] + cache[nb[i][1]]) >> 1;
 +            tp = p[band][nnz];
 +            if (++i == n_coeffs)
 +                break; //invalid input; blocks should end with EOB
 +            goto skip_eob;
 +        }
 +
 +        rc = scan[i];
 +        if (!vp56_rac_get_prob_branchy(c, tp[2])) { // one
 +            cnt[band][nnz][1]++;
 +            val = 1;
 +            cache[rc] = 1;
 +        } else {
 +            // fill in p[3-10] (model fill) - only once per frame for each pos
 +            if (!tp[3])
 +                memcpy(&tp[3], vp9_model_pareto8[tp[2]], 8);
 +
 +            cnt[band][nnz][2]++;
 +            if (!vp56_rac_get_prob_branchy(c, tp[3])) { // 2, 3, 4
 +                if (!vp56_rac_get_prob_branchy(c, tp[4])) {
 +                    cache[rc] = val = 2;
 +                } else {
 +                    val = 3 + vp56_rac_get_prob(c, tp[5]);
 +                    cache[rc] = 3;
 +                }
 +            } else if (!vp56_rac_get_prob_branchy(c, tp[6])) { // cat1/2
 +                cache[rc] = 4;
 +                if (!vp56_rac_get_prob_branchy(c, tp[7])) {
 +                    val = 5 + vp56_rac_get_prob(c, 159);
 +                } else {
 +                    val = 7 + (vp56_rac_get_prob(c, 165) << 1) +
 +                               vp56_rac_get_prob(c, 145);
 +                }
 +            } else { // cat 3-6
 +                cache[rc] = 5;
 +                if (!vp56_rac_get_prob_branchy(c, tp[8])) {
 +                    if (!vp56_rac_get_prob_branchy(c, tp[9])) {
 +                        val = 11 + (vp56_rac_get_prob(c, 173) << 2) +
 +                                   (vp56_rac_get_prob(c, 148) << 1) +
 +                                    vp56_rac_get_prob(c, 140);
 +                    } else {
 +                        val = 19 + (vp56_rac_get_prob(c, 176) << 3) +
 +                                   (vp56_rac_get_prob(c, 155) << 2) +
 +                                   (vp56_rac_get_prob(c, 140) << 1) +
 +                                    vp56_rac_get_prob(c, 135);
 +                    }
 +                } else if (!vp56_rac_get_prob_branchy(c, tp[10])) {
 +                    val = 35 + (vp56_rac_get_prob(c, 180) << 4) +
 +                               (vp56_rac_get_prob(c, 157) << 3) +
 +                               (vp56_rac_get_prob(c, 141) << 2) +
 +                               (vp56_rac_get_prob(c, 134) << 1) +
 +                                vp56_rac_get_prob(c, 130);
 +                } else {
 +                    val = 67 + (vp56_rac_get_prob(c, 254) << 13) +
 +                               (vp56_rac_get_prob(c, 254) << 12) +
 +                               (vp56_rac_get_prob(c, 254) << 11) +
 +                               (vp56_rac_get_prob(c, 252) << 10) +
 +                               (vp56_rac_get_prob(c, 249) << 9) +
 +                               (vp56_rac_get_prob(c, 243) << 8) +
 +                               (vp56_rac_get_prob(c, 230) << 7) +
 +                               (vp56_rac_get_prob(c, 196) << 6) +
 +                               (vp56_rac_get_prob(c, 177) << 5) +
 +                               (vp56_rac_get_prob(c, 153) << 4) +
 +                               (vp56_rac_get_prob(c, 140) << 3) +
 +                               (vp56_rac_get_prob(c, 133) << 2) +
 +                               (vp56_rac_get_prob(c, 130) << 1) +
 +                                vp56_rac_get_prob(c, 129);
 +                }
 +            }
 +        }
 +        if (!--band_left)
 +            band_left = band_counts[++band];
 +        if (tx == TX_32X32) // FIXME slow
 +            coef[rc] = ((vp8_rac_get(c) ? -val : val) * qmul[!!i]) / 2;
 +        else
 +            coef[rc] = (vp8_rac_get(c) ? -val : val) * qmul[!!i];
 +        nnz = (1 + cache[nb[i][0]] + cache[nb[i][1]]) >> 1;
 +        tp = p[band][nnz];
 +    } while (++i < n_coeffs);
 +
 +    return i;
 +}
 +
 +static int decode_coeffs(AVCodecContext *ctx)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    VP9Block *const b = &s->b;
 +    int row = b->row, col = b->col;
 +    uint8_t (*p)[6][11] = s->prob.coef[b->tx][0 /* y */][!b->intra];
 +    unsigned (*c)[6][3] = s->counts.coef[b->tx][0 /* y */][!b->intra];
 +    unsigned (*e)[6][2] = s->counts.eob[b->tx][0 /* y */][!b->intra];
 +    int w4 = bwh_tab[1][b->bs][0] << 1, h4 = bwh_tab[1][b->bs][1] << 1;
 +    int end_x = FFMIN(2 * (s->cols - col), w4);
 +    int end_y = FFMIN(2 * (s->rows - row), h4);
 +    int n, pl, x, y, step1d = 1 << b->tx, step = 1 << (b->tx * 2);
 +    int uvstep1d = 1 << b->uvtx, uvstep = 1 << (b->uvtx * 2), res;
 +    int16_t (*qmul)[2] = s->segmentation.feat[b->seg_id].qmul;
 +    int tx = 4 * s->lossless + b->tx;
 +    const int16_t * const *yscans = vp9_scans[tx];
 +    const int16_t (* const *ynbs)[2] = vp9_scans_nb[tx];
 +    const int16_t *uvscan = vp9_scans[b->uvtx][DCT_DCT];
 +    const int16_t (*uvnb)[2] = vp9_scans_nb[b->uvtx][DCT_DCT];
 +    uint8_t *a = &s->above_y_nnz_ctx[col * 2];
 +    uint8_t *l = &s->left_y_nnz_ctx[(row & 7) << 1];
 +    static const int16_t band_counts[4][8] = {
 +        { 1, 2, 3, 4,  3,   16 - 13 },
 +        { 1, 2, 3, 4, 11,   64 - 21 },
 +        { 1, 2, 3, 4, 11,  256 - 21 },
 +        { 1, 2, 3, 4, 11, 1024 - 21 },
 +    };
 +    const int16_t *y_band_counts = band_counts[b->tx];
 +    const int16_t *uv_band_counts = band_counts[b->uvtx];
 +
 +    /* y tokens */
 +    if (b->tx > TX_4X4) { // FIXME slow
 +        for (y = 0; y < end_y; y += step1d)
 +            for (x = 1; x < step1d; x++)
 +                l[y] |= l[y + x];
 +        for (x = 0; x < end_x; x += step1d)
 +            for (y = 1; y < step1d; y++)
 +                a[x] |= a[x + y];
 +    }
 +    for (n = 0, y = 0; y < end_y; y += step1d) {
 +        for (x = 0; x < end_x; x += step1d, n += step) {
 +            enum TxfmType txtp = vp9_intra_txfm_type[b->mode[b->tx == TX_4X4 &&
 +                                                             b->bs > BS_8x8 ?
 +                                                             n : 0]];
 +            int nnz = a[x] + l[y];
 +            if ((res = decode_coeffs_b(&s->c, s->block + 16 * n, 16 * step,
 +                                       b->tx, c, e, p, nnz, yscans[txtp],
 +                                       ynbs[txtp], y_band_counts, qmul[0])) < 0)
 +                return res;
 +            a[x] = l[y] = !!res;
 +            if (b->tx > TX_8X8) {
 +                AV_WN16A(&s->eob[n], res);
 +            } else {
 +                s->eob[n] = res;
 +            }
 +        }
 +    }
 +    if (b->tx > TX_4X4) { // FIXME slow
 +        for (y = 0; y < end_y; y += step1d)
 +            memset(&l[y + 1], l[y], FFMIN(end_y - y - 1, step1d - 1));
 +        for (x = 0; x < end_x; x += step1d)
 +            memset(&a[x + 1], a[x], FFMIN(end_x - x - 1, step1d - 1));
 +    }
 +
 +    p = s->prob.coef[b->uvtx][1 /* uv */][!b->intra];
 +    c = s->counts.coef[b->uvtx][1 /* uv */][!b->intra];
 +    e = s->counts.eob[b->uvtx][1 /* uv */][!b->intra];
 +    w4 >>= 1;
 +    h4 >>= 1;
 +    end_x >>= 1;
 +    end_y >>= 1;
 +    for (pl = 0; pl < 2; pl++) {
 +        a = &s->above_uv_nnz_ctx[pl][col];
 +        l = &s->left_uv_nnz_ctx[pl][row & 7];
 +        if (b->uvtx > TX_4X4) { // FIXME slow
 +            for (y = 0; y < end_y; y += uvstep1d)
 +                for (x = 1; x < uvstep1d; x++)
 +                    l[y] |= l[y + x];
 +            for (x = 0; x < end_x; x += uvstep1d)
 +                for (y = 1; y < uvstep1d; y++)
 +                    a[x] |= a[x + y];
 +        }
 +        for (n = 0, y = 0; y < end_y; y += uvstep1d) {
 +            for (x = 0; x < end_x; x += uvstep1d, n += uvstep) {
 +                int nnz = a[x] + l[y];
 +                if ((res = decode_coeffs_b(&s->c, s->uvblock[pl] + 16 * n,
 +                                           16 * uvstep, b->uvtx, c, e, p, nnz,
 +                                           uvscan, uvnb, uv_band_counts,
 +                                           qmul[1])) < 0)
 +                    return res;
 +                a[x] = l[y] = !!res;
 +                if (b->uvtx > TX_8X8) {
 +                    AV_WN16A(&s->uveob[pl][n], res);
 +                } else {
 +                    s->uveob[pl][n] = res;
 +                }
 +            }
 +        }
 +        if (b->uvtx > TX_4X4) { // FIXME slow
 +            for (y = 0; y < end_y; y += uvstep1d)
 +                memset(&l[y + 1], l[y], FFMIN(end_y - y - 1, uvstep1d - 1));
 +            for (x = 0; x < end_x; x += uvstep1d)
 +                memset(&a[x + 1], a[x], FFMIN(end_x - x - 1, uvstep1d - 1));
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static av_always_inline int check_intra_mode(VP9Context *s, int mode, uint8_t **a,
 +                                             uint8_t *dst_edge, ptrdiff_t stride_edge,
 +                                             uint8_t *dst_inner, ptrdiff_t stride_inner,
 +                                             uint8_t *l, int col, int x, int w,
 +                                             int row, int y, enum TxfmMode tx,
 +                                             int p)
 +{
 +    int have_top = row > 0 || y > 0;
 +    int have_left = col > s->tiling.tile_col_start || x > 0;
 +    int have_right = x < w - 1;
 +    static const uint8_t mode_conv[10][2 /* have_left */][2 /* have_top */] = {
 +        [VERT_PRED]            = { { DC_127_PRED,          VERT_PRED },
 +                                   { DC_127_PRED,          VERT_PRED } },
 +        [HOR_PRED]             = { { DC_129_PRED,          DC_129_PRED },
 +                                   { HOR_PRED,             HOR_PRED } },
 +        [DC_PRED]              = { { DC_128_PRED,          TOP_DC_PRED },
 +                                   { LEFT_DC_PRED,         DC_PRED } },
 +        [DIAG_DOWN_LEFT_PRED]  = { { DC_127_PRED,          DIAG_DOWN_LEFT_PRED },
 +                                   { DC_127_PRED,          DIAG_DOWN_LEFT_PRED } },
 +        [DIAG_DOWN_RIGHT_PRED] = { { DIAG_DOWN_RIGHT_PRED, DIAG_DOWN_RIGHT_PRED },
 +                                   { DIAG_DOWN_RIGHT_PRED, DIAG_DOWN_RIGHT_PRED } },
 +        [VERT_RIGHT_PRED]      = { { VERT_RIGHT_PRED,      VERT_RIGHT_PRED },
 +                                   { VERT_RIGHT_PRED,      VERT_RIGHT_PRED } },
 +        [HOR_DOWN_PRED]        = { { HOR_DOWN_PRED,        HOR_DOWN_PRED },
 +                                   { HOR_DOWN_PRED,        HOR_DOWN_PRED } },
 +        [VERT_LEFT_PRED]       = { { DC_127_PRED,          VERT_LEFT_PRED },
 +                                   { DC_127_PRED,          VERT_LEFT_PRED } },
 +        [HOR_UP_PRED]          = { { DC_129_PRED,          DC_129_PRED },
 +                                   { HOR_UP_PRED,          HOR_UP_PRED } },
 +        [TM_VP8_PRED]          = { { DC_129_PRED,          VERT_PRED },
 +                                   { HOR_PRED,             TM_VP8_PRED } },
 +    };
 +    static const struct {
 +        uint8_t needs_left:1;
 +        uint8_t needs_top:1;
 +        uint8_t needs_topleft:1;
 +        uint8_t needs_topright:1;
 +    } edges[N_INTRA_PRED_MODES] = {
 +        [VERT_PRED]            = { .needs_top  = 1 },
 +        [HOR_PRED]             = { .needs_left = 1 },
 +        [DC_PRED]              = { .needs_top  = 1, .needs_left = 1 },
 +        [DIAG_DOWN_LEFT_PRED]  = { .needs_top  = 1, .needs_topright = 1 },
 +        [DIAG_DOWN_RIGHT_PRED] = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 },
 +        [VERT_RIGHT_PRED]      = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 },
 +        [HOR_DOWN_PRED]        = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 },
 +        [VERT_LEFT_PRED]       = { .needs_top  = 1, .needs_topright = 1 },
 +        [HOR_UP_PRED]          = { .needs_left = 1 },
 +        [TM_VP8_PRED]          = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 },
 +        [LEFT_DC_PRED]         = { .needs_left = 1 },
 +        [TOP_DC_PRED]          = { .needs_top  = 1 },
 +        [DC_128_PRED]          = { 0 },
 +        [DC_127_PRED]          = { 0 },
 +        [DC_129_PRED]          = { 0 }
 +    };
 +
 +    av_assert2(mode >= 0 && mode < 10);
 +    mode = mode_conv[mode][have_left][have_top];
 +    if (edges[mode].needs_top) {
 +        uint8_t *top, *topleft;
 +        int n_px_need = 4 << tx, n_px_have = (((s->cols - col) << !p) - x) * 4;
 +        int n_px_need_tr = 0;
 +
 +        if (tx == TX_4X4 && edges[mode].needs_topright && have_right)
 +            n_px_need_tr = 4;
 +
 +        // if top of sb64-row, use s->intra_pred_data[] instead of
 +        // dst[-stride] for intra prediction (it contains pre- instead of
 +        // post-loopfilter data)
 +        if (have_top) {
 +            top = !(row & 7) && !y ?
 +                s->intra_pred_data[p] + col * (8 >> !!p) + x * 4 :
 +                y == 0 ? &dst_edge[-stride_edge] : &dst_inner[-stride_inner];
 +            if (have_left)
 +                topleft = !(row & 7) && !y ?
 +                    s->intra_pred_data[p] + col * (8 >> !!p) + x * 4 :
 +                    y == 0 || x == 0 ? &dst_edge[-stride_edge] :
 +                    &dst_inner[-stride_inner];
 +        }
 +
 +        if (have_top &&
 +            (!edges[mode].needs_topleft || (have_left && top == topleft)) &&
 +            (tx != TX_4X4 || !edges[mode].needs_topright || have_right) &&
 +            n_px_need + n_px_need_tr <= n_px_have) {
 +            *a = top;
 +        } else {
 +            if (have_top) {
 +                if (n_px_need <= n_px_have) {
 +                    memcpy(*a, top, n_px_need);
 +                } else {
 +                    memcpy(*a, top, n_px_have);
 +                    memset(&(*a)[n_px_have], (*a)[n_px_have - 1],
 +                           n_px_need - n_px_have);
 +                }
 +            } else {
 +                memset(*a, 127, n_px_need);
 +            }
 +            if (edges[mode].needs_topleft) {
 +                if (have_left && have_top) {
 +                    (*a)[-1] = topleft[-1];
 +                } else {
 +                    (*a)[-1] = have_top ? 129 : 127;
 +                }
 +            }
 +            if (tx == TX_4X4 && edges[mode].needs_topright) {
 +                if (have_top && have_right &&
 +                    n_px_need + n_px_need_tr <= n_px_have) {
 +                    memcpy(&(*a)[4], &top[4], 4);
 +                } else {
 +                    memset(&(*a)[4], (*a)[3], 4);
 +                }
 +            }
 +        }
 +    }
 +    if (edges[mode].needs_left) {
 +        if (have_left) {
 +            int n_px_need = 4 << tx, i, n_px_have = (((s->rows - row) << !p) - y) * 4;
 +            uint8_t *dst = x == 0 ? dst_edge : dst_inner;
 +            ptrdiff_t stride = x == 0 ? stride_edge : stride_inner;
 +
 +            if (n_px_need <= n_px_have) {
 +                for (i = 0; i < n_px_need; i++)
 +                    l[i] = dst[i * stride - 1];
 +            } else {
 +                for (i = 0; i < n_px_have; i++)
 +                    l[i] = dst[i * stride - 1];
 +                memset(&l[i], l[i - 1], n_px_need - n_px_have);
 +            }
 +        } else {
 +            memset(l, 129, 4 << tx);
 +        }
 +    }
 +
 +    return mode;
 +}
 +
 +static void intra_recon(AVCodecContext *ctx, ptrdiff_t y_off, ptrdiff_t uv_off)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    VP9Block *const b = &s->b;
 +    int row = b->row, col = b->col;
 +    int w4 = bwh_tab[1][b->bs][0] << 1, step1d = 1 << b->tx, n;
 +    int h4 = bwh_tab[1][b->bs][1] << 1, x, y, step = 1 << (b->tx * 2);
 +    int end_x = FFMIN(2 * (s->cols - col), w4);
 +    int end_y = FFMIN(2 * (s->rows - row), h4);
 +    int tx = 4 * s->lossless + b->tx, uvtx = b->uvtx + 4 * s->lossless;
 +    int uvstep1d = 1 << b->uvtx, p;
 +    uint8_t *dst = b->dst[0], *dst_r = s->f->data[0] + y_off;
 +
 +    for (n = 0, y = 0; y < end_y; y += step1d) {
 +        uint8_t *ptr = dst, *ptr_r = dst_r;
 +        for (x = 0; x < end_x; x += step1d, ptr += 4 * step1d,
 +                               ptr_r += 4 * step1d, n += step) {
 +            int mode = b->mode[b->bs > BS_8x8 && b->tx == TX_4X4 ?
 +                               y * 2 + x : 0];
 +            LOCAL_ALIGNED_16(uint8_t, a_buf, [48]);
 +            uint8_t *a = &a_buf[16], l[32];
 +            enum TxfmType txtp = vp9_intra_txfm_type[mode];
 +            int eob = b->skip ? 0 : b->tx > TX_8X8 ? AV_RN16A(&s->eob[n]) : s->eob[n];
 +
 +            mode = check_intra_mode(s, mode, &a, ptr_r, s->f->linesize[0],
 +                                    ptr, b->y_stride, l,
 +                                    col, x, w4, row, y, b->tx, 0);
 +            s->dsp.intra_pred[b->tx][mode](ptr, b->y_stride, l, a);
 +            if (eob)
 +                s->dsp.itxfm_add[tx][txtp](ptr, b->y_stride,
 +                                           s->block + 16 * n, eob);
 +        }
 +        dst_r += 4 * s->f->linesize[0] * step1d;
 +        dst   += 4 * b->y_stride       * step1d;
 +    }
 +
 +    // U/V
 +    h4 >>= 1;
 +    w4 >>= 1;
 +    end_x >>= 1;
 +    end_y >>= 1;
 +    step = 1 << (b->uvtx * 2);
 +    for (p = 0; p < 2; p++) {
 +        dst   = b->dst[1 + p];
 +        dst_r = s->f->data[1 + p] + uv_off;
 +        for (n = 0, y = 0; y < end_y; y += uvstep1d) {
 +            uint8_t *ptr = dst, *ptr_r = dst_r;
 +            for (x = 0; x < end_x; x += uvstep1d, ptr += 4 * uvstep1d,
 +                                   ptr_r += 4 * uvstep1d, n += step) {
 +                int mode = b->uvmode;
 +                LOCAL_ALIGNED_16(uint8_t, a_buf, [48]);
 +                uint8_t *a = &a_buf[16], l[32];
 +                int eob = b->skip ? 0 : b->uvtx > TX_8X8 ? AV_RN16A(&s->uveob[p][n]) : s->uveob[p][n];
 +
 +                mode = check_intra_mode(s, mode, &a, ptr_r, s->f->linesize[1],
 +                                        ptr, b->uv_stride, l,
 +                                        col, x, w4, row, y, b->uvtx, p + 1);
 +                s->dsp.intra_pred[b->uvtx][mode](ptr, b->uv_stride, l, a);
 +                if (eob)
 +                    s->dsp.itxfm_add[uvtx][DCT_DCT](ptr, b->uv_stride,
 +                                                    s->uvblock[p] + 16 * n, eob);
 +            }
 +            dst_r += 4 * uvstep1d * s->f->linesize[1];
 +            dst   += 4 * uvstep1d * b->uv_stride;
 +        }
 +    }
 +}
 +
 +static av_always_inline void mc_luma_dir(VP9Context *s, vp9_mc_func (*mc)[2],
 +                                         uint8_t *dst, ptrdiff_t dst_stride,
 +                                         const uint8_t *ref, ptrdiff_t ref_stride,
 +                                         ptrdiff_t y, ptrdiff_t x, const VP56mv *mv,
 +                                         int bw, int bh, int w, int h)
 +{
 +    int mx = mv->x, my = mv->y;
 +
 +    y += my >> 3;
 +    x += mx >> 3;
 +    ref += y * ref_stride + x;
 +    mx &= 7;
 +    my &= 7;
 +    // FIXME bilinear filter only needs 0/1 pixels, not 3/4
 +    if (x < !!mx * 3 || y < !!my * 3 ||
 +        x + !!mx * 4 > w - bw || y + !!my * 4 > h - bh) {
-                                  ref_stride,
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer,
 +                                 ref - !!my * 3 * ref_stride - !!mx * 3,
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, 80,
-                                  ref_u - !!my * 3 * src_stride_u - !!mx * 3, src_stride_u,
++                                 80, ref_stride,
 +                                 bw + !!mx * 7, bh + !!my * 7,
 +                                 x - !!mx * 3, y - !!my * 3, w, h);
 +        ref = s->edge_emu_buffer + !!my * 3 * 80 + !!mx * 3;
 +        ref_stride = 80;
 +    }
 +    mc[!!mx][!!my](dst, dst_stride, ref, ref_stride, bh, mx << 1, my << 1);
 +}
 +
 +static av_always_inline void mc_chroma_dir(VP9Context *s, vp9_mc_func (*mc)[2],
 +                                           uint8_t *dst_u, uint8_t *dst_v,
 +                                           ptrdiff_t dst_stride,
 +                                           const uint8_t *ref_u, ptrdiff_t src_stride_u,
 +                                           const uint8_t *ref_v, ptrdiff_t src_stride_v,
 +                                           ptrdiff_t y, ptrdiff_t x, const VP56mv *mv,
 +                                           int bw, int bh, int w, int h)
 +{
 +    int mx = mv->x, my = mv->y;
 +
 +    y += my >> 4;
 +    x += mx >> 4;
 +    ref_u += y * src_stride_u + x;
 +    ref_v += y * src_stride_v + x;
 +    mx &= 15;
 +    my &= 15;
 +    // FIXME bilinear filter only needs 0/1 pixels, not 3/4
 +    if (x < !!mx * 3 || y < !!my * 3 ||
 +        x + !!mx * 4 > w - bw || y + !!my * 4 > h - bh) {
-         s->vdsp.emulated_edge_mc(s->edge_emu_buffer, 80,
-                                  ref_v - !!my * 3 * src_stride_v - !!mx * 3, src_stride_v,
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer,
++                                 ref_u - !!my * 3 * src_stride_u - !!mx * 3,
++                                 80, src_stride_u,
 +                                 bw + !!mx * 7, bh + !!my * 7,
 +                                 x - !!mx * 3, y - !!my * 3, w, h);
 +        ref_u = s->edge_emu_buffer + !!my * 3 * 80 + !!mx * 3;
 +        mc[!!mx][!!my](dst_u, dst_stride, ref_u, 80, bh, mx, my);
 +
++        s->vdsp.emulated_edge_mc(s->edge_emu_buffer,
++                                 ref_v - !!my * 3 * src_stride_v - !!mx * 3,
++                                 80, src_stride_v,
 +                                 bw + !!mx * 7, bh + !!my * 7,
 +                                 x - !!mx * 3, y - !!my * 3, w, h);
 +        ref_v = s->edge_emu_buffer + !!my * 3 * 80 + !!mx * 3;
 +        mc[!!mx][!!my](dst_v, dst_stride, ref_v, 80, bh, mx, my);
 +    } else {
 +        mc[!!mx][!!my](dst_u, dst_stride, ref_u, src_stride_u, bh, mx, my);
 +        mc[!!mx][!!my](dst_v, dst_stride, ref_v, src_stride_v, bh, mx, my);
 +    }
 +}
 +
 +static void inter_recon(AVCodecContext *ctx)
 +{
 +    static const uint8_t bwlog_tab[2][N_BS_SIZES] = {
 +        { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 },
 +        { 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4 },
 +    };
 +    VP9Context *s = ctx->priv_data;
 +    VP9Block *const b = &s->b;
 +    int row = b->row, col = b->col;
 +    AVFrame *ref1 = s->refs[s->refidx[b->ref[0]]];
 +    AVFrame *ref2 = b->comp ? s->refs[s->refidx[b->ref[1]]] : NULL;
 +    int w = ctx->width, h = ctx->height;
 +    ptrdiff_t ls_y = b->y_stride, ls_uv = b->uv_stride;
 +
 +    // y inter pred
 +    if (b->bs > BS_8x8) {
 +        if (b->bs == BS_8x4) {
 +            mc_luma_dir(s, s->dsp.mc[3][b->filter][0], b->dst[0], ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        row << 3, col << 3, &b->mv[0][0], 8, 4, w, h);
 +            mc_luma_dir(s, s->dsp.mc[3][b->filter][0],
 +                        b->dst[0] + 4 * ls_y, ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        (row << 3) + 4, col << 3, &b->mv[2][0], 8, 4, w, h);
 +
 +            if (b->comp) {
 +                mc_luma_dir(s, s->dsp.mc[3][b->filter][1], b->dst[0], ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            row << 3, col << 3, &b->mv[0][1], 8, 4, w, h);
 +                mc_luma_dir(s, s->dsp.mc[3][b->filter][1],
 +                            b->dst[0] + 4 * ls_y, ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            (row << 3) + 4, col << 3, &b->mv[2][1], 8, 4, w, h);
 +            }
 +        } else if (b->bs == BS_4x8) {
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0], b->dst[0], ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        row << 3, col << 3, &b->mv[0][0], 4, 8, w, h);
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0], b->dst[0] + 4, ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        row << 3, (col << 3) + 4, &b->mv[1][0], 4, 8, w, h);
 +
 +            if (b->comp) {
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1], b->dst[0], ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            row << 3, col << 3, &b->mv[0][1], 4, 8, w, h);
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1], b->dst[0] + 4, ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            row << 3, (col << 3) + 4, &b->mv[1][1], 4, 8, w, h);
 +            }
 +        } else {
 +            av_assert2(b->bs == BS_4x4);
 +
 +            // FIXME if two horizontally adjacent blocks have the same MV,
 +            // do a w8 instead of a w4 call
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0], b->dst[0], ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        row << 3, col << 3, &b->mv[0][0], 4, 4, w, h);
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0], b->dst[0] + 4, ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        row << 3, (col << 3) + 4, &b->mv[1][0], 4, 4, w, h);
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0],
 +                        b->dst[0] + 4 * ls_y, ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        (row << 3) + 4, col << 3, &b->mv[2][0], 4, 4, w, h);
 +            mc_luma_dir(s, s->dsp.mc[4][b->filter][0],
 +                        b->dst[0] + 4 * ls_y + 4, ls_y,
 +                        ref1->data[0], ref1->linesize[0],
 +                        (row << 3) + 4, (col << 3) + 4, &b->mv[3][0], 4, 4, w, h);
 +
 +            if (b->comp) {
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1], b->dst[0], ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            row << 3, col << 3, &b->mv[0][1], 4, 4, w, h);
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1], b->dst[0] + 4, ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            row << 3, (col << 3) + 4, &b->mv[1][1], 4, 4, w, h);
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1],
 +                            b->dst[0] + 4 * ls_y, ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            (row << 3) + 4, col << 3, &b->mv[2][1], 4, 4, w, h);
 +                mc_luma_dir(s, s->dsp.mc[4][b->filter][1],
 +                            b->dst[0] + 4 * ls_y + 4, ls_y,
 +                            ref2->data[0], ref2->linesize[0],
 +                            (row << 3) + 4, (col << 3) + 4, &b->mv[3][1], 4, 4, w, h);
 +            }
 +        }
 +    } else {
 +        int bwl = bwlog_tab[0][b->bs];
 +        int bw = bwh_tab[0][b->bs][0] * 4, bh = bwh_tab[0][b->bs][1] * 4;
 +
 +        mc_luma_dir(s, s->dsp.mc[bwl][b->filter][0], b->dst[0], ls_y,
 +                    ref1->data[0], ref1->linesize[0],
 +                    row << 3, col << 3, &b->mv[0][0],bw, bh, w, h);
 +
 +        if (b->comp)
 +            mc_luma_dir(s, s->dsp.mc[bwl][b->filter][1], b->dst[0], ls_y,
 +                        ref2->data[0], ref2->linesize[0],
 +                        row << 3, col << 3, &b->mv[0][1], bw, bh, w, h);
 +    }
 +
 +    // uv inter pred
 +    {
 +        int bwl = bwlog_tab[1][b->bs];
 +        int bw = bwh_tab[1][b->bs][0] * 4, bh = bwh_tab[1][b->bs][1] * 4;
 +        VP56mv mvuv;
 +
 +        w = (w + 1) >> 1;
 +        h = (h + 1) >> 1;
 +        if (b->bs > BS_8x8) {
 +            mvuv.x = ROUNDED_DIV(b->mv[0][0].x + b->mv[1][0].x + b->mv[2][0].x + b->mv[3][0].x, 4);
 +            mvuv.y = ROUNDED_DIV(b->mv[0][0].y + b->mv[1][0].y + b->mv[2][0].y + b->mv[3][0].y, 4);
 +        } else {
 +            mvuv = b->mv[0][0];
 +        }
 +
 +        mc_chroma_dir(s, s->dsp.mc[bwl][b->filter][0],
 +                      b->dst[1], b->dst[2], ls_uv,
 +                      ref1->data[1], ref1->linesize[1],
 +                      ref1->data[2], ref1->linesize[2],
 +                      row << 2, col << 2, &mvuv, bw, bh, w, h);
 +
 +        if (b->comp) {
 +            if (b->bs > BS_8x8) {
 +                mvuv.x = ROUNDED_DIV(b->mv[0][1].x + b->mv[1][1].x + b->mv[2][1].x + b->mv[3][1].x, 4);
 +                mvuv.y = ROUNDED_DIV(b->mv[0][1].y + b->mv[1][1].y + b->mv[2][1].y + b->mv[3][1].y, 4);
 +            } else {
 +                mvuv = b->mv[0][1];
 +            }
 +            mc_chroma_dir(s, s->dsp.mc[bwl][b->filter][1],
 +                          b->dst[1], b->dst[2], ls_uv,
 +                          ref2->data[1], ref2->linesize[1],
 +                          ref2->data[2], ref2->linesize[2],
 +                          row << 2, col << 2, &mvuv, bw, bh, w, h);
 +        }
 +    }
 +
 +    if (!b->skip) {
 +        /* mostly copied intra_reconn() */
 +
 +        int w4 = bwh_tab[1][b->bs][0] << 1, step1d = 1 << b->tx, n;
 +        int h4 = bwh_tab[1][b->bs][1] << 1, x, y, step = 1 << (b->tx * 2);
 +        int end_x = FFMIN(2 * (s->cols - col), w4);
 +        int end_y = FFMIN(2 * (s->rows - row), h4);
 +        int tx = 4 * s->lossless + b->tx, uvtx = b->uvtx + 4 * s->lossless;
 +        int uvstep1d = 1 << b->uvtx, p;
 +        uint8_t *dst = b->dst[0];
 +
 +        // y itxfm add
 +        for (n = 0, y = 0; y < end_y; y += step1d) {
 +            uint8_t *ptr = dst;
 +            for (x = 0; x < end_x; x += step1d, ptr += 4 * step1d, n += step) {
 +                int eob = b->tx > TX_8X8 ? AV_RN16A(&s->eob[n]) : s->eob[n];
 +
 +                if (eob)
 +                    s->dsp.itxfm_add[tx][DCT_DCT](ptr, b->y_stride,
 +                                                  s->block + 16 * n, eob);
 +            }
 +            dst += 4 * b->y_stride * step1d;
 +        }
 +
 +        // uv itxfm add
 +        h4 >>= 1;
 +        w4 >>= 1;
 +        end_x >>= 1;
 +        end_y >>= 1;
 +        step = 1 << (b->uvtx * 2);
 +        for (p = 0; p < 2; p++) {
 +            dst = b->dst[p + 1];
 +            for (n = 0, y = 0; y < end_y; y += uvstep1d) {
 +                uint8_t *ptr = dst;
 +                for (x = 0; x < end_x; x += uvstep1d, ptr += 4 * uvstep1d, n += step) {
 +                    int eob = b->uvtx > TX_8X8 ? AV_RN16A(&s->uveob[p][n]) : s->uveob[p][n];
 +
 +                    if (eob)
 +                        s->dsp.itxfm_add[uvtx][DCT_DCT](ptr, b->uv_stride,
 +                                                        s->uvblock[p] + 16 * n, eob);
 +                }
 +                dst += 4 * uvstep1d * b->uv_stride;
 +            }
 +        }
 +    }
 +}
 +
 +static av_always_inline void mask_edges(struct VP9Filter *lflvl, int is_uv,
 +                                        int row_and_7, int col_and_7,
 +                                        int w, int h, int col_end, int row_end,
 +                                        enum TxfmMode tx, int skip_inter)
 +{
 +    // FIXME I'm pretty sure all loops can be replaced by a single LUT if
 +    // we make VP9Filter.mask uint64_t (i.e. row/col all single variable)
 +    // and make the LUT 5-indexed (bl, bp, is_uv, tx and row/col), and then
 +    // use row_and_7/col_and_7 as shifts (1*col_and_7+8*row_and_7)
 +
 +    // the intended behaviour of the vp9 loopfilter is to work on 8-pixel
 +    // edges. This means that for UV, we work on two subsampled blocks at
 +    // a time, and we only use the topleft block's mode information to set
 +    // things like block strength. Thus, for any block size smaller than
 +    // 16x16, ignore the odd portion of the block.
 +    if (tx == TX_4X4 && is_uv) {
 +        if (h == 1) {
 +            if (row_and_7 & 1)
 +                return;
 +            if (!row_end)
 +                h += 1;
 +        }
 +        if (w == 1) {
 +            if (col_and_7 & 1)
 +                return;
 +            if (!col_end)
 +                w += 1;
 +        }
 +    }
 +
 +    if (tx == TX_4X4 && !skip_inter) {
 +        int t = 1 << col_and_7, m_col = (t << w) - t, y;
 +        int m_col_odd = (t << (w - 1)) - t;
 +
 +        // on 32-px edges, use the 8-px wide loopfilter; else, use 4-px wide
 +        if (is_uv) {
 +            int m_row_8 = m_col & 0x01, m_row_4 = m_col - m_row_8;
 +
 +            for (y = row_and_7; y < h + row_and_7; y++) {
 +                int col_mask_id = 2 - !(y & 7);
 +
 +                lflvl->mask[is_uv][0][y][1] |= m_row_8;
 +                lflvl->mask[is_uv][0][y][2] |= m_row_4;
 +                // for odd lines, if the odd col is not being filtered,
 +                // skip odd row also:
 +                // .---. <-- a
 +                // |   |
 +                // |___| <-- b
 +                // ^   ^
 +                // c   d
 +                //
 +                // if a/c are even row/col and b/d are odd, and d is skipped,
 +                // e.g. right edge of size-66x66.webm, then skip b also (bug)
 +                if ((col_end & 1) && (y & 1)) {
 +                    lflvl->mask[is_uv][1][y][col_mask_id] |= m_col_odd;
 +                } else {
 +                    lflvl->mask[is_uv][1][y][col_mask_id] |= m_col;
 +                }
 +            }
 +        } else {
 +            int m_row_8 = m_col & 0x11, m_row_4 = m_col - m_row_8;
 +
 +            for (y = row_and_7; y < h + row_and_7; y++) {
 +                int col_mask_id = 2 - !(y & 3);
 +
 +                lflvl->mask[is_uv][0][y][1] |= m_row_8; // row edge
 +                lflvl->mask[is_uv][0][y][2] |= m_row_4;
 +                lflvl->mask[is_uv][1][y][col_mask_id] |= m_col; // col edge
 +                lflvl->mask[is_uv][0][y][3] |= m_col;
 +                lflvl->mask[is_uv][1][y][3] |= m_col;
 +            }
 +        }
 +    } else {
 +        int y, t = 1 << col_and_7, m_col = (t << w) - t;
 +
 +        if (!skip_inter) {
 +            int mask_id = (tx == TX_8X8);
 +            int l2 = tx + is_uv - 1, step1d = 1 << l2;
 +            static const unsigned masks[4] = { 0xff, 0x55, 0x11, 0x01 };
 +            int m_row = m_col & masks[l2];
 +
 +            // at odd UV col/row edges tx16/tx32 loopfilter edges, force
 +            // 8wd loopfilter to prevent going off the visible edge.
 +            if (is_uv && tx > TX_8X8 && (w ^ (w - 1)) == 1) {
 +                int m_row_16 = ((t << (w - 1)) - t) & masks[l2];
 +                int m_row_8 = m_row - m_row_16;
 +
 +                for (y = row_and_7; y < h + row_and_7; y++) {
 +                    lflvl->mask[is_uv][0][y][0] |= m_row_16;
 +                    lflvl->mask[is_uv][0][y][1] |= m_row_8;
 +                }
 +            } else {
 +                for (y = row_and_7; y < h + row_and_7; y++)
 +                    lflvl->mask[is_uv][0][y][mask_id] |= m_row;
 +            }
 +
 +            if (is_uv && tx > TX_8X8 && (h ^ (h - 1)) == 1) {
 +                for (y = row_and_7; y < h + row_and_7 - 1; y += step1d)
 +                    lflvl->mask[is_uv][1][y][0] |= m_col;
 +                if (y - row_and_7 == h - 1)
 +                    lflvl->mask[is_uv][1][y][1] |= m_col;
 +            } else {
 +                for (y = row_and_7; y < h + row_and_7; y += step1d)
 +                    lflvl->mask[is_uv][1][y][mask_id] |= m_col;
 +            }
 +        } else if (tx != TX_4X4) {
 +            int mask_id;
 +
 +            mask_id = (tx == TX_8X8) || (is_uv && h == 1);
 +            lflvl->mask[is_uv][1][row_and_7][mask_id] |= m_col;
 +            mask_id = (tx == TX_8X8) || (is_uv && w == 1);
 +            for (y = row_and_7; y < h + row_and_7; y++)
 +                lflvl->mask[is_uv][0][y][mask_id] |= t;
 +        } else if (is_uv) {
 +            int t8 = t & 0x01, t4 = t - t8;
 +
 +            for (y = row_and_7; y < h + row_and_7; y++) {
 +                lflvl->mask[is_uv][0][y][2] |= t4;
 +                lflvl->mask[is_uv][0][y][1] |= t8;
 +            }
 +            lflvl->mask[is_uv][1][row_and_7][2 - !(row_and_7 & 7)] |= m_col;
 +        } else {
 +            int t8 = t & 0x11, t4 = t - t8;
 +
 +            for (y = row_and_7; y < h + row_and_7; y++) {
 +                lflvl->mask[is_uv][0][y][2] |= t4;
 +                lflvl->mask[is_uv][0][y][1] |= t8;
 +            }
 +            lflvl->mask[is_uv][1][row_and_7][2 - !(row_and_7 & 3)] |= m_col;
 +        }
 +    }
 +}
 +
 +static int decode_b(AVCodecContext *ctx, int row, int col,
 +                    struct VP9Filter *lflvl, ptrdiff_t yoff, ptrdiff_t uvoff,
 +                    enum BlockLevel bl, enum BlockPartition bp)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    VP9Block *const b = &s->b;
 +    enum BlockSize bs = bl * 3 + bp;
 +    int res, y, w4 = bwh_tab[1][bs][0], h4 = bwh_tab[1][bs][1], lvl;
 +    int emu[2];
 +
 +    b->row = row;
 +    b->row7 = row & 7;
 +    b->col = col;
 +    b->col7 = col & 7;
 +    s->min_mv.x = -(128 + col * 64);
 +    s->min_mv.y = -(128 + row * 64);
 +    s->max_mv.x = 128 + (s->cols - col - w4) * 64;
 +    s->max_mv.y = 128 + (s->rows - row - h4) * 64;
 +    b->bs = bs;
 +    decode_mode(ctx);
 +    b->uvtx = b->tx - (w4 * 2 == (1 << b->tx) || h4 * 2 == (1 << b->tx));
 +
 +    if (!b->skip) {
 +        if ((res = decode_coeffs(ctx)) < 0)
 +            return res;
 +    } else {
 +        int pl;
 +
 +        memset(&s->above_y_nnz_ctx[col * 2], 0, w4 * 2);
 +        memset(&s->left_y_nnz_ctx[(row & 7) << 1], 0, h4 * 2);
 +        for (pl = 0; pl < 2; pl++) {
 +            memset(&s->above_uv_nnz_ctx[pl][col], 0, w4);
 +            memset(&s->left_uv_nnz_ctx[pl][row & 7], 0, h4);
 +        }
 +    }
 +
 +    // emulated overhangs if the stride of the target buffer can't hold. This
 +    // allows to support emu-edge and so on even if we have large block
 +    // overhangs
 +    emu[0] = (col + w4) * 8 > s->f->linesize[0] ||
 +             (row + h4) > s->rows + 2 * !(ctx->flags & CODEC_FLAG_EMU_EDGE);
 +    emu[1] = (col + w4) * 4 > s->f->linesize[1] ||
 +             (row + h4) > s->rows + 2 * !(ctx->flags & CODEC_FLAG_EMU_EDGE);
 +    if (emu[0]) {
 +        b->dst[0] = s->tmp_y;
 +        b->y_stride = 64;
 +    } else {
 +        b->dst[0] = s->f->data[0] + yoff;
 +        b->y_stride = s->f->linesize[0];
 +    }
 +    if (emu[1]) {
 +        b->dst[1] = s->tmp_uv[0];
 +        b->dst[2] = s->tmp_uv[1];
 +        b->uv_stride = 32;
 +    } else {
 +        b->dst[1] = s->f->data[1] + uvoff;
 +        b->dst[2] = s->f->data[2] + uvoff;
 +        b->uv_stride = s->f->linesize[1];
 +    }
 +    if (b->intra) {
 +        intra_recon(ctx, yoff, uvoff);
 +    } else {
 +        inter_recon(ctx);
 +    }
 +    if (emu[0]) {
 +        int w = FFMIN(s->cols - col, w4) * 8, h = FFMIN(s->rows - row, h4) * 8, n, o = 0;
 +
 +        for (n = 0; o < w; n++) {
 +            int bw = 64 >> n;
 +
 +            av_assert2(n <= 4);
 +            if (w & bw) {
 +                s->dsp.mc[n][0][0][0][0](s->f->data[0] + yoff + o, s->f->linesize[0],
 +                                         s->tmp_y + o, 64, h, 0, 0);
 +                o += bw;
 +            }
 +        }
 +    }
 +    if (emu[1]) {
 +        int w = FFMIN(s->cols - col, w4) * 4, h = FFMIN(s->rows - row, h4) * 4, n, o = 0;
 +
 +        for (n = 1; o < w; n++) {
 +            int bw = 64 >> n;
 +
 +            av_assert2(n <= 4);
 +            if (w & bw) {
 +                s->dsp.mc[n][0][0][0][0](s->f->data[1] + uvoff + o, s->f->linesize[1],
 +                                         s->tmp_uv[0] + o, 32, h, 0, 0);
 +                s->dsp.mc[n][0][0][0][0](s->f->data[2] + uvoff + o, s->f->linesize[2],
 +                                         s->tmp_uv[1] + o, 32, h, 0, 0);
 +                o += bw;
 +            }
 +        }
 +    }
 +
 +    // pick filter level and find edges to apply filter to
 +    if (s->filter.level &&
 +        (lvl = s->segmentation.feat[b->seg_id].lflvl[b->intra ? 0 : b->ref[0] + 1]
 +                                                    [b->mode[3] != ZEROMV]) > 0) {
 +        int x_end = FFMIN(s->cols - col, w4), y_end = FFMIN(s->rows - row, h4);
 +        int skip_inter = !b->intra && b->skip;
 +
 +        for (y = 0; y < h4; y++)
 +            memset(&lflvl->level[((row & 7) + y) * 8 + (col & 7)], lvl, w4);
 +        mask_edges(lflvl, 0, row & 7, col & 7, x_end, y_end, 0, 0, b->tx, skip_inter);
 +        mask_edges(lflvl, 1, row & 7, col & 7, x_end, y_end,
 +                   s->cols & 1 && col + w4 >= s->cols ? s->cols & 7 : 0,
 +                   s->rows & 1 && row + h4 >= s->rows ? s->rows & 7 : 0,
 +                   b->uvtx, skip_inter);
 +
 +        if (!s->filter.lim_lut[lvl]) {
 +            int sharp = s->filter.sharpness;
 +            int limit = lvl;
 +
 +            if (sharp > 0) {
 +                limit >>= (sharp + 3) >> 2;
 +                limit = FFMIN(limit, 9 - sharp);
 +            }
 +            limit = FFMAX(limit, 1);
 +
 +            s->filter.lim_lut[lvl] = limit;
 +            s->filter.mblim_lut[lvl] = 2 * (lvl + 2) + limit;
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int decode_sb(AVCodecContext *ctx, int row, int col, struct VP9Filter *lflvl,
 +                     ptrdiff_t yoff, ptrdiff_t uvoff, enum BlockLevel bl)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    int c = ((s->above_partition_ctx[col] >> (3 - bl)) & 1) |
 +            (((s->left_partition_ctx[row & 0x7] >> (3 - bl)) & 1) << 1), res;
 +    const uint8_t *p = s->keyframe ? vp9_default_kf_partition_probs[bl][c] :
 +                                     s->prob.p.partition[bl][c];
 +    enum BlockPartition bp;
 +    ptrdiff_t hbs = 4 >> bl;
 +
 +    if (bl == BL_8X8) {
 +        bp = vp8_rac_get_tree(&s->c, vp9_partition_tree, p);
 +        res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp);
 +    } else if (col + hbs < s->cols) {
 +        if (row + hbs < s->rows) {
 +            bp = vp8_rac_get_tree(&s->c, vp9_partition_tree, p);
 +            switch (bp) {
 +            case PARTITION_NONE:
 +                res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp);
 +                break;
 +            case PARTITION_H:
 +                if (!(res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp))) {
 +                    yoff  += hbs * 8 * s->f->linesize[0];
 +                    uvoff += hbs * 4 * s->f->linesize[1];
 +                    res = decode_b(ctx, row + hbs, col, lflvl, yoff, uvoff, bl, bp);
 +                }
 +                break;
 +            case PARTITION_V:
 +                if (!(res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp))) {
 +                    yoff  += hbs * 8;
 +                    uvoff += hbs * 4;
 +                    res = decode_b(ctx, row, col + hbs, lflvl, yoff, uvoff, bl, bp);
 +                }
 +                break;
 +            case PARTITION_SPLIT:
 +                if (!(res = decode_sb(ctx, row, col, lflvl, yoff, uvoff, bl + 1))) {
 +                    if (!(res = decode_sb(ctx, row, col + hbs, lflvl,
 +                                          yoff + 8 * hbs, uvoff + 4 * hbs, bl + 1))) {
 +                        yoff  += hbs * 8 * s->f->linesize[0];
 +                        uvoff += hbs * 4 * s->f->linesize[1];
 +                        if (!(res = decode_sb(ctx, row + hbs, col, lflvl,
 +                                              yoff, uvoff, bl + 1)))
 +                            res = decode_sb(ctx, row + hbs, col + hbs, lflvl,
 +                                            yoff + 8 * hbs, uvoff + 4 * hbs, bl + 1);
 +                    }
 +                }
 +                break;
 +            }
 +        } else if (vp56_rac_get_prob_branchy(&s->c, p[1])) {
 +            bp = PARTITION_SPLIT;
 +            if (!(res = decode_sb(ctx, row, col, lflvl, yoff, uvoff, bl + 1)))
 +                res = decode_sb(ctx, row, col + hbs, lflvl,
 +                                yoff + 8 * hbs, uvoff + 4 * hbs, bl + 1);
 +        } else {
 +            bp = PARTITION_H;
 +            res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp);
 +        }
 +    } else if (row + hbs < s->rows) {
 +        if (vp56_rac_get_prob_branchy(&s->c, p[2])) {
 +            bp = PARTITION_SPLIT;
 +            if (!(res = decode_sb(ctx, row, col, lflvl, yoff, uvoff, bl + 1))) {
 +                yoff  += hbs * 8 * s->f->linesize[0];
 +                uvoff += hbs * 4 * s->f->linesize[1];
 +                res = decode_sb(ctx, row + hbs, col, lflvl,
 +                                yoff, uvoff, bl + 1);
 +            }
 +        } else {
 +            bp = PARTITION_V;
 +            res = decode_b(ctx, row, col, lflvl, yoff, uvoff, bl, bp);
 +        }
 +    } else {
 +        bp = PARTITION_SPLIT;
 +        res = decode_sb(ctx, row, col, lflvl, yoff, uvoff, bl + 1);
 +    }
 +    s->counts.partition[bl][c][bp]++;
 +
 +    return res;
 +}
 +
 +static void loopfilter_sb(AVCodecContext *ctx, struct VP9Filter *lflvl,
 +                          int row, int col, ptrdiff_t yoff, ptrdiff_t uvoff)
 +{
 +    VP9Context *s = ctx->priv_data;
 +    uint8_t *dst = s->f->data[0] + yoff, *lvl = lflvl->level;
 +    ptrdiff_t ls_y = s->f->linesize[0], ls_uv = s->f->linesize[1];
 +    int y, x, p;
 +
 +    // FIXME in how far can we interleave the v/h loopfilter calls? E.g.
 +    // if you think of them as acting on a 8x8 block max, we can interleave
 +    // each v/h within the single x loop, but that only works if we work on
 +    // 8 pixel blocks, and we won't always do that (we want at least 16px
 +    // to use SSE2 optimizations, perhaps 32 for AVX2)
 +
 +    // filter edges between columns, Y plane (e.g. block1 | block2)
 +    for (y = 0; y < 8; y += 2, dst += 16 * ls_y, lvl += 16) {
 +        uint8_t *ptr = dst, *l = lvl, *hmask1 = lflvl->mask[0][0][y];
 +        uint8_t *hmask2 = lflvl->mask[0][0][y + 1];
 +        unsigned hm1 = hmask1[0] | hmask1[1] | hmask1[2], hm13 = hmask1[3];
 +        unsigned hm2 = hmask2[1] | hmask2[2], hm23 = hmask2[3];
 +        unsigned hm = hm1 | hm2 | hm13 | hm23;
 +
 +        for (x = 1; hm & ~(x - 1); x <<= 1, ptr += 8, l++) {
 +            if (hm1 & x) {
 +                int L = *l, H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                if (col || x > 1) {
 +                    if (hmask1[0] & x) {
 +                        if (hmask2[0] & x) {
 +                            av_assert2(l[8] == L);
 +                            s->dsp.loop_filter_16[0](ptr, ls_y, E, I, H);
 +                        } else {
 +                            s->dsp.loop_filter_8[2][0](ptr, ls_y, E, I, H);
 +                        }
 +                    } else if (hm2 & x) {
 +                        L = l[8];
 +                        H |= (L >> 4) << 8;
 +                        E |= s->filter.mblim_lut[L] << 8;
 +                        I |= s->filter.lim_lut[L] << 8;
 +                        s->dsp.loop_filter_mix2[!!(hmask1[1] & x)]
 +                                               [!!(hmask2[1] & x)]
 +                                               [0](ptr, ls_y, E, I, H);
 +                    } else {
 +                        s->dsp.loop_filter_8[!!(hmask1[1] & x)]
 +                                            [0](ptr, ls_y, E, I, H);
 +                    }
 +                }
 +            } else if (hm2 & x) {
 +                int L = l[8], H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                if (col || x > 1) {
 +                    s->dsp.loop_filter_8[!!(hmask2[1] & x)]
 +                                        [0](ptr + 8 * ls_y, ls_y, E, I, H);
 +                }
 +            }
 +            if (hm13 & x) {
 +                int L = *l, H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                if (hm23 & x) {
 +                    L = l[8];
 +                    H |= (L >> 4) << 8;
 +                    E |= s->filter.mblim_lut[L] << 8;
 +                    I |= s->filter.lim_lut[L] << 8;
 +                    s->dsp.loop_filter_mix2[0][0][0](ptr + 4, ls_y, E, I, H);
 +                } else {
 +                    s->dsp.loop_filter_8[0][0](ptr + 4, ls_y, E, I, H);
 +                }
 +            } else if (hm23 & x) {
 +                int L = l[8], H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                s->dsp.loop_filter_8[0][0](ptr + 8 * ls_y + 4, ls_y, E, I, H);
 +            }
 +        }
 +    }
 +
 +    //                                          block1
 +    // filter edges between rows, Y plane (e.g. ------)
 +    //                                          block2
 +    dst = s->f->data[0] + yoff;
 +    lvl = lflvl->level;
 +    for (y = 0; y < 8; y++, dst += 8 * ls_y, lvl += 8) {
 +        uint8_t *ptr = dst, *l = lvl, *vmask = lflvl->mask[0][1][y];
 +        unsigned vm = vmask[0] | vmask[1] | vmask[2], vm3 = vmask[3];
 +
 +        for (x = 1; vm & ~(x - 1); x <<= 2, ptr += 16, l += 2) {
 +            if (row || y) {
 +                if (vm & x) {
 +                    int L = *l, H = L >> 4;
 +                    int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                    if (vmask[0] & x) {
 +                        if (vmask[0] & (x << 1)) {
 +                            av_assert2(l[1] == L);
 +                            s->dsp.loop_filter_16[1](ptr, ls_y, E, I, H);
 +                        } else {
 +                            s->dsp.loop_filter_8[2][1](ptr, ls_y, E, I, H);
 +                        }
 +                    } else if (vm & (x << 1)) {
 +                        L = l[1];
 +                        H |= (L >> 4) << 8;
 +                        E |= s->filter.mblim_lut[L] << 8;
 +                        I |= s->filter.lim_lut[L] << 8;
 +                        s->dsp.loop_filter_mix2[!!(vmask[1] &  x)]
 +                                               [!!(vmask[1] & (x << 1))]
 +                                               [1](ptr, ls_y, E, I, H);
 +                    } else {
 +                        s->dsp.loop_filter_8[!!(vmask[1] & x)]
 +                                            [1](ptr, ls_y, E, I, H);
 +                    }
 +                } else if (vm & (x << 1)) {
 +                    int L = l[1], H = L >> 4;
 +                    int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                    s->dsp.loop_filter_8[!!(vmask[1] & (x << 1))]
 +                                        [1](ptr + 8, ls_y, E, I, H);
 +                }
 +            }
 +            if (vm3 & x) {
 +                int L = *l, H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                if (vm3 & (x << 1)) {
 +                    L = l[1];
 +                    H |= (L >> 4) << 8;
 +                    E |= s->filter.mblim_lut[L] << 8;
 +                    I |= s->filter.lim_lut[L] << 8;
 +                    s->dsp.loop_filter_mix2[0][0][1](ptr + ls_y * 4, ls_y, E, I, H);
 +                } else {
 +                    s->dsp.loop_filter_8[0][1](ptr + ls_y * 4, ls_y, E, I, H);
 +                }
 +            } else if (vm3 & (x << 1)) {
 +                int L = l[1], H = L >> 4;
 +                int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                s->dsp.loop_filter_8[0][1](ptr + ls_y * 4 + 8, ls_y, E, I, H);
 +            }
 +        }
 +    }
 +
 +    // same principle but for U/V planes
 +    for (p = 0; p < 2; p++) {
 +        lvl = lflvl->level;
 +        dst = s->f->data[1 + p] + uvoff;
 +        for (y = 0; y < 8; y += 4, dst += 16 * ls_uv, lvl += 32) {
 +            uint8_t *ptr = dst, *l = lvl, *hmask1 = lflvl->mask[1][0][y];
 +            uint8_t *hmask2 = lflvl->mask[1][0][y + 2];
 +            unsigned hm1 = hmask1[0] | hmask1[1] | hmask1[2];
 +            unsigned hm2 = hmask2[1] | hmask2[2], hm = hm1 | hm2;
 +
 +            for (x = 1; hm & ~(x - 1); x <<= 1, ptr += 4) {
 +                if (col || x > 1) {
 +                    if (hm1 & x) {
 +                        int L = *l, H = L >> 4;
 +                        int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                        if (hmask1[0] & x) {
 +                            if (hmask2[0] & x) {
 +                                av_assert2(l[16] == L);
 +                                s->dsp.loop_filter_16[0](ptr, ls_uv, E, I, H);
 +                            } else {
 +                                s->dsp.loop_filter_8[2][0](ptr, ls_uv, E, I, H);
 +                            }
 +                        } else if (hm2 & x) {
 +                            L = l[16];
 +                            H |= (L >> 4) << 8;
 +                            E |= s->filter.mblim_lut[L] << 8;
 +                            I |= s->filter.lim_lut[L] << 8;
 +                            s->dsp.loop_filter_mix2[!!(hmask1[1] & x)]
 +                                                   [!!(hmask2[1] & x)]
 +                                                   [0](ptr, ls_uv, E, I, H);
 +                        } else {
 +                            s->dsp.loop_filter_8[!!(hmask1[1] & x)]
 +                                                [0](ptr, ls_uv, E, I, H);
 +                        }
 +                    } else if (hm2 & x) {
 +                        int L = l[16], H = L >> 4;
 +                        int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                        s->dsp.loop_filter_8[!!(hmask2[1] & x)]
 +                                            [0](ptr + 8 * ls_uv, ls_uv, E, I, H);
 +                    }
 +                }
 +                if (x & 0xAA)
 +                    l += 2;
 +            }
 +        }
 +        lvl = lflvl->level;
 +        dst = s->f->data[1 + p] + uvoff;
 +        for (y = 0; y < 8; y++, dst += 4 * ls_uv) {
 +            uint8_t *ptr = dst, *l = lvl, *vmask = lflvl->mask[1][1][y];
 +            unsigned vm = vmask[0] | vmask[1] | vmask[2];
 +
 +            for (x = 1; vm & ~(x - 1); x <<= 4, ptr += 16, l += 4) {
 +                if (row || y) {
 +                    if (vm & x) {
 +                        int L = *l, H = L >> 4;
 +                        int E = s->filter.mblim_lut[L], I = s->filter.lim_lut[L];
 +
 +                        if (vmask[0] & x) {
 +    &n