Commit 076ef3e745 for ffmpeg

commit 076ef3e745f961fc372611375857d79b6a17e1e7
Author: Rainbaby <r@bgr.jp>
Date:   Fri Aug 28 16:18:09 2026 +0900

    avcodec/cavsdec: decode the inter macroblock layer with AEC

    The mv_diff contexts make the neighbouring blocks' difference a
    predictor, so the differences are now cached. The three per picture
    type macroblock loops fold into one.

    Signed-off-by: Rainbaby <r@bgr.jp>

diff --git a/libavcodec/cavs.c b/libavcodec/cavs.c
index 0fdc9daca4..cd0e28bf20 100644
--- a/libavcodec/cavs.c
+++ b/libavcodec/cavs.c
@@ -57,7 +57,7 @@ static const uint8_t tc_tab[64] = {

 /** mark block as unavailable, i.e. out of picture
  *  or not yet decoded */
-static const cavs_vector un_mv = { 0, 0, 1, NOT_AVAIL };
+static const cavs_vector un_mv = { 0, 0, 1, NOT_AVAIL, 0 };

 /**
  * Inverse block scan method 2 (clause 9.5.3 c), figure 23), for field
@@ -594,6 +594,9 @@ void ff_cavs_mv(AVSContext *h, enum cavs_mv_loc nP, enum cavs_mv_loc nC,

     mvP->ref  = ref;
     mvP->dist = h->dist[mvP->ref];
+    /* a derived reference has index 0 (clause 9.4.5) */
+    if (mode >= MV_PRED_PSKIP)
+        mvP->ref_idx = 0;
     if (mvC->ref == NOT_AVAIL || (nP == MV_FWD_X3) || (nP == MV_BWD_X3 ))
         mvC = &h->mv[nP - 5];  // set to top-left (mvD)
     if (mode == MV_PRED_PSKIP &&
@@ -623,8 +626,20 @@ void ff_cavs_mv(AVSContext *h, enum cavs_mv_loc nP, enum cavs_mv_loc nC,
         mv_pred_median(h, mvP, mvA, mvB, mvC);

     if (mode < MV_PRED_PSKIP) {
-        int mx = get_se_golomb(&h->gb) + (unsigned)mvP->x;
-        int my = get_se_golomb(&h->gb) + (unsigned)mvP->y;
+        int dx, dy, mx, my;
+
+        /* under AEC mv_diff is already in the cache (aec_mv() in cavsdec.c) */
+        if (h->aec_enable) {
+            dx = h->mvd[nP][0];
+            dy = h->mvd[nP][1];
+        } else {
+            dx = get_se_golomb(&h->gb);
+            dy = get_se_golomb(&h->gb);
+            h->mvd[nP][0] = dx;
+            h->mvd[nP][1] = dy;
+        }
+        mx = dx + (unsigned)mvP->x;
+        my = dy + (unsigned)mvP->y;

         if (mx != (int16_t)mx || my != (int16_t)my) {
             av_log(h->avctx, AV_LOG_ERROR, "MV %d %d out of supported range\n", mx, my);
@@ -632,6 +647,7 @@ void ff_cavs_mv(AVSContext *h, enum cavs_mv_loc nP, enum cavs_mv_loc nC,
             mvP->x = mx;
             mvP->y = my;
         }
+        set_mvds(&h->mvd[nP], size);
     }
     set_mvs(mvP, size);
 }
@@ -649,6 +665,11 @@ void ff_cavs_init_mb(AVSContext *h)
 {
     int i;

+    /* a macroblock has no mv_diff until one is decoded for it */
+    memset(h->mvd[MV_FWD_X0], 0, 2 * sizeof(*h->mvd));
+    memset(h->mvd[MV_FWD_X2], 0, 2 * sizeof(*h->mvd));
+    memset(h->mvd[MV_BWD_X0], 0, 2 * sizeof(*h->mvd));
+    memset(h->mvd[MV_BWD_X2], 0, 2 * sizeof(*h->mvd));
     /* copy predictors from top line (MB B and C) into cache */
     for (i = 0; i < 3; i++) {
         h->mv[MV_FWD_B2 + i] = h->top_mv[0][h->mbx * 2 + i];
@@ -695,8 +716,10 @@ int ff_cavs_next_mb(AVSContext *h)
     h->cu    += 8;
     h->cv    += 8;
     /* copy mvs as predictors to the left */
-    for (i = 0; i <= 20; i += 4)
+    for (i = 0; i <= 20; i += 4) {
         h->mv[i] = h->mv[i + 2];
+        memcpy(h->mvd[i], h->mvd[i + 2], sizeof(*h->mvd));
+    }
     /* copy bottom mvs from cache to top line */
     h->top_mv[0][h->mbx * 2 + 0] = h->mv[MV_FWD_X2];
     h->top_mv[0][h->mbx * 2 + 1] = h->mv[MV_FWD_X3];
@@ -710,8 +733,10 @@ int ff_cavs_next_mb(AVSContext *h)
         /* clear left pred_modes */
         h->pred_mode_Y[3] = h->pred_mode_Y[6] = NOT_AVAIL;
         /* clear left mv predictors */
-        for (i = 0; i <= 20; i += 4)
+        for (i = 0; i <= 20; i += 4) {
             h->mv[i] = un_mv;
+            memset(h->mvd[i], 0, sizeof(*h->mvd));
+        }
         h->mbx = 0;
         h->mby++;
         /* re-calculate sample pointers */
@@ -736,8 +761,10 @@ int ff_cavs_init_pic(AVSContext *h)
     int i;

     /* clear some predictors */
-    for (i = 0; i <= 20; i += 4)
+    for (i = 0; i <= 20; i += 4) {
         h->mv[i] = un_mv;
+        memset(h->mvd[i], 0, sizeof(*h->mvd));
+    }
     h->mv[MV_BWD_X0] = CAVS_DIR_MV;
     set_mvs(&h->mv[MV_BWD_X0], BLK_16X16);
     h->mv[MV_FWD_X0] = CAVS_DIR_MV;
@@ -784,15 +811,17 @@ int ff_cavs_init_top_lines(AVSContext *h)
     h->col_type_base = av_mallocz(h->mb_width * h->mb_height);

     h->top_cbp         = av_mallocz(h->mb_width);
+    h->top_mb_type     = av_mallocz(h->mb_width);
     h->top_c_pred_mode = av_mallocz(h->mb_width);

     if (!h->top_qp || !h->top_mv[0] || !h->top_mv[1] || !h->top_pred_Y ||
-        !h->top_cbp || !h->top_c_pred_mode ||
+        !h->top_cbp || !h->top_c_pred_mode || !h->top_mb_type ||
         !h->top_border_y || !h->top_border_u || !h->top_border_v ||
         !h->col_mv || !h->col_type_base) {
         av_freep(&h->top_qp);
         av_freep(&h->top_cbp);
         av_freep(&h->top_c_pred_mode);
+        av_freep(&h->top_mb_type);
         av_freep(&h->top_mv[0]);
         av_freep(&h->top_mv[1]);
         av_freep(&h->top_pred_Y);
@@ -865,6 +894,7 @@ av_cold int ff_cavs_end(AVCodecContext *avctx)
     av_freep(&h->top_qp);
     av_freep(&h->top_cbp);
     av_freep(&h->top_c_pred_mode);
+    av_freep(&h->top_mb_type);
     av_freep(&h->top_mv[0]);
     av_freep(&h->top_mv[1]);
     av_freep(&h->top_pred_Y);
diff --git a/libavcodec/cavs.h b/libavcodec/cavs.h
index d111714879..802971914e 100644
--- a/libavcodec/cavs.h
+++ b/libavcodec/cavs.h
@@ -24,6 +24,7 @@

 #include <stddef.h>
 #include <stdint.h>
+#include <string.h>

 #include "libavutil/attributes.h"
 #include "libavutil/frame.h"
@@ -153,6 +154,11 @@ typedef struct cavs_vector {
     int16_t y;
     int16_t dist;
     int16_t ref;
+    /**
+     * mb_reference_index of the block, 0 when it codes none (clause 9.4.5);
+     * ref above is where the reference list puts it
+     */
+    int16_t ref_idx;
 } cavs_vector;

 struct dec_2dvlc {
@@ -193,6 +199,8 @@ typedef struct AVSContext {
     int prev_delta_qp;    ///< PreviousDeltaQP of clause 9.4.8, an AEC context
     int left_cbp;         ///< MbCBP of the macroblock to the left
     uint8_t *top_cbp;     ///< MbCBP of the macroblock above, one per column
+    int left_mb_type;          ///< MbType to the left, clause 8.4.4.2 b)
+    uint8_t *top_mb_type;      ///< MbType above, one per column
     int left_c_pred_mode;      ///< IntraChromaPredMode to the left, 8.4.4.2 e)
     uint8_t *top_c_pred_mode;  ///< IntraChromaPredMode above, per column
     int loop_filter_disable;
@@ -219,6 +227,8 @@ typedef struct AVSContext {

        the same is repeated for backward motion vectors */
     DECLARE_ALIGNED(8, cavs_vector, mv)[2*4*3];
+    /** mv_diff of the same blocks, for the ctxIdxInc of clause 8.4.4.2 g) */
+    int16_t mvd[2*4*3][2];
     cavs_vector *top_mv[2];
     cavs_vector *col_mv;

@@ -305,6 +315,23 @@ static inline void set_mvs(cavs_vector *mv, enum cavs_block size) {
     }
 }

+/** replicate one partition's mv_diff over the 8x8 blocks it covers */
+static inline void set_mvds(int16_t (*mvd)[2], enum cavs_block size)
+{
+    switch (size) {
+    case BLK_16X16:
+        memcpy(mvd[MV_STRIDE  ], mvd[0], sizeof(*mvd));
+        memcpy(mvd[MV_STRIDE+1], mvd[0], sizeof(*mvd));
+        av_fallthrough;
+    case BLK_16X8:
+        memcpy(mvd[1], mvd[0], sizeof(*mvd));
+        break;
+    case BLK_8X16:
+        memcpy(mvd[MV_STRIDE], mvd[0], sizeof(*mvd));
+        break;
+    }
+}
+
 void ff_cavs_filter(AVSContext *h, enum cavs_mb mb_type);
 void ff_cavs_load_intra_pred_luma(AVSContext *h, uint8_t *top, uint8_t **left,
                                   int block);
diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c
index 4d1f1c9275..108c611da4 100644
--- a/libavcodec/cavsdec.c
+++ b/libavcodec/cavsdec.c
@@ -636,6 +636,148 @@ static int decode_residual_block(AVSContext *h, GetBitContext *gb,
 /** Table 52, priIdx as a function of lMax, which saturates at 5. */
 static const uint8_t cavs_aec_pri_idx[6] = { 0, 1, 2, 3, 3, 4 };

+/** Decode mb_skip_run, table 44 unary (8.4.4.2 a)). @return SkipMbCount */
+static int aec_mb_skip_run(AVSContext *h)
+{
+    int v = 0;
+
+    while (!ff_cavs_aec_decode_decision(&h->aec,
+                                        AEC_MB_SKIP_RUN + FFMIN(v, 3))) {
+        if (++v > h->mb_width * h->mb_height) {
+            av_log(h->avctx, AV_LOG_ERROR, "mb_skip_run out of range\n");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    return v;
+}
+
+/** Decode mb_type, table 44 unary mapped by tables 54 and 55 (9.4.2 a) 2)) */
+static int aec_mb_type_p(AVSContext *h)
+{
+    int v = 0;
+
+    while (!ff_cavs_aec_decode_decision(&h->aec, AEC_MB_TYPE + FFMIN(v, 4))) {
+        if (++v > 5 - h->skip_mode_flag) {
+            av_log(h->avctx, AV_LOG_ERROR, "mb_type out of range\n");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    return v ? v - 1 + h->skip_mode_flag : 5;
+}
+
+/** Decode mb_type in a B picture (table 45, 8.4.4.2 b)); see table 56. */
+static int aec_mb_type_b(AVSContext *h)
+{
+    int a = (h->flags & A_AVAIL) && h->left_mb_type        != B_SKIP &&
+                                    h->left_mb_type        != B_DIRECT;
+    int b = (h->flags & B_AVAIL) && h->top_mb_type[h->mbx] != B_SKIP &&
+                                    h->top_mb_type[h->mbx] != B_DIRECT;
+    int v;
+
+    if (!ff_cavs_aec_decode_decision(&h->aec, AEC_MB_TYPE + 5 + a + b))
+        return h->skip_mode_flag;
+    for (v = 1;
+         !ff_cavs_aec_decode_decision(&h->aec, AEC_MB_TYPE + 7 + FFMIN(v, 7));
+         v++) {
+        if (v >= 24 - h->skip_mode_flag) {
+            av_log(h->avctx, AV_LOG_ERROR, "mb_type out of range\n");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    return v + h->skip_mode_flag;
+}
+
+/**
+ * Decode mb_part_type (tables 46 and 57, 8.4.4.2 c)).
+ * @return MbPartType, 0 to 3
+ */
+static int aec_mb_part_type(AVSContext *h)
+{
+    int b0 = ff_cavs_aec_decode_decision(&h->aec, AEC_MB_PART_TYPE);
+    int b1 = ff_cavs_aec_decode_decision(&h->aec,
+                                         AEC_MB_PART_TYPE + (b0 ? 2 : 1));
+
+    return (b0 << 1) | b1;
+}
+
+/** Decode mb_reference_index, table 44 unary (8.4.3 j), 8.4.4.2 f)). */
+static int aec_ref_index(AVSContext *h, int ctx_inc, int max)
+{
+    if (ff_cavs_aec_decode_decision(&h->aec,
+                                    AEC_MB_REFERENCE_INDEX + ctx_inc))
+        return 0;
+    if (h->cur.f->pict_type == AV_PICTURE_TYPE_B)
+        return 1;
+    if (ff_cavs_aec_decode_decision(&h->aec, AEC_MB_REFERENCE_INDEX + 4))
+        return 1;
+    for (int v = 2; v <= max; v++)
+        if (ff_cavs_aec_decode_decision(&h->aec, AEC_MB_REFERENCE_INDEX + 5))
+            return v;
+    av_log(h->avctx, AV_LOG_ERROR, "mb_reference_index out of range\n");
+    return AVERROR_INVALIDDATA;
+}
+
+/**
+ * Decode one mv_diff_x or mv_diff_y (table 49, 8.4.4.2 g)); mvda is the
+ * magnitude of the same component of the left block's mv_diff.
+ */
+static int aec_mv_diff(AVSContext *h, int base, int mvda, int *diff)
+{
+    int v;
+
+    if (!ff_cavs_aec_decode_decision(&h->aec,
+                                     base + (mvda < 2 ? 0 :
+                                             mvda < 16 ? 1 : 2))) {
+        *diff = 0;                  /* mvdSign is not coded for mvdAbs 0 */
+        return 0;
+    }
+    if (!ff_cavs_aec_decode_decision(&h->aec, base + 3)) {
+        v = 1;
+    } else if (!ff_cavs_aec_decode_decision(&h->aec, base + 4)) {
+        v = 2;
+    } else {
+        int even = ff_cavs_aec_decode_decision(&h->aec, base + 5);
+        int k    = 0;
+
+        while (!ff_cavs_aec_decode_bypass(&h->aec)) {
+            if (++k > 11) {
+                av_log(h->avctx, AV_LOG_ERROR, "mv_diff out of range\n");
+                return AVERROR_INVALIDDATA;
+            }
+        }
+        v = (1 << k) - 1;
+        while (k--)
+            v += ff_cavs_aec_decode_bypass(&h->aec) << k;
+        v = 3 + 2 * v + even;
+    }
+    if (ff_cavs_aec_decode_bypass(&h->aec))     /* mvdSign */
+        v = -v;
+    if (v < -4096 || v > 4095) {
+        av_log(h->avctx, AV_LOG_ERROR, "mv_diff %d out of range\n", v);
+        return AVERROR_INVALIDDATA;
+    }
+    *diff = v;
+    return 0;
+}
+
+/**
+ * Decode a motion vector's mv_diff pair into the cache that
+ * ff_cavs_mv() reads and the next block takes its mvda from.
+ */
+static int aec_mv(AVSContext *h, enum cavs_mv_loc nP)
+{
+    for (int i = 0; i < 2; i++) {
+        int base = i ? AEC_MV_DIFF_Y : AEC_MV_DIFF_X;
+        int diff;
+        int ret = aec_mv_diff(h, base, FFABS(h->mvd[nP - 1][i]), &diff);
+
+        if (ret < 0)
+            return ret;
+        h->mvd[nP][i] = diff;
+    }
+    return 0;
+}
+
 /**
  * Decode intra_luma_pred_mode (table 47) and derive IntraLumaPredMode
  * from predIntraPredMode per clause 9.4.4.2 a).
@@ -953,22 +1095,24 @@ static inline int decode_residual_inter(AVSContext *h)
 {
     int block, ret;

-    /* get coded block pattern */
-    int cbp = get_ue_golomb(&h->gb);
-    if (cbp > 63U) {
-        av_log(h->avctx, AV_LOG_ERROR, "illegal inter cbp %d\n", cbp);
-        return AVERROR_INVALIDDATA;
+    if (h->aec_enable) {
+        h->cbp = aec_cbp(h);
+    } else {
+        /* get coded block pattern */
+        int cbp = get_ue_golomb(&h->gb);
+        if (cbp > 63U) {
+            av_log(h->avctx, AV_LOG_ERROR, "illegal inter cbp %d\n", cbp);
+            return AVERROR_INVALIDDATA;
+        }
+        h->cbp = cbp_tab[cbp][1];
     }
-    h->cbp = cbp_tab[cbp][1];
-
-    /* get quantizer */
-    if (h->cbp && !h->qp_fixed)
-        h->qp = (h->qp + (unsigned)get_se_golomb(&h->gb)) & 63;
+    ret = decode_mb_qp_delta(h);
+    if (ret < 0)
+        return ret;
     for (block = 0; block < 4; block++)
         if (h->cbp & (1 << block)) {
-            ret = decode_residual_block(h, &h->gb, inter_dec, 0, h->qp,
-                                        h->cy + h->luma_scan[block],
-                                        h->l_stride);
+            ret = decode_residual(h, inter_dec, 0, 0, h->qp,
+                                  h->cy + h->luma_scan[block], h->l_stride);
             if (ret < 0)
                 return ret;
         }
@@ -1003,6 +1147,8 @@ static int decode_mb_i(AVSContext *h, int cbp_code)
     int ret;

     ff_cavs_init_mb(h);
+    h->left_mb_type        = I_8X8;
+    h->top_mb_type[h->mbx] = I_8X8;

     /* get intra prediction modes from stream */
     for (block = 0; block < 4; block++) {
@@ -1089,42 +1235,114 @@ static inline void set_intra_mode_default(AVSContext *h)
     }
 }

+/**
+ * Read the mb_reference_index of a P macroblock's block (table 26,
+ * clauses 7.2.5 and 9.4.5). @return the reference index it selects
+ */
+static int decode_ref_index(AVSContext *h, enum cavs_mv_loc nP)
+{
+    int idx = 0;
+
+    if (!h->ref_flag) {
+        if (h->aec_enable) {
+            /* a and b of 8.4.4.2 f); unavailable, intra and derived
+             * neighbours carry index 0 */
+            int a = h->mv[nP - 1].ref_idx > 0;
+            int b = h->mv[nP - 4].ref_idx > 0;
+
+            idx = aec_ref_index(h, a + 2 * b, 1);
+            if (idx < 0)
+                return idx;
+        } else {
+            idx = get_bits1(&h->gb);
+        }
+    }
+    h->mv[nP].ref     = idx;
+    h->mv[nP].ref_idx = idx;
+    return idx;
+}
+
+/**
+ * Decode one motion vector: under AEC its mv_diff must be read before
+ * ff_cavs_mv() overwrites the cache its context comes from.
+ */
+static int decode_mv(AVSContext *h, enum cavs_mv_loc nP, enum cavs_mv_loc nC,
+                     enum cavs_mv_pred mode, enum cavs_block size, int ref)
+{
+    if (h->aec_enable && mode < MV_PRED_PSKIP) {
+        int ret = aec_mv(h, nP);
+        if (ret < 0)
+            return ret;
+    }
+    ff_cavs_mv(h, nP, nC, mode, size, ref);
+    return 0;
+}
+
+/* what the AEC contexts of later macroblocks read about this one (8.4.4.2) */
+static void set_inter_mb_ctx(AVSContext *h, enum cavs_mb mb_type, int skip)
+{
+    h->left_mb_type            = mb_type;
+    h->top_mb_type[h->mbx]     = mb_type;
+    h->left_c_pred_mode        = INTRA_C_LP;
+    h->top_c_pred_mode[h->mbx] = INTRA_C_LP;
+    if (skip) {
+        h->left_cbp        = 0;
+        h->top_cbp[h->mbx] = 0;
+        h->prev_delta_qp   = 0;
+    }
+}
+
+#define MV(nP, nC, mode, size, ref)                             \
+    do {                                                        \
+        ret = decode_mv(h, nP, nC, mode, size, ref);            \
+        if (ret < 0)                                            \
+            return ret;                                         \
+    } while (0)
+
+#define REF(dst, nP)                                            \
+    do {                                                        \
+        ret = decode_ref_index(h, nP);                          \
+        if (ret < 0)                                            \
+            return ret;                                         \
+        dst = ret;                                              \
+    } while (0)
+
 static int decode_mb_p(AVSContext *h, enum cavs_mb mb_type)
 {
-    GetBitContext *gb = &h->gb;
     int ref[4];
     int ret;

     ff_cavs_init_mb(h);
+    set_inter_mb_ctx(h, mb_type, mb_type == P_SKIP);
     switch (mb_type) {
     case P_SKIP:
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_PSKIP,  BLK_16X16, 0);
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_PSKIP,  BLK_16X16, 0);
         break;
     case P_16X16:
-        ref[0] = h->ref_flag ? 0 : get_bits1(gb);
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, ref[0]);
+        REF(ref[0], MV_FWD_X0);
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, ref[0]);
         break;
     case P_16X8:
-        ref[0] = h->ref_flag ? 0 : get_bits1(gb);
-        ref[2] = h->ref_flag ? 0 : get_bits1(gb);
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_TOP,    BLK_16X8, ref[0]);
-        ff_cavs_mv(h, MV_FWD_X2, MV_FWD_A1, MV_PRED_LEFT,   BLK_16X8, ref[2]);
+        REF(ref[0], MV_FWD_X0);
+        REF(ref[2], MV_FWD_X2);
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_TOP,    BLK_16X8, ref[0]);
+        MV(MV_FWD_X2, MV_FWD_A1, MV_PRED_LEFT,   BLK_16X8, ref[2]);
         break;
     case P_8X16:
-        ref[0] = h->ref_flag ? 0 : get_bits1(gb);
-        ref[1] = h->ref_flag ? 0 : get_bits1(gb);
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_B3, MV_PRED_LEFT,     BLK_8X16, ref[0]);
-        ff_cavs_mv(h, MV_FWD_X1, MV_FWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, ref[1]);
+        REF(ref[0], MV_FWD_X0);
+        REF(ref[1], MV_FWD_X1);
+        MV(MV_FWD_X0, MV_FWD_B3, MV_PRED_LEFT,     BLK_8X16, ref[0]);
+        MV(MV_FWD_X1, MV_FWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, ref[1]);
         break;
     case P_8X8:
-        ref[0] = h->ref_flag ? 0 : get_bits1(gb);
-        ref[1] = h->ref_flag ? 0 : get_bits1(gb);
-        ref[2] = h->ref_flag ? 0 : get_bits1(gb);
-        ref[3] = h->ref_flag ? 0 : get_bits1(gb);
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_B3, MV_PRED_MEDIAN,   BLK_8X8, ref[0]);
-        ff_cavs_mv(h, MV_FWD_X1, MV_FWD_C2, MV_PRED_MEDIAN,   BLK_8X8, ref[1]);
-        ff_cavs_mv(h, MV_FWD_X2, MV_FWD_X1, MV_PRED_MEDIAN,   BLK_8X8, ref[2]);
-        ff_cavs_mv(h, MV_FWD_X3, MV_FWD_X0, MV_PRED_MEDIAN,   BLK_8X8, ref[3]);
+        REF(ref[0], MV_FWD_X0);
+        REF(ref[1], MV_FWD_X1);
+        REF(ref[2], MV_FWD_X2);
+        REF(ref[3], MV_FWD_X3);
+        MV(MV_FWD_X0, MV_FWD_B3, MV_PRED_MEDIAN,   BLK_8X8, ref[0]);
+        MV(MV_FWD_X1, MV_FWD_C2, MV_PRED_MEDIAN,   BLK_8X8, ref[1]);
+        MV(MV_FWD_X2, MV_FWD_X1, MV_PRED_MEDIAN,   BLK_8X8, ref[2]);
+        MV(MV_FWD_X3, MV_FWD_X0, MV_PRED_MEDIAN,   BLK_8X8, ref[3]);
         break;
     default:
         av_log(h->avctx, AV_LOG_ERROR, "Invalid mb_type %d in P frame\n",
@@ -1141,7 +1359,6 @@ static int decode_mb_p(AVSContext *h, enum cavs_mb mb_type)
     }
     ff_cavs_filter(h, mb_type);
     h->col_type_base[h->mbidx] = mb_type;
-
     return 0;
 }

@@ -1153,6 +1370,7 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
     int ret;

     ff_cavs_init_mb(h);
+    set_inter_mb_ctx(h, mb_type, mb_type == B_SKIP);

     /* reset all MVs */
     h->mv[MV_FWD_X0] = CAVS_DIR_MV;
@@ -1173,20 +1391,24 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
                                &h->col_mv[h->mbidx * 4 + block]);
         break;
     case B_FWD_16X16:
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, 1);
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, 1);
         break;
     case B_SYM_16X16:
-        ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, 1);
+        MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_MEDIAN, BLK_16X16, 1);
         mv_pred_sym(h, &h->mv[MV_FWD_X0], BLK_16X16);
         break;
     case B_BWD_16X16:
-        ff_cavs_mv(h, MV_BWD_X0, MV_BWD_C2, MV_PRED_MEDIAN, BLK_16X16, 0);
+        MV(MV_BWD_X0, MV_BWD_C2, MV_PRED_MEDIAN, BLK_16X16, 0);
         break;
     case B_8X8:
 #define TMP_UNUSED_INX  7
         flags = 0;
-        for (block = 0; block < 4; block++)
-            sub_type[block] = get_bits(&h->gb, 2);
+        for (block = 0; block < 4; block++) {
+            if (h->aec_enable)
+                sub_type[block] = aec_mb_part_type(h);
+            else
+                sub_type[block] = get_bits(&h->gb, 2);
+        }
         for (block = 0; block < 4; block++) {
             switch (sub_type[block]) {
             case B_SUB_DIRECT:
@@ -1221,12 +1443,12 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
                                    &h->col_mv[h->mbidx * 4 + block]);
                 break;
             case B_SUB_FWD:
-                ff_cavs_mv(h, mv_scan[block], mv_scan[block] - 3,
-                           MV_PRED_MEDIAN, BLK_8X8, 1);
+                MV(mv_scan[block], mv_scan[block] - 3,
+                   MV_PRED_MEDIAN, BLK_8X8, 1);
                 break;
             case B_SUB_SYM:
-                ff_cavs_mv(h, mv_scan[block], mv_scan[block] - 3,
-                           MV_PRED_MEDIAN, BLK_8X8, 1);
+                MV(mv_scan[block], mv_scan[block] - 3,
+                   MV_PRED_MEDIAN, BLK_8X8, 1);
                 mv_pred_sym(h, &h->mv[mv_scan[block]], BLK_8X8);
                 break;
             }
@@ -1234,9 +1456,9 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
 #undef TMP_UNUSED_INX
         for (block = 0; block < 4; block++) {
             if (sub_type[block] == B_SUB_BWD)
-                ff_cavs_mv(h, mv_scan[block] + MV_BWD_OFFS,
-                           mv_scan[block] + MV_BWD_OFFS - 3,
-                           MV_PRED_MEDIAN, BLK_8X8, 0);
+                MV(mv_scan[block] + MV_BWD_OFFS,
+                   mv_scan[block] + MV_BWD_OFFS - 3,
+                   MV_PRED_MEDIAN, BLK_8X8, 0);
         }
         break;
     default:
@@ -1248,30 +1470,30 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
         flags = ff_cavs_partition_flags[mb_type];
         if (mb_type & 1) { /* 16x8 macroblock types */
             if (flags & FWD0)
-                ff_cavs_mv(h, MV_FWD_X0, MV_FWD_C2, MV_PRED_TOP,  BLK_16X8, 1);
+                MV(MV_FWD_X0, MV_FWD_C2, MV_PRED_TOP,  BLK_16X8, 1);
             if (flags & SYM0)
                 mv_pred_sym(h, &h->mv[MV_FWD_X0], BLK_16X8);
             if (flags & FWD1)
-                ff_cavs_mv(h, MV_FWD_X2, MV_FWD_A1, MV_PRED_LEFT, BLK_16X8, 1);
+                MV(MV_FWD_X2, MV_FWD_A1, MV_PRED_LEFT, BLK_16X8, 1);
             if (flags & SYM1)
                 mv_pred_sym(h, &h->mv[MV_FWD_X2], BLK_16X8);
             if (flags & BWD0)
-                ff_cavs_mv(h, MV_BWD_X0, MV_BWD_C2, MV_PRED_TOP,  BLK_16X8, 0);
+                MV(MV_BWD_X0, MV_BWD_C2, MV_PRED_TOP,  BLK_16X8, 0);
             if (flags & BWD1)
-                ff_cavs_mv(h, MV_BWD_X2, MV_BWD_A1, MV_PRED_LEFT, BLK_16X8, 0);
+                MV(MV_BWD_X2, MV_BWD_A1, MV_PRED_LEFT, BLK_16X8, 0);
         } else {          /* 8x16 macroblock types */
             if (flags & FWD0)
-                ff_cavs_mv(h, MV_FWD_X0, MV_FWD_B3, MV_PRED_LEFT, BLK_8X16, 1);
+                MV(MV_FWD_X0, MV_FWD_B3, MV_PRED_LEFT, BLK_8X16, 1);
             if (flags & SYM0)
                 mv_pred_sym(h, &h->mv[MV_FWD_X0], BLK_8X16);
             if (flags & FWD1)
-                ff_cavs_mv(h, MV_FWD_X1, MV_FWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, 1);
+                MV(MV_FWD_X1, MV_FWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, 1);
             if (flags & SYM1)
                 mv_pred_sym(h, &h->mv[MV_FWD_X1], BLK_8X16);
             if (flags & BWD0)
-                ff_cavs_mv(h, MV_BWD_X0, MV_BWD_B3, MV_PRED_LEFT, BLK_8X16, 0);
+                MV(MV_BWD_X0, MV_BWD_B3, MV_PRED_LEFT, BLK_8X16, 0);
             if (flags & BWD1)
-                ff_cavs_mv(h, MV_BWD_X1, MV_BWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, 0);
+                MV(MV_BWD_X1, MV_BWD_C2, MV_PRED_TOPRIGHT, BLK_8X16, 0);
         }
     }
     ff_cavs_inter(h, mb_type);
@@ -1286,6 +1508,59 @@ static int decode_mb_b(AVSContext *h, enum cavs_mb mb_type)
     return 0;
 }

+#undef MV
+#undef REF
+
+/**
+ * Decode one macroblock of a P or B coded macroblock layer, table 25.
+ * @param skip_count what is left of the current mb_skip_run, or -1 when the
+ *                   next macroblock position starts a new one
+ */
+static int decode_mb_pb_layer(AVSContext *h, int *skip_count)
+{
+    const int b = h->cur.f->pict_type == AV_PICTURE_TYPE_B;
+    const enum cavs_mb skip = b ? B_SKIP : P_SKIP;
+    const enum cavs_mb last = b ? B_8X8  : P_8X8;
+    enum cavs_mb mb_type;
+    int skipped, ret;
+
+    if (h->skip_mode_flag && *skip_count < 0) {
+        if (h->aec_enable) {
+            *skip_count = aec_mb_skip_run(h);
+        } else {
+            if (get_bits_left(&h->gb) < 1)
+                return AVERROR_INVALIDDATA;
+            *skip_count = get_ue_golomb(&h->gb);
+        }
+        if (*skip_count < 0)
+            return AVERROR_INVALIDDATA;
+    }
+    skipped = h->skip_mode_flag && (*skip_count)--;
+    if (skipped) {
+        mb_type = skip;
+    } else if (h->aec_enable) {
+        ret = b ? aec_mb_type_b(h) : aec_mb_type_p(h);
+        if (ret < 0)
+            return ret;
+        mb_type = ret + skip;
+    } else {
+        if (get_bits_left(&h->gb) < 1)
+            return AVERROR_INVALIDDATA;
+        mb_type = get_ue_golomb(&h->gb) + skip + h->skip_mode_flag;
+    }
+    if (mb_type > last)
+        ret = decode_mb_i(h, mb_type - last - 1);
+    else
+        ret = b ? decode_mb_b(h, mb_type) : decode_mb_p(h, mb_type);
+    if (ret < 0)
+        return ret;
+    /* the stuffing bit follows a coded MB and the last one of a skip run */
+    if (h->aec_enable && (!skipped || !*skip_count) &&
+        ff_cavs_aec_decode_stuffing_bit(&h->aec))
+        aec_end_of_slice(h);
+    return 0;
+}
+
 /*****************************************************************************
  *
  * slice level
@@ -1380,7 +1655,6 @@ static int decode_pic(AVSContext *h)
 {
     int ret;
     int skip_count    = -1;
-    enum cavs_mb mb_type;

     if (!h->top_qp) {
         av_log(h->avctx, AV_LOG_ERROR, "No sequence header decoded yet\n");
@@ -1508,112 +1782,37 @@ static int decode_pic(AVSContext *h)
     ret = 0;
     h->aec.gb = NULL;   /* started by the first slice header */
     h->pic_data_end = 0;
-    if (h->cur.f->pict_type == AV_PICTURE_TYPE_I) {
-        do {
-            ret = check_for_slice(h);
-            if (ret < 0)
-                break;
-            if (h->aec_enable && !h->aec.gb) {
-                av_log(h->avctx, AV_LOG_ERROR,
-                       "AEC picture without a slice header\n");
-                ret = AVERROR_INVALIDDATA;
-                break;
-            }
-            if (h->pic_data_end) {
-                av_log(h->avctx, AV_LOG_ERROR,
-                       "picture data ended before MB %d\n", h->mbidx);
-                ret = AVERROR_INVALIDDATA;
-                break;
-            }
+    do {
+        ret = check_for_slice(h);
+        if (ret < 0)
+            break;
+        if (ret)
+            skip_count = -1;
+        if (h->aec_enable && !h->aec.gb) {
+            av_log(h->avctx, AV_LOG_ERROR,
+                   "AEC picture without a slice header\n");
+            ret = AVERROR_INVALIDDATA;
+            break;
+        }
+        /* past the picture's data, only a pending skip run can code MBs */
+        if (h->pic_data_end && !(h->skip_mode_flag && skip_count > 0)) {
+            av_log(h->avctx, AV_LOG_ERROR, "picture data ended before MB %d\n",
+                   h->mbidx);
+            ret = AVERROR_INVALIDDATA;
+            break;
+        }
+        if (h->cur.f->pict_type != AV_PICTURE_TYPE_I) {
+            ret = decode_mb_pb_layer(h, &skip_count);
+        } else {
             ret = decode_mb_i(h, 0);
-            if (ret < 0)
-                break;
-            if (h->aec_enable && ff_cavs_aec_decode_stuffing_bit(&h->aec))
+            /* aec_mb_stuffing_bit is 1 on the slice's last MB (7.2.4) */
+            if (ret >= 0 && h->aec_enable &&
+                ff_cavs_aec_decode_stuffing_bit(&h->aec))
                 aec_end_of_slice(h);
-        } while (ff_cavs_next_mb(h));
-    } else if (h->cur.f->pict_type == AV_PICTURE_TYPE_P) {
-        do {
-            ret = check_for_slice(h);
-            if (ret < 0)
-                break;
-            if (ret)
-                skip_count = -1;
-            /* past the picture's data, only a pending skip run can code MBs */
-            if (h->pic_data_end && !(h->skip_mode_flag && skip_count > 0)) {
-                av_log(h->avctx, AV_LOG_ERROR,
-                       "picture data ended before MB %d\n", h->mbidx);
-                ret = AVERROR_INVALIDDATA;
-                break;
-            }
-            if (h->skip_mode_flag && (skip_count < 0)) {
-                if (get_bits_left(&h->gb) < 1) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-                skip_count = get_ue_golomb(&h->gb);
-                if (skip_count < 0) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-            }
-            if (h->skip_mode_flag && skip_count--) {
-                ret = decode_mb_p(h, P_SKIP);
-            } else {
-                if (get_bits_left(&h->gb) < 1) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-                mb_type = get_ue_golomb(&h->gb) + P_SKIP + h->skip_mode_flag;
-                if (mb_type > P_8X8)
-                    ret = decode_mb_i(h, mb_type - P_8X8 - 1);
-                else
-                    ret = decode_mb_p(h, mb_type);
-            }
-            if (ret < 0)
-                break;
-        } while (ff_cavs_next_mb(h));
-    } else { /* AV_PICTURE_TYPE_B */
-        do {
-            ret = check_for_slice(h);
-            if (ret < 0)
-                break;
-            if (ret)
-                skip_count = -1;
-            /* past the picture's data, only a pending skip run can code MBs */
-            if (h->pic_data_end && !(h->skip_mode_flag && skip_count > 0)) {
-                av_log(h->avctx, AV_LOG_ERROR,
-                       "picture data ended before MB %d\n", h->mbidx);
-                ret = AVERROR_INVALIDDATA;
-                break;
-            }
-            if (h->skip_mode_flag && (skip_count < 0)) {
-                if (get_bits_left(&h->gb) < 1) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-                skip_count = get_ue_golomb(&h->gb);
-                if (skip_count < 0) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-            }
-            if (h->skip_mode_flag && skip_count--) {
-                ret = decode_mb_b(h, B_SKIP);
-            } else {
-                if (get_bits_left(&h->gb) < 1) {
-                    ret = AVERROR_INVALIDDATA;
-                    break;
-                }
-                mb_type = get_ue_golomb(&h->gb) + B_SKIP + h->skip_mode_flag;
-                if (mb_type > B_8X8)
-                    ret = decode_mb_i(h, mb_type - B_8X8 - 1);
-                else
-                    ret = decode_mb_b(h, mb_type);
-            }
-            if (ret < 0)
-                break;
-        } while (ff_cavs_next_mb(h));
-    }
+        }
+        if (ret < 0)
+            break;
+    } while (ff_cavs_next_mb(h));
     if (ret >= 0 && h->cur.f->pict_type != AV_PICTURE_TYPE_B) {
         av_frame_unref(h->DPB[1].f);
         FFSWAP(AVSFrame, h->cur, h->DPB[1]);