Commit 0fb9bceab1 for ffmpeg

commit 0fb9bceab1d953739431010cff778d8188107924
Author: YoungSoo Lee <youngsoo.lee@navercorp.com>
Date:   Fri Aug 21 10:46:32 2026 +0900

    lavc/hevc: group the state that continues across CTBs

    The CABAC and QP prediction state sits in HEVCLocalContext next to the scratch
    space used while a single CTB is decoded, although the two have different
    lifetimes: the former has to survive from one CTB to the next within a
    substream, the latter does not survive at all.

    Move it into a HEVCSubstream struct held by the local context. Whether the
    CABAC decoder was already opened on the substream data now travels with the
    substream, which replaces the is_wpp argument of ff_hevc_cabac_init() and
    allows a substream to be read from its own buffer. No functional change.

    Signed-off-by: YoungSoo Lee <youngsoo.lee@navercorp.com>

diff --git a/libavcodec/hevc/cabac.c b/libavcodec/hevc/cabac.c
index 57f43be06f..5b53815630 100644
--- a/libavcodec/hevc/cabac.c
+++ b/libavcodec/hevc/cabac.c
@@ -407,24 +407,24 @@ void ff_hevc_save_states(HEVCLocalContext *lc, const HEVCPPS *pps,
         (ctb_addr_ts % sps->ctb_width == 2 ||
          (sps->ctb_width == 2 &&
           ctb_addr_ts % sps->ctb_width == 0))) {
-        memcpy(lc->common_cabac_state->state, lc->cabac_state, HEVC_CONTEXTS);
+        memcpy(lc->common_cabac_state->state, lc->sub.cabac_state, HEVC_CONTEXTS);
         if (sps->persistent_rice_adaptation_enabled) {
-            memcpy(lc->common_cabac_state->stat_coeff, lc->stat_coeff, HEVC_STAT_COEFFS);
+            memcpy(lc->common_cabac_state->stat_coeff, lc->sub.stat_coeff, HEVC_STAT_COEFFS);
         }
     }
 }

 static void load_states(HEVCLocalContext *lc, const HEVCSPS *sps)
 {
-    memcpy(lc->cabac_state, lc->common_cabac_state->state, HEVC_CONTEXTS);
+    memcpy(lc->sub.cabac_state, lc->common_cabac_state->state, HEVC_CONTEXTS);
     if (sps->persistent_rice_adaptation_enabled) {
-        memcpy(lc->stat_coeff, lc->common_cabac_state->stat_coeff, HEVC_STAT_COEFFS);
+        memcpy(lc->sub.stat_coeff, lc->common_cabac_state->stat_coeff, HEVC_STAT_COEFFS);
     }
 }

 static int cabac_reinit(HEVCLocalContext *lc)
 {
-    return skip_bytes(&lc->cc, 0) == NULL ? AVERROR_INVALIDDATA : 0;
+    return skip_bytes(&lc->sub.cc, 0) == NULL ? AVERROR_INVALIDDATA : 0;
 }

 static void cabac_init_state(HEVCLocalContext *lc, const HEVCContext *s)
@@ -444,24 +444,45 @@ static void cabac_init_state(HEVCLocalContext *lc, const HEVCContext *s)
         pre ^= pre >> 31;
         if (pre > 124)
             pre = 124 + (pre & 1);
-        lc->cabac_state[i] = pre;
+        lc->sub.cabac_state[i] = pre;
     }

     for (i = 0; i < 4; i++)
-        lc->stat_coeff[i] = 0;
+        lc->sub.stat_coeff[i] = 0;
+}
+
+/* Open CABAC on this substream's data, or continue byte-aligned in it if it
+ * is already open (one slice read from a single buffer). */
+static int substream_continue(HEVCLocalContext *lc)
+{
+    HEVCSubstream *const sub = &lc->sub;
+    int ret;
+
+    if (sub->opened)
+        return cabac_reinit(lc);
+
+    ret = ff_init_cabac_decoder(&sub->cc, sub->data, sub->size);
+    if (ret < 0)
+        return ret;
+    sub->opened = 1;
+
+    return 0;
 }

 int ff_hevc_cabac_init(HEVCLocalContext *lc, const HEVCPPS *pps,
-                       int ctb_addr_ts, const uint8_t *data, size_t size,
-                       int is_wpp)
+                       int ctb_addr_ts)
 {
     const HEVCContext *const s = lc->parent;
     const HEVCSPS   *const sps = pps->sps;
+    HEVCSubstream *const sub = &lc->sub;

     if (ctb_addr_ts == pps->ctb_addr_rs_to_ts[s->sh.slice_ctb_addr_rs]) {
-        int ret = ff_init_cabac_decoder(&lc->cc, data, size);
+        /* First CTB of a slice segment. For a dependent slice segment the
+         * data still belongs to the new segment. */
+        int ret = ff_init_cabac_decoder(&sub->cc, sub->data, sub->size);
         if (ret < 0)
             return ret;
+        sub->opened = 1;
         if (s->sh.dependent_slice_segment_flag == 0 ||
             (pps->tiles_enabled_flag &&
              pps->tile_id[ctb_addr_ts] != pps->tile_id[ctb_addr_ts - 1]))
@@ -479,12 +500,7 @@ int ff_hevc_cabac_init(HEVCLocalContext *lc, const HEVCPPS *pps,
     } else {
         if (pps->tiles_enabled_flag &&
             pps->tile_id[ctb_addr_ts] != pps->tile_id[ctb_addr_ts - 1]) {
-            int ret;
-            if (!is_wpp)
-                ret = cabac_reinit(lc);
-            else {
-                ret = ff_init_cabac_decoder(&lc->cc, data, size);
-            }
+            int ret = substream_continue(lc);
             if (ret < 0)
                 return ret;
             cabac_init_state(lc, s);
@@ -492,12 +508,10 @@ int ff_hevc_cabac_init(HEVCLocalContext *lc, const HEVCPPS *pps,
         if (pps->entropy_coding_sync_enabled_flag) {
             if (ctb_addr_ts % sps->ctb_width == 0) {
                 int ret;
-                get_cabac_terminate(&lc->cc);
-                if (!is_wpp)
-                    ret = cabac_reinit(lc);
-                else {
-                    ret = ff_init_cabac_decoder(&lc->cc, data, size);
-                }
+                /* The end of row terminate bit is read only when continuing */
+                if (sub->opened)
+                    get_cabac_terminate(&sub->cc);
+                ret = substream_continue(lc);
                 if (ret < 0)
                     return ret;

@@ -511,7 +525,7 @@ int ff_hevc_cabac_init(HEVCLocalContext *lc, const HEVCPPS *pps,
     return 0;
 }

-#define GET_CABAC(ctx)  get_cabac(&lc->cc, &lc->cabac_state[ctx])
+#define GET_CABAC(ctx)  get_cabac(&lc->sub.cc, &lc->sub.cabac_state[ctx])

 int ff_hevc_sao_merge_flag_decode(HEVCLocalContext *lc)
 {
@@ -523,7 +537,7 @@ int ff_hevc_sao_type_idx_decode(HEVCLocalContext *lc)
     if (!GET_CABAC(SAO_TYPE_IDX_OFFSET))
         return 0;

-    if (!get_cabac_bypass(&lc->cc))
+    if (!get_cabac_bypass(&lc->sub.cc))
         return SAO_BAND;
     return SAO_EDGE;
 }
@@ -531,10 +545,10 @@ int ff_hevc_sao_type_idx_decode(HEVCLocalContext *lc)
 int ff_hevc_sao_band_position_decode(HEVCLocalContext *lc)
 {
     int i;
-    int value = get_cabac_bypass(&lc->cc);
+    int value = get_cabac_bypass(&lc->sub.cc);

     for (i = 0; i < 4; i++)
-        value = (value << 1) | get_cabac_bypass(&lc->cc);
+        value = (value << 1) | get_cabac_bypass(&lc->sub.cc);
     return value;
 }

@@ -543,26 +557,26 @@ int ff_hevc_sao_offset_abs_decode(HEVCLocalContext *lc, int bit_depth)
     int i = 0;
     int length = (1 << (FFMIN(bit_depth, 10) - 5)) - 1;

-    while (i < length && get_cabac_bypass(&lc->cc))
+    while (i < length && get_cabac_bypass(&lc->sub.cc))
         i++;
     return i;
 }

 int ff_hevc_sao_offset_sign_decode(HEVCLocalContext *lc)
 {
-    return get_cabac_bypass(&lc->cc);
+    return get_cabac_bypass(&lc->sub.cc);
 }

 int ff_hevc_sao_eo_class_decode(HEVCLocalContext *lc)
 {
-    int ret = get_cabac_bypass(&lc->cc) << 1;
-    ret    |= get_cabac_bypass(&lc->cc);
+    int ret = get_cabac_bypass(&lc->sub.cc) << 1;
+    ret    |= get_cabac_bypass(&lc->sub.cc);
     return ret;
 }

 int ff_hevc_end_of_slice_flag_decode(HEVCLocalContext *lc)
 {
-    return get_cabac_terminate(&lc->cc);
+    return get_cabac_terminate(&lc->sub.cc);
 }

 int ff_hevc_cu_transquant_bypass_flag_decode(HEVCLocalContext *lc)
@@ -595,7 +609,7 @@ int ff_hevc_cu_qp_delta_abs(HEVCLocalContext *lc)
     }
     if (prefix_val >= 5) {
         int k = 0;
-        while (k < 7 && get_cabac_bypass(&lc->cc)) {
+        while (k < 7 && get_cabac_bypass(&lc->sub.cc)) {
             suffix_val += 1 << k;
             k++;
         }
@@ -605,14 +619,14 @@ int ff_hevc_cu_qp_delta_abs(HEVCLocalContext *lc)
         }

         while (k--)
-            suffix_val += get_cabac_bypass(&lc->cc) << k;
+            suffix_val += get_cabac_bypass(&lc->sub.cc) << k;
     }
     return prefix_val + suffix_val;
 }

 int ff_hevc_cu_qp_delta_sign_flag(HEVCLocalContext *lc)
 {
-    return get_cabac_bypass(&lc->cc);
+    return get_cabac_bypass(&lc->sub.cc);
 }

 int ff_hevc_cu_chroma_qp_offset_flag(HEVCLocalContext *lc)
@@ -682,21 +696,21 @@ int ff_hevc_part_mode_decode(HEVCLocalContext *lc, const HEVCSPS *sps, int log2_
     if (GET_CABAC(PART_MODE_OFFSET + 1)) { // 01X, 01XX
         if (GET_CABAC(PART_MODE_OFFSET + 3)) // 011
             return PART_2NxN;
-        if (get_cabac_bypass(&lc->cc)) // 0101
+        if (get_cabac_bypass(&lc->sub.cc)) // 0101
             return PART_2NxnD;
         return PART_2NxnU; // 0100
     }

     if (GET_CABAC(PART_MODE_OFFSET + 3)) // 001
         return PART_Nx2N;
-    if (get_cabac_bypass(&lc->cc)) // 0001
+    if (get_cabac_bypass(&lc->sub.cc)) // 0001
         return PART_nRx2N;
     return PART_nLx2N;  // 0000
 }

 int ff_hevc_pcm_flag_decode(HEVCLocalContext *lc)
 {
-    return get_cabac_terminate(&lc->cc);
+    return get_cabac_terminate(&lc->sub.cc);
 }

 int ff_hevc_prev_intra_luma_pred_flag_decode(HEVCLocalContext *lc)
@@ -707,7 +721,7 @@ int ff_hevc_prev_intra_luma_pred_flag_decode(HEVCLocalContext *lc)
 int ff_hevc_mpm_idx_decode(HEVCLocalContext *lc)
 {
     int i = 0;
-    while (i < 2 && get_cabac_bypass(&lc->cc))
+    while (i < 2 && get_cabac_bypass(&lc->sub.cc))
         i++;
     return i;
 }
@@ -715,10 +729,10 @@ int ff_hevc_mpm_idx_decode(HEVCLocalContext *lc)
 int ff_hevc_rem_intra_luma_pred_mode_decode(HEVCLocalContext *lc)
 {
     int i;
-    int value = get_cabac_bypass(&lc->cc);
+    int value = get_cabac_bypass(&lc->sub.cc);

     for (i = 0; i < 4; i++)
-        value = (value << 1) | get_cabac_bypass(&lc->cc);
+        value = (value << 1) | get_cabac_bypass(&lc->sub.cc);
     return value;
 }

@@ -728,8 +742,8 @@ int ff_hevc_intra_chroma_pred_mode_decode(HEVCLocalContext *lc)
     if (!GET_CABAC(INTRA_CHROMA_PRED_MODE_OFFSET))
         return 4;

-    ret  = get_cabac_bypass(&lc->cc) << 1;
-    ret |= get_cabac_bypass(&lc->cc);
+    ret  = get_cabac_bypass(&lc->sub.cc) << 1;
+    ret |= get_cabac_bypass(&lc->sub.cc);
     return ret;
 }

@@ -738,7 +752,7 @@ int ff_hevc_merge_idx_decode(HEVCLocalContext *lc)
     int i = GET_CABAC(MERGE_IDX_OFFSET);

     if (i != 0) {
-        while (i < lc->parent->sh.max_num_merge_cand-1 && get_cabac_bypass(&lc->cc))
+        while (i < lc->parent->sh.max_num_merge_cand-1 && get_cabac_bypass(&lc->sub.cc))
             i++;
     }
     return i;
@@ -768,7 +782,7 @@ int ff_hevc_ref_idx_lx_decode(HEVCLocalContext *lc, int num_ref_idx_lx)
     while (i < max_ctx && GET_CABAC(REF_IDX_L0_OFFSET + i))
         i++;
     if (i == 2) {
-        while (i < max && get_cabac_bypass(&lc->cc))
+        while (i < max && get_cabac_bypass(&lc->sub.cc))
             i++;
     }

@@ -800,7 +814,7 @@ static av_always_inline int mvd_decode(HEVCLocalContext *lc)
     int ret = 2;
     int k = 1;

-    while (k < CABAC_MAX_BIN && get_cabac_bypass(&lc->cc)) {
+    while (k < CABAC_MAX_BIN && get_cabac_bypass(&lc->sub.cc)) {
         ret += 1U << k;
         k++;
     }
@@ -809,13 +823,13 @@ static av_always_inline int mvd_decode(HEVCLocalContext *lc)
         return 0;
     }
     while (k--)
-        ret += get_cabac_bypass(&lc->cc) << k;
-    return get_cabac_bypass_sign(&lc->cc, -ret);
+        ret += get_cabac_bypass(&lc->sub.cc) << k;
+    return get_cabac_bypass_sign(&lc->sub.cc, -ret);
 }

 static av_always_inline int mvd_sign_flag_decode(HEVCLocalContext *lc)
 {
-    return get_cabac_bypass_sign(&lc->cc, -1);
+    return get_cabac_bypass_sign(&lc->sub.cc, -1);
 }

 int ff_hevc_split_transform_flag_decode(HEVCLocalContext *lc, int log2_trafo_size)
@@ -894,10 +908,10 @@ static av_always_inline int last_significant_coeff_suffix_decode(HEVCLocalContex
 {
     int i;
     int length = (last_significant_coeff_prefix >> 1) - 1;
-    int value = get_cabac_bypass(&lc->cc);
+    int value = get_cabac_bypass(&lc->sub.cc);

     for (i = 1; i < length; i++)
-        value = (value << 1) | get_cabac_bypass(&lc->cc);
+        value = (value << 1) | get_cabac_bypass(&lc->sub.cc);
     return value;
 }

@@ -1011,14 +1025,14 @@ static av_always_inline int coeff_abs_level_remaining_decode(HEVCLocalContext *l
     int last_coeff_abs_level_remaining;
     int i;

-    prefix = cabac_unary_prefix(&lc->cc, CABAC_MAX_BIN);
+    prefix = cabac_unary_prefix(&lc->sub.cc, CABAC_MAX_BIN);

     if (prefix < 3) {
         if (rc_rice_param > 2)
-            suffix = cabac_bypass_bits(&lc->cc, rc_rice_param);
+            suffix = cabac_bypass_bits(&lc->sub.cc, rc_rice_param);
         else
             for (i = 0; i < rc_rice_param; i++)
-                suffix = (suffix << 1) | get_cabac_bypass(&lc->cc);
+                suffix = (suffix << 1) | get_cabac_bypass(&lc->sub.cc);
         last_coeff_abs_level_remaining = (prefix << rc_rice_param) + suffix;
     } else {
         int prefix_minus3 = prefix - 3;
@@ -1031,13 +1045,13 @@ static av_always_inline int coeff_abs_level_remaining_decode(HEVCLocalContext *l

         k = prefix_minus3 + rc_rice_param;
         if (k > 16) {
-            suffix  = cabac_bypass_bits(&lc->cc, 16) << (k - 16);
-            suffix |= cabac_bypass_bits(&lc->cc, k - 16);
+            suffix  = cabac_bypass_bits(&lc->sub.cc, 16) << (k - 16);
+            suffix |= cabac_bypass_bits(&lc->sub.cc, k - 16);
         } else if (k > 2) {
-            suffix = cabac_bypass_bits(&lc->cc, k);
+            suffix = cabac_bypass_bits(&lc->sub.cc, k);
         } else {
             for (i = 0; i < k; i++)
-                suffix = (suffix << 1) | get_cabac_bypass(&lc->cc);
+                suffix = (suffix << 1) | get_cabac_bypass(&lc->sub.cc);
         }
         last_coeff_abs_level_remaining = (((1 << prefix_minus3) + 3 - 1)
                                               << rc_rice_param) + suffix;
@@ -1051,10 +1065,10 @@ static av_always_inline int coeff_sign_flag_decode(HEVCLocalContext *lc, uint8_t
     int ret = 0;

     if (nb > 2)
-        return cabac_bypass_bits(&lc->cc, nb);
+        return cabac_bypass_bits(&lc->sub.cc, nb);

     for (i = 0; i < nb; i++)
-        ret = (ret << 1) | get_cabac_bypass(&lc->cc);
+        ret = (ret << 1) | get_cabac_bypass(&lc->sub.cc);
     return ret;
 }

@@ -1119,7 +1133,7 @@ void ff_hevc_hls_residual_coding(HEVCLocalContext *lc, const HEVCPPS *pps,
             7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10,
             10, 10, 11, 11, 11, 11, 11, 11, 12, 12
         };
-        int qp_y = lc->qp_y;
+        int qp_y = lc->sub.qp_y;

         if (pps->transform_skip_enabled_flag &&
             log2_trafo_size <= pps->log2_max_transform_skip_block_size) {
@@ -1133,10 +1147,10 @@ void ff_hevc_hls_residual_coding(HEVCLocalContext *lc, const HEVCPPS *pps,

             if (c_idx == 1)
                 offset = pps->cb_qp_offset + s->sh.slice_cb_qp_offset +
-                         lc->tu.cu_qp_offset_cb;
+                         lc->sub.cu_qp_offset_cb;
             else
                 offset = pps->cr_qp_offset + s->sh.slice_cr_qp_offset +
-                         lc->tu.cu_qp_offset_cr;
+                         lc->sub.cu_qp_offset_cr;

             qp_i = av_clip(qp_y + offset, - sps->qp_bd_offset, 57);
             if (sps->chroma_format_idc == 1) {
@@ -1439,7 +1453,7 @@ void ff_hevc_hls_residual_coding(HEVCLocalContext *lc, const HEVCPPS *pps,
                     sb_type = 2 * (c_idx == 0 ? 1 : 0);
                 else
                     sb_type = 2 * (c_idx == 0 ? 1 : 0) + 1;
-                c_rice_param = lc->stat_coeff[sb_type] / 4;
+                c_rice_param = lc->sub.stat_coeff[sb_type] / 4;
             }

             if (!(i == num_last_subset) && greater1_ctx == 0)
@@ -1491,12 +1505,12 @@ void ff_hevc_hls_residual_coding(HEVCLocalContext *lc, const HEVCPPS *pps,
                         if (trans_coeff_level > (3 << c_rice_param))
                             c_rice_param = sps->persistent_rice_adaptation_enabled ? c_rice_param + 1 : FFMIN(c_rice_param + 1, 4);
                         if (sps->persistent_rice_adaptation_enabled && !rice_init) {
-                            int c_rice_p_init = lc->stat_coeff[sb_type] / 4;
+                            int c_rice_p_init = lc->sub.stat_coeff[sb_type] / 4;
                             if (last_coeff_abs_level_remaining >= (3 << c_rice_p_init))
-                                lc->stat_coeff[sb_type]++;
+                                lc->sub.stat_coeff[sb_type]++;
                             else if (2 * last_coeff_abs_level_remaining < (1 << c_rice_p_init))
-                                if (lc->stat_coeff[sb_type] > 0)
-                                    lc->stat_coeff[sb_type]--;
+                                if (lc->sub.stat_coeff[sb_type] > 0)
+                                    lc->sub.stat_coeff[sb_type]--;
                             rice_init = 1;
                         }
                     }
@@ -1507,12 +1521,12 @@ void ff_hevc_hls_residual_coding(HEVCLocalContext *lc, const HEVCPPS *pps,
                     if (trans_coeff_level > (3 << c_rice_param))
                         c_rice_param = sps->persistent_rice_adaptation_enabled ? c_rice_param + 1 : FFMIN(c_rice_param + 1, 4);
                     if (sps->persistent_rice_adaptation_enabled && !rice_init) {
-                        int c_rice_p_init = lc->stat_coeff[sb_type] / 4;
+                        int c_rice_p_init = lc->sub.stat_coeff[sb_type] / 4;
                         if (last_coeff_abs_level_remaining >= (3 << c_rice_p_init))
-                            lc->stat_coeff[sb_type]++;
+                            lc->sub.stat_coeff[sb_type]++;
                         else if (2 * last_coeff_abs_level_remaining < (1 << c_rice_p_init))
-                            if (lc->stat_coeff[sb_type] > 0)
-                                lc->stat_coeff[sb_type]--;
+                            if (lc->sub.stat_coeff[sb_type] > 0)
+                                lc->sub.stat_coeff[sb_type]--;
                         rice_init = 1;
                     }
                 }
diff --git a/libavcodec/hevc/filter.c b/libavcodec/hevc/filter.c
index a4f1b24db7..1769014602 100644
--- a/libavcodec/hevc/filter.c
+++ b/libavcodec/hevc/filter.c
@@ -94,11 +94,11 @@ static int get_qPy_pred(HEVCLocalContext *lc, const HEVCContext *s,
     int qPy_pred, qPy_a, qPy_b;

     // qPy_pred
-    if (lc->first_qp_group || (!xQgBase && !yQgBase)) {
-        lc->first_qp_group = !lc->tu.is_cu_qp_delta_coded;
+    if (lc->sub.first_qp_group || (!xQgBase && !yQgBase)) {
+        lc->sub.first_qp_group = !lc->tu.is_cu_qp_delta_coded;
         qPy_pred = s->sh.slice_qp;
     } else {
-        qPy_pred = lc->qPy_pred;
+        qPy_pred = lc->sub.qPy_pred;
     }

     // qPy_a
@@ -129,10 +129,10 @@ void ff_hevc_set_qPy(HEVCLocalContext *lc,

     if (lc->tu.cu_qp_delta != 0) {
         int off = sps->qp_bd_offset;
-        lc->qp_y = FFUMOD(qp_y + lc->tu.cu_qp_delta + 52 + 2 * off,
+        lc->sub.qp_y = FFUMOD(qp_y + lc->tu.cu_qp_delta + 52 + 2 * off,
                                  52 + off) - off;
     } else
-        lc->qp_y = qp_y;
+        lc->sub.qp_y = qp_y;
 }

 static int get_qPy(const HEVCSPS *sps, const int8_t *qp_y_tab, int xC, int yC)
diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c
index f1bd562291..7680cd4154 100644
--- a/libavcodec/hevc/hevcdec.c
+++ b/libavcodec/hevc/hevcdec.c
@@ -1385,11 +1385,11 @@ static int hls_transform_unit(HEVCLocalContext *lc,
                     av_log(s->avctx, AV_LOG_ERROR,
                         "cu_chroma_qp_offset_idx not yet tested.\n");
                 }
-                lc->tu.cu_qp_offset_cb = pps->cb_qp_offset_list[cu_chroma_qp_offset_idx];
-                lc->tu.cu_qp_offset_cr = pps->cr_qp_offset_list[cu_chroma_qp_offset_idx];
+                lc->sub.cu_qp_offset_cb = pps->cb_qp_offset_list[cu_chroma_qp_offset_idx];
+                lc->sub.cu_qp_offset_cr = pps->cr_qp_offset_list[cu_chroma_qp_offset_idx];
             } else {
-                lc->tu.cu_qp_offset_cb = 0;
-                lc->tu.cu_qp_offset_cr = 0;
+                lc->sub.cu_qp_offset_cb = 0;
+                lc->sub.cu_qp_offset_cr = 0;
             }
             lc->tu.is_cu_chroma_qp_offset_coded = 1;
         }
@@ -1697,7 +1697,7 @@ static int hls_pcm_sample(HEVCLocalContext *lc, const HEVCLayerContext *l,
                          (((cb_size >> sps->hshift[1]) * (cb_size >> sps->vshift[1])) +
                           ((cb_size >> sps->hshift[2]) * (cb_size >> sps->vshift[2]))) *
                           sps->pcm.bit_depth_chroma : 0);
-    const uint8_t *pcm = skip_bytes(&lc->cc, (length + 7) >> 3);
+    const uint8_t *pcm = skip_bytes(&lc->sub.cc, (length + 7) >> 3);
     int ret;

     if (!s->sh.disable_deblocking_filter_flag)
@@ -2619,13 +2619,13 @@ static int hls_coding_unit(HEVCLocalContext *lc, const HEVCContext *s,

     x = y_cb * min_cb_width + x_cb;
     for (y = 0; y < length; y++) {
-        memset(&l->qp_y_tab[x], lc->qp_y, length);
+        memset(&l->qp_y_tab[x], lc->sub.qp_y, length);
         x += min_cb_width;
     }

     if(((x0 + (1<<log2_cb_size)) & qp_block_mask) == 0 &&
        ((y0 + (1<<log2_cb_size)) & qp_block_mask) == 0) {
-        lc->qPy_pred = lc->qp_y;
+        lc->sub.qPy_pred = lc->sub.qp_y;
     }

     set_ct_depth(sps, l->tab_ct_depth, x0, y0, log2_cb_size, lc->ct_depth);
@@ -2699,7 +2699,7 @@ static int hls_coding_quadtree(HEVCLocalContext *lc,

         if(((x0 + (1<<log2_cb_size)) & qp_block_mask) == 0 &&
             ((y0 + (1<<log2_cb_size)) & qp_block_mask) == 0)
-            lc->qPy_pred = lc->qp_y;
+            lc->sub.qPy_pred = lc->sub.qp_y;

         if (more_data)
             return ((x1 + cb_size_split) < sps->width ||
@@ -2742,13 +2742,13 @@ static void hls_decode_neighbour(HEVCLocalContext *lc,

     if (pps->entropy_coding_sync_enabled_flag) {
         if (x_ctb == 0 && (y_ctb & (ctb_size - 1)) == 0)
-            lc->first_qp_group = 1;
+            lc->sub.first_qp_group = 1;
         lc->end_of_tiles_x = sps->width;
     } else if (pps->tiles_enabled_flag) {
         if (ctb_addr_ts && pps->tile_id[ctb_addr_ts] != pps->tile_id[ctb_addr_ts - 1]) {
             int idxX = pps->col_idxX[x_ctb >> sps->log2_ctb_size];
             lc->end_of_tiles_x   = x_ctb + (pps->column_width[idxX] << sps->log2_ctb_size);
-            lc->first_qp_group   = 1;
+            lc->sub.first_qp_group   = 1;
         }
     } else {
         lc->end_of_tiles_x = sps->width;
@@ -2785,8 +2785,6 @@ static int hls_decode_entry(HEVCContext *s, GetBitContext *gb)
     const HEVCLayerContext *const l = &s->layers[s->cur_layer];
     const HEVCPPS   *const pps = s->pps;
     const HEVCSPS   *const sps = pps->sps;
-    const uint8_t *slice_data = gb->buffer + s->sh.data_offset;
-    const size_t   slice_size = get_bits_bytesize(gb, 1) - s->sh.data_offset;
     int ctb_size    = 1 << sps->log2_ctb_size;
     int more_data   = 1;
     int x_ctb       = 0;
@@ -2794,6 +2792,12 @@ static int hls_decode_entry(HEVCContext *s, GetBitContext *gb)
     int ctb_addr_ts = pps->ctb_addr_rs_to_ts[s->sh.slice_ctb_addr_rs];
     int ret;

+    /* The slice segment is one buffer, so CABAC stays open across tile and
+     * WPP row boundaries. */
+    lc->sub.data   = gb->buffer + s->sh.data_offset;
+    lc->sub.size   = get_bits_bytesize(gb, 1) - s->sh.data_offset;
+    lc->sub.opened = 0;
+
     while (more_data && ctb_addr_ts < sps->ctb_size) {
         int ctb_addr_rs = pps->ctb_addr_ts_to_rs[ctb_addr_ts];

@@ -2801,7 +2805,7 @@ static int hls_decode_entry(HEVCContext *s, GetBitContext *gb)
         y_ctb = (ctb_addr_rs / ((sps->width + ctb_size - 1) >> sps->log2_ctb_size)) << sps->log2_ctb_size;
         hls_decode_neighbour(lc, l, pps, sps, x_ctb, y_ctb, ctb_addr_ts);

-        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts, slice_data, slice_size, 0);
+        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts);
         if (ret < 0) {
             l->tab_slice_address[ctb_addr_rs] = -1;
             return ret;
@@ -2847,15 +2851,18 @@ static int hls_decode_entry_wpp(AVCodecContext *avctx, void *hevc_lclist,
     int ctb_addr_rs = s->sh.slice_ctb_addr_rs + ctb_row * ((sps->width + ctb_size - 1) >> sps->log2_ctb_size);
     int ctb_addr_ts = pps->ctb_addr_rs_to_ts[ctb_addr_rs];

-    const uint8_t *data      = s->data + s->sh.offset[ctb_row];
-    const size_t   data_size = s->sh.size[ctb_row];
-
     int progress = 0;

     int ret;

+    /* Substream of this row, opened at its first CTB */
+    lc->sub.data   = s->data + s->sh.offset[ctb_row];
+    lc->sub.size   = s->sh.size[ctb_row];
+    lc->sub.opened = 0;
+
+    /* Slices starting mid-row skip the initialization below */
     if (ctb_row)
-        ff_init_cabac_decoder(&lc->cc, data, data_size);
+        ff_init_cabac_decoder(&lc->sub.cc, lc->sub.data, lc->sub.size);

     while(more_data && ctb_addr_ts < sps->ctb_size) {
         int x_ctb = (ctb_addr_rs % sps->ctb_width) << sps->log2_ctb_size;
@@ -2875,7 +2882,7 @@ static int hls_decode_entry_wpp(AVCodecContext *avctx, void *hevc_lclist,
             return 0;
         }

-        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts, data, data_size, 1);
+        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts);
         if (ret < 0)
             goto error;
         hls_sao_param(lc, l, pps, sps,
@@ -3052,8 +3059,8 @@ static int hls_slice_data_wpp(HEVCContext *s, const H2645NAL *nal)
         return res;

     for (unsigned i = 1; i < s->nb_local_ctx; i++) {
-        s->local_ctx[i].first_qp_group = 1;
-        s->local_ctx[i].qp_y = s->local_ctx[0].qp_y;
+        s->local_ctx[i].sub.first_qp_group = 1;
+        s->local_ctx[i].sub.qp_y = s->local_ctx[0].sub.qp_y;
     }

     atomic_store(&s->wpp_err, 0);
@@ -3083,16 +3090,19 @@ static int hls_decode_entry_tile(AVCodecContext *avctx, void *hevc_lclist,
     const HEVCLayerContext *const l = &s->layers[s->cur_layer];
     const HEVCPPS   *const pps = s->pps;
     const HEVCSPS   *const sps = pps->sps;
-    const uint8_t *data      = s->data + s->sh.offset[job];
-    const size_t   data_size = s->sh.size[job];
     /* the slice covers every tile, so job and tile are the same index */
     int ctb_addr_ts = pps->ctb_addr_rs_to_ts[pps->row_bd[job / pps->num_tile_columns] * sps->ctb_width +
                                              pps->col_bd[job % pps->num_tile_columns]];
     int more_data = 1, ret;

-    lc->tile_bs_defer      = 1;
-    lc->tu.cu_qp_offset_cb = 0;
-    lc->tu.cu_qp_offset_cr = 0;
+    /* Substream of this tile, opened at its first CTB */
+    lc->sub.data   = s->data + s->sh.offset[job];
+    lc->sub.size   = s->sh.size[job];
+    lc->sub.opened = 0;
+
+    lc->tile_bs_defer       = 1;
+    lc->sub.cu_qp_offset_cb = 0;
+    lc->sub.cu_qp_offset_cr = 0;
     /* hls_decode_neighbour() skips this for the first CTB of the picture */
     lc->end_of_tiles_x     = pps->col_bd[job % pps->num_tile_columns + 1] << sps->log2_ctb_size;

@@ -3104,7 +3114,7 @@ static int hls_decode_entry_tile(AVCodecContext *avctx, void *hevc_lclist,

         hls_decode_neighbour(lc, l, pps, sps, x_ctb, y_ctb, ctb_addr_ts);

-        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts, data, data_size, 1);
+        ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts);
         if (ret < 0)
             return ret;

@@ -3143,8 +3153,8 @@ static int hls_slice_data_tiles(HEVCContext *s, const H2645NAL *nal)
         return res;

     for (unsigned i = 1; i < s->nb_local_ctx; i++) {
-        s->local_ctx[i].first_qp_group = 1;
-        s->local_ctx[i].qp_y           = s->local_ctx[0].qp_y;
+        s->local_ctx[i].sub.first_qp_group = 1;
+        s->local_ctx[i].sub.qp_y           = s->local_ctx[0].sub.qp_y;
     }

     for (ctb_addr_ts = start_ts; ctb_addr_ts < sps->ctb_size; ctb_addr_ts++)
@@ -3231,13 +3241,13 @@ static int decode_slice_data(HEVCContext *s, const HEVCLayerContext *l,
         }
     }

-    s->local_ctx[0].first_qp_group = !s->sh.dependent_slice_segment_flag;
+    s->local_ctx[0].sub.first_qp_group = !s->sh.dependent_slice_segment_flag;

     if (!pps->cu_qp_delta_enabled_flag)
-        s->local_ctx[0].qp_y = s->sh.slice_qp;
+        s->local_ctx[0].sub.qp_y = s->sh.slice_qp;

-    s->local_ctx[0].tu.cu_qp_offset_cb = 0;
-    s->local_ctx[0].tu.cu_qp_offset_cr = 0;
+    s->local_ctx[0].sub.cu_qp_offset_cb = 0;
+    s->local_ctx[0].sub.cu_qp_offset_cr = 0;

     if (s->avctx->active_thread_type == FF_THREAD_SLICE  &&
         s->sh.num_entry_point_offsets > 0                &&
diff --git a/libavcodec/hevc/hevcdec.h b/libavcodec/hevc/hevcdec.h
index 703b8df1af..b6116e74a3 100644
--- a/libavcodec/hevc/hevcdec.h
+++ b/libavcodec/hevc/hevcdec.h
@@ -341,8 +341,6 @@ typedef struct TransformUnit {
     int chroma_mode_c;
     uint8_t is_cu_qp_delta_coded;
     uint8_t is_cu_chroma_qp_offset_coded;
-    int8_t  cu_qp_offset_cb;
-    int8_t  cu_qp_offset_cr;
     uint8_t cross_pf;
 } TransformUnit;

@@ -388,18 +386,31 @@ typedef struct HEVCFrame {
     uint8_t flags;
 } HEVCFrame;

-typedef struct HEVCLocalContext {
-    uint8_t cabac_state[HEVC_CONTEXTS];
+/* State carried from one CTB to the next within a substream: one per tile
+ * column, or one per row for WPP. */
+typedef struct HEVCSubstream {
+    const uint8_t *data;    ///< CABAC bytes of this substream
+    size_t         size;
+    /* 0: open CABAC on data at first use, 1: already open, continue in it */
+    uint8_t        opened;

+    CABACContext cc;
+    uint8_t cabac_state[HEVC_CONTEXTS];
     uint8_t stat_coeff[HEVC_STAT_COEFFS];

+    int8_t  qp_y;
+    int     qPy_pred;
     uint8_t first_qp_group;
+    int8_t  cu_qp_offset_cb;
+    int8_t  cu_qp_offset_cr;
+} HEVCSubstream;
+
+typedef struct HEVCLocalContext {
+    HEVCSubstream sub;

     void *logctx;
     const struct HEVCContext *parent;

-    CABACContext cc;
-
     /**
      * This is a pointer to the common CABAC state.
      * In case entropy_coding_sync_enabled_flag is set,
@@ -412,11 +423,6 @@ typedef struct HEVCLocalContext {
      */
     HEVCCABACState *common_cabac_state;

-    int8_t qp_y;
-    int8_t curr_qp_y;
-
-    int qPy_pred;
-
     TransformUnit tu;

     uint8_t ctb_left_flag;
@@ -615,8 +621,7 @@ int ff_hevc_slice_rpl(HEVCContext *s);
 void ff_hevc_save_states(HEVCLocalContext *lc, const HEVCPPS *pps,
                          int ctb_addr_ts);
 int ff_hevc_cabac_init(HEVCLocalContext *lc, const HEVCPPS *pps,
-                       int ctb_addr_ts, const uint8_t *data, size_t size,
-                       int is_wpp);
+                       int ctb_addr_ts);
 int ff_hevc_sao_merge_flag_decode(HEVCLocalContext *lc);
 int ff_hevc_sao_type_idx_decode(HEVCLocalContext *lc);
 int ff_hevc_sao_band_position_decode(HEVCLocalContext *lc);