[PR] avcodec/h264dec: implement slice data partitioning (PR #24267)

guy-with-a-why via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
PR #24267 opened by guy-with-a-why
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24267
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24267.patch

Implements slice data partitioning (NAL unit types 2 to 4) in the h264 decoder. Partition A carries the headers, B the intra residual and C the inter residual, chosen per macroblock from its type.

Partial support was removed in [167e004e1a](https://code.ffmpeg.org/FFmpeg/FFmpeg/commit/167e004e1aca7765686ed95d7cd8ea5064d4f6f6) as "does not work correctly and apparently never did". The defect was that partitions B and C were read from the start of the RBSP payload, skipping their `slice_id`, `colour_plane_id` and `redundant_pic_cnt` headers, so every residual read started at the wrong bit offset. Those headers are parsed here.

One divergence needs a decision. Clause 9.2.1 makes an inter neighbour unavailable for the `coeff_token` `nC` under constrained intra prediction, and step 7 then drops it from the `(nA + nB + 1) >> 1` average. JM instead treats it as available with `nN = 0`, so it still contributes to the average: the excluded neighbour increments the count that later triggers averaging, in both the reference [decoder][1] and [encoder][2]. This follows JM, since every partitioned stream comes from JM or a derivative, and the literal reading makes data_partitioning_cip.h264 fail. Two lines to flip if the spec text is preferred.

[1]: https://vcgit.hhi.fraunhofer.de/jvet/JM/-/blob/JM-19.0/ldecod/src/read_comp_cavlc.c#L61-66
[2]: https://vcgit.hhi.fraunhofer.de/jvet/JM/-/blob/JM-19.0/lencod/src/macroblock.c#L3890-3895

A missing partition fails the macroblock rather than concealing it; JM does not recover either. I've left as a follow-up.

```fate-samples
h264/data_partitioning.h264
h264/data_partitioning_ab.h264
h264/data_partitioning_cip.h264
```

The samples for data partitioning are JM 19.0 output derived from a synthetic clip and they have been sent to samples-request. The injected-samples CI step will fail until they land.


>From 92a941631832e00185e6f30c1abb85622db2636d Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:09:12 +0100
Subject: [PATCH 1/8] avcodec/h264dec: add data partitioning fields to
 H264SliceContext

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264dec.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index 74fd09dfaa..4b3e423741 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -180,6 +180,15 @@ typedef struct H264SliceContext {
     GetBitContext gb;
     ERContext *er;
 
+    /* Data partitioning: residual comes from gb_dpb (intra) or gb_dpc (inter),
+     * chosen per macroblock. Values not pointers: this struct is memcpy'd. */
+    GetBitContext gb_dpb;
+    GetBitContext gb_dpc;
+    int data_partitioning;
+    int dpb_available;
+    int dpc_available;
+    unsigned slice_id;
+
     int slice_num;
     int slice_type;
     int slice_type_nos;         ///< S free slice type (SI/SP are remapped to I/P)
-- 
2.52.0


>From edc807f960df261500bce01a0766e8706a0c8522 Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:09:37 +0100
Subject: [PATCH 2/8] avcodec/h264_slice: parse slice_id from partition A

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264_slice.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 9b5ed8f77e..429e955e6e 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -2073,6 +2073,32 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl,
     return 0;
 }
 
+/* slice_id follows slice_header() in a partition A (7.3.2.9.1). */
+static int h264_parse_slice_id(const H264Context *h, H264SliceContext *sl)
+{
+    const PPS *pps = h->ps.pps_list[sl->pps_id];
+    const SPS *sps = pps->sps;
+    unsigned nb_slice_ids = sps->mb_width * sps->mb_height;
+
+    if (pps->cabac) {
+        av_log(h->avctx, AV_LOG_ERROR, "Data partitioning requires CAVLC\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (sps->mb_aff && sl->picture_structure == PICT_FRAME)
+        nb_slice_ids /= 2;
+
+    sl->slice_id = get_ue_golomb_long(&sl->gb);
+    if (sl->slice_id >= nb_slice_ids) {
+        av_log(h->avctx, AV_LOG_ERROR, "slice_id %u out of range\n", sl->slice_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    sl->data_partitioning = 1;
+
+    return 0;
+}
+
 int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
 {
     H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
@@ -2081,10 +2107,20 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
 
     sl->gb = nal->gb;
 
+    sl->data_partitioning = 0;
+    sl->dpb_available     = 0;
+    sl->dpc_available     = 0;
+
     ret = h264_slice_header_parse(h, sl, nal);
     if (ret < 0)
         return ret;
 
+    if (nal->type == H264_NAL_DPA) {
+        ret = h264_parse_slice_id(h, sl);
+        if (ret < 0)
+            return ret;
+    }
+
     // discard redundant pictures
     if (sl->redundant_pic_count > 0) {
         sl->ref_count[0] = sl->ref_count[1] = 0;
-- 
2.52.0


>From c733de4f97e42ac0664dc1cdc2dbb4173f92532b Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:09:51 +0100
Subject: [PATCH 3/8] avcodec/h264dec: attach slice data partitions B and C

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264_slice.c | 41 ++++++++++++++++++++++++++++++
 libavcodec/h264dec.c    | 55 ++++++++++++++++++++++++++++++++++++++---
 libavcodec/h264dec.h    |  7 ++++++
 3 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 429e955e6e..f64bb6a17e 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -2099,6 +2099,47 @@ static int h264_parse_slice_id(const H264Context *h, H264SliceContext *sl)
     return 0;
 }
 
+int ff_h264_attach_slice_partition(const H264Context *h, H264SliceContext *sl,
+                                   const H2645NAL *nal)
+{
+    const PPS *pps = h->ps.pps_list[sl->pps_id];
+    GetBitContext gb = nal->gb;
+    int redundant_pic_cnt = 0;
+    unsigned slice_id;
+
+    if (!sl->data_partitioning)
+        return AVERROR_INVALIDDATA;
+
+    slice_id = get_ue_golomb_long(&gb);
+    if (pps->sps->residual_color_transform_flag)
+        skip_bits(&gb, 2);                  // colour_plane_id
+    if (pps->redundant_pic_cnt_present)
+        redundant_pic_cnt = get_ue_golomb(&gb);
+
+    if (get_bits_left(&gb) < 0) {
+        av_log(h->avctx, AV_LOG_ERROR, "Truncated slice data partition\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* 7.4.2.9.2: B and C repeat the slice_id and redundant_pic_cnt of their A. */
+    if (slice_id != sl->slice_id || redundant_pic_cnt != sl->redundant_pic_count) {
+        av_log(h->avctx, AV_LOG_WARNING, "Slice data partition %c does not "
+               "match the preceding partition A\n",
+               nal->type == H264_NAL_DPB ? 'B' : 'C');
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (nal->type == H264_NAL_DPB) {
+        sl->gb_dpb        = gb;
+        sl->dpb_available = 1;
+    } else {
+        sl->gb_dpc        = gb;
+        sl->dpc_available = 1;
+    }
+
+    return 0;
+}
+
 int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
 {
     H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index b78b7989ea..1b8c1ecd92 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -580,12 +580,35 @@ static void debug_green_metadata(const H264SEIGreenMetaData *gm, void *logctx)
     }
 }
 
+/**
+ * Attach the partitions B and C following the partition A at idx; 7.4.1.2.3
+ * requires them to be adjacent, so no state is kept across NAL units.
+ *
+ * @return index of the last NAL absorbed, or idx if there were none.
+ */
+static int h264_attach_partitions(const H264Context *h, H264SliceContext *sl,
+                                  int idx)
+{
+    while (idx + 1 < h->pkt.nb_nals) {
+        const H2645NAL *nal = &h->pkt.nals[idx + 1];
+
+        if (nal->type != H264_NAL_DPB && nal->type != H264_NAL_DPC)
+            break;
+        if (ff_h264_attach_slice_partition(h, sl, nal) < 0)
+            break;
+        idx++;
+    }
+
+    return idx;
+}
+
 static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
                             const uint8_t *buf, int buf_size)
 {
     AVCodecContext *const avctx = h->avctx;
     int nals_needed = 0; ///< number of NALs that need decoding before the next frame thread starts
     int idr_cleared=0;
+    int dp_attached_to = -1; ///< index of the last partition B/C claimed
     int i, ret = 0;
 
     h->has_slice = 0;
@@ -621,7 +644,7 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
 
     for (i = 0; i < h->pkt.nb_nals; i++) {
         H2645NAL *nal = &h->pkt.nals[i];
-        int max_slice_ctx, err;
+        int max_slice_ctx, nb_queued, err;
 
         if (avctx->skip_frame >= AVDISCARD_NONREF &&
             nal->ref_idc == 0 && nal->type != H264_NAL_SEI)
@@ -647,14 +670,37 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
             h->has_recovery_point = 1;
             av_fallthrough;
         case H264_NAL_SLICE:
+        case H264_NAL_DPA:
             h->has_slice = 1;
 
+            if (nal->type == H264_NAL_DPA) {
+                /* hwaccels take one self-contained slice NAL, not three */
+                if (avctx->hwaccel) {
+                    avpriv_request_sample(avctx, "hardware accelerated data partitioning");
+                    ret = AVERROR_PATCHWELCOME;
+                    goto end;
+                }
+                /* the lookahead needs all three partitions in one packet */
+                if (avctx->flags2 & AV_CODEC_FLAG2_CHUNKS) {
+                    av_log(avctx, AV_LOG_ERROR, "Decoding in chunks is not "
+                           "supported for partitioned slices\n");
+                    ret = AVERROR(ENOSYS);
+                    goto end;
+                }
+            }
+
+            nb_queued = h->nb_slice_ctx_queued;
+
             if ((err = ff_h264_queue_decode_slice(h, nal))) {
                 H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
                 sl->ref_count[0] = sl->ref_count[1] = 0;
                 break;
             }
 
+            if (nal->type == H264_NAL_DPA && h->nb_slice_ctx_queued > nb_queued)
+                dp_attached_to = h264_attach_partitions(h,
+                    h->slice_ctx + h->nb_slice_ctx_queued - 1, i);
+
             if (h->current_slice == 1) {
                 if (avctx->active_thread_type & FF_THREAD_FRAME &&
                     i >= nals_needed && !h->setup_finished && h->cur_pic_ptr) {
@@ -679,10 +725,13 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
                     goto end;
             }
             break;
-        case H264_NAL_DPA:
         case H264_NAL_DPB:
         case H264_NAL_DPC:
-            avpriv_request_sample(avctx, "data partitioning");
+            /* not claimed by the lookahead above, so it has no partition A */
+            if (i > dp_attached_to)
+                av_log(avctx, AV_LOG_WARNING, "Ignoring slice data partition "
+                       "%c without a matching partition A\n",
+                       nal->type == H264_NAL_DPB ? 'B' : 'C');
             break;
         case H264_NAL_SEI:
             if (h->setup_finished) {
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index 4b3e423741..2212dfa2a7 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -700,6 +700,13 @@ void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, int y,
  * slices are queued for the previous field, they are decoded.
  */
 int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal);
+
+/**
+ * Attach a slice data partition B or C to the slice started by partition A.
+ */
+int ff_h264_attach_slice_partition(const H264Context *h, H264SliceContext *sl,
+                                   const H2645NAL *nal);
+
 int ff_h264_execute_decode_slices(H264Context *h);
 int ff_h264_update_thread_context(AVCodecContext *dst,
                                   const AVCodecContext *src);
-- 
2.52.0


>From 23d2bfee3677647330b25b0acd805a3a26a1833b Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:24:05 +0100
Subject: [PATCH 4/8] avcodec/h264_cavlc: read residual from the matching
 partition

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264_cavlc.c | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/libavcodec/h264_cavlc.c b/libavcodec/h264_cavlc.c
index 09f7b42ba0..d3737166ad 100644
--- a/libavcodec/h264_cavlc.c
+++ b/libavcodec/h264_cavlc.c
@@ -662,6 +662,23 @@ int decode_luma_residual(const H264Context *h, H264SliceContext *sl,
     }
 }
 
+/* Residual is category 3 (intra) or 4 (inter), so it comes from partition B
+ * or C. NULL if that partition was not received. */
+static GetBitContext *mb_residual_gb(const H264Context *h, H264SliceContext *sl,
+                                     unsigned mb_type)
+{
+    int intra = IS_INTRA(mb_type);
+
+    if (!sl->data_partitioning)
+        return &sl->gb;
+    if (intra ? sl->dpb_available : sl->dpc_available)
+        return intra ? &sl->gb_dpb : &sl->gb_dpc;
+
+    av_log(h->avctx, AV_LOG_ERROR, "Missing slice data partition %c\n",
+           intra ? 'B' : 'C');
+    return NULL;
+}
+
 int ff_h264_decode_mb_cavlc(const H264Context *h, H264SliceContext *sl)
 {
     int mb_xy;
@@ -742,14 +759,18 @@ decode_intra_mb:
     if(IS_INTRA_PCM(mb_type)){
         const int mb_size = ff_h264_mb_sizes[h->ps.sps->chroma_format_idc] *
                             h->ps.sps->bit_depth_luma;
+        GetBitContext *gb = mb_residual_gb(h, sl, mb_type); // samples are category 3
+
+        if (!gb)
+            return AVERROR_INVALIDDATA;
 
         // We assume these blocks are very rare so we do not optimize it.
-        sl->intra_pcm_ptr = align_get_bits(&sl->gb);
-        if (get_bits_left(&sl->gb) < mb_size) {
+        sl->intra_pcm_ptr = align_get_bits(gb);
+        if (get_bits_left(gb) < mb_size) {
             av_log(h->avctx, AV_LOG_ERROR, "Not enough data for an intra PCM block.\n");
             return AVERROR_INVALIDDATA;
         }
-        skip_bits_long(&sl->gb, mb_size);
+        skip_bits_long(gb, mb_size);
 
         // In deblocking, the quantizer is 0
         h->cur_pic.qscale_table[mb_xy] = 0;
@@ -1067,10 +1088,13 @@ decode_intra_mb:
         int i4x4, i8x8, chroma_idx;
         int dquant;
         int ret;
-        GetBitContext *gb = &sl->gb;
+        GetBitContext *gb = mb_residual_gb(h, sl, mb_type);
         const uint8_t *scan, *scan8x8;
         const int max_qp = 51 + 6 * (h->ps.sps->bit_depth_luma - 8);
 
+        if (!gb)
+            return AVERROR_INVALIDDATA;
+
         dquant= get_se_golomb(&sl->gb);
 
         sl->qscale += (unsigned)dquant;
-- 
2.52.0


>From 8c6a8d6a5cc70d02b00a80980617d5b3acaa67e6 Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:24:05 +0100
Subject: [PATCH 5/8] avcodec/h264_parser: recognise partition A as a picture
 start

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264_parser.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
index af43cad609..56a8d830e2 100644
--- a/libavcodec/h264_parser.c
+++ b/libavcodec/h264_parser.c
@@ -360,6 +360,7 @@ static inline int parse_nal_units(AVCodecParserContext *s,
             p->poc.prev_poc_lsb          = 0;
             av_fallthrough;
         case H264_NAL_SLICE:
+        case H264_NAL_DPA:                // starts with a slice header too
             get_ue_golomb_long(&nal.gb);  // skip first_mb_in_slice
             slice_type   = get_ue_golomb_31(&nal.gb);
             s->pict_type = ff_h264_golomb_to_pict_type[slice_type % 5];
-- 
2.52.0


>From 74f9f416a5c638813b7479a83158487e34617e9d Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:37:41 +0100
Subject: [PATCH 6/8] avcodec/h264_mvpred: exclude inter neighbours from nC
 when partitioned

Signed-off-by: guy-with-a-why <[email protected]>
---
 libavcodec/h264_mvpred.h | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/libavcodec/h264_mvpred.h b/libavcodec/h264_mvpred.h
index 1fa5487322..3e4cde9625 100644
--- a/libavcodec/h264_mvpred.h
+++ b/libavcodec/h264_mvpred.h
@@ -668,10 +668,16 @@ static void fill_decode_caches(const H264Context *h, H264SliceContext *sl, int m
          * 4 L . .L . . . .
          * 5 L . .. . . . .
          */
-        /* FIXME: constraint_intra_pred & partitioning & nnz
-         * (let us hope this is just a typo in the spec) */
+        /* 9.2.1: with data partitioning and constrained intra prediction, an
+         * inter neighbour must not contribute to nC for an intra macroblock.
+         * Step 7 would drop it from the (nA + nB + 1) >> 1 average entirely,
+         * but JM's predict_nnz() counts it as present with zero coefficients,
+         * and real partitioned streams are encoded that way. Follow JM. */
+        int nnz_mask = sl->data_partitioning && h->ps.pps->constrained_intra_pred &&
+                       IS_INTRA(mb_type) ? IS_INTRA(-1) : -1;
+
         nnz_cache = sl->non_zero_count_cache;
-        if (top_type) {
+        if (top_type & nnz_mask) {
             nnz = h->non_zero_count[top_xy];
             AV_COPY32(&nnz_cache[4 + 8 * 0], &nnz[4 * 3]);
             if (!h->chroma_y_shift) {
@@ -682,14 +688,14 @@ static void fill_decode_caches(const H264Context *h, H264SliceContext *sl, int m
                 AV_COPY32(&nnz_cache[4 + 8 * 10], &nnz[4 * 9]);
             }
         } else {
-            uint32_t top_empty = CABAC(h) && !IS_INTRA(mb_type) ? 0 : 0x40404040;
+            uint32_t top_empty = (CABAC(h) && !IS_INTRA(mb_type)) || top_type ? 0 : 0x40404040;
             AV_WN32A(&nnz_cache[4 + 8 *  0], top_empty);
             AV_WN32A(&nnz_cache[4 + 8 *  5], top_empty);
             AV_WN32A(&nnz_cache[4 + 8 * 10], top_empty);
         }
 
         for (i = 0; i < 2; i++) {
-            if (left_type[LEFT(i)]) {
+            if (left_type[LEFT(i)] & nnz_mask) {
                 nnz = h->non_zero_count[left_xy[LEFT(i)]];
                 nnz_cache[3 + 8 * 1 + 2 * 8 * i] = nnz[left_block[8 + 0 + 2 * i]];
                 nnz_cache[3 + 8 * 2 + 2 * 8 * i] = nnz[left_block[8 + 1 + 2 * i]];
@@ -708,12 +714,14 @@ static void fill_decode_caches(const H264Context *h, H264SliceContext *sl, int m
                     nnz_cache[3 + 8 * 11 + 8 * i] = nnz[left_block[8 + 5 + 2 * i]];
                 }
             } else {
+                int empty = (CABAC(h) && !IS_INTRA(mb_type)) ||
+                            left_type[LEFT(i)] ? 0 : 64;
                 nnz_cache[3 + 8 *  1 + 2 * 8 * i] =
                 nnz_cache[3 + 8 *  2 + 2 * 8 * i] =
                 nnz_cache[3 + 8 *  6 + 2 * 8 * i] =
                 nnz_cache[3 + 8 *  7 + 2 * 8 * i] =
                 nnz_cache[3 + 8 * 11 + 2 * 8 * i] =
-                nnz_cache[3 + 8 * 12 + 2 * 8 * i] = CABAC(h) && !IS_INTRA(mb_type) ? 0 : 64;
+                nnz_cache[3 + 8 * 12 + 2 * 8 * i] = empty;
             }
         }
 
-- 
2.52.0


>From 1517b9b896f8aa8e0ccf13ab67667df1e17984f0 Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 14:42:02 +0100
Subject: [PATCH 7/8] Changelog,doc: note H.264 data partitioning support

Signed-off-by: guy-with-a-why <[email protected]>
---
 Changelog                 | 1 +
 doc/general_contents.texi | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/Changelog b/Changelog
index 38f1e10263..bde534351e 100644
--- a/Changelog
+++ b/Changelog
@@ -10,6 +10,7 @@ version <next>:
 - latticepal filter
 - DVD-Audio LPCM decoder and demuxing support
 - AVFoundation input device selection by unique ID and USB serial number
+- H.264 data partitioning support
 
 
 version 9.0:
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index d81392e03f..d446391429 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -1059,7 +1059,8 @@ following image formats are supported:
 @item H.263 / H.263-1996     @tab  X  @tab  X
 @item H.263+ / H.263-1998 / H.263 version 2  @tab  X  @tab  X
 @item H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10  @tab  E  @tab  X
-    @tab encoding supported through external library libx264 and OpenH264
+    @tab encoding supported through external library libx264 and OpenH264,
+          decoding supports data partitioning
 @item HEVC                   @tab  X  @tab  X
     @tab encoding supported through external library libx265 and libkvazaar
 @item HNM version 4          @tab     @tab  X
-- 
2.52.0


>From 4a52755fda95d51b7910fbe351a7cc26e7a590d0 Mon Sep 17 00:00:00 2001
From: guy-with-a-why <[email protected]>
Date: Tue, 25 Aug 2026 15:36:17 +0100
Subject: [PATCH 8/8] fate/h264: add slice data partitioning tests

Signed-off-by: guy-with-a-why <[email protected]>
---
 tests/fate/h264.mak                       |  8 ++++++++
 tests/ref/fate/h264-data-partitioning     | 15 +++++++++++++++
 tests/ref/fate/h264-data-partitioning-ab  | 15 +++++++++++++++
 tests/ref/fate/h264-data-partitioning-cip | 15 +++++++++++++++
 4 files changed, 53 insertions(+)
 create mode 100644 tests/ref/fate/h264-data-partitioning
 create mode 100644 tests/ref/fate/h264-data-partitioning-ab
 create mode 100644 tests/ref/fate/h264-data-partitioning-cip

diff --git a/tests/fate/h264.mak b/tests/fate/h264.mak
index dacaaab274..a42366f8eb 100644
--- a/tests/fate/h264.mak
+++ b/tests/fate/h264.mak
@@ -193,6 +193,9 @@ FATE_H264_REINIT_TESTS := large_420_8-to-small_420_8                    \
                           small_422_9-to-small_420_9                    \
 
 FATE_H264  := $(FATE_H264:%=fate-h264-conformance-%)                    \
+              fate-h264-data-partitioning                               \
+              fate-h264-data-partitioning-ab                            \
+              fate-h264-data-partitioning-cip                           \
               fate-h264-intra-refresh-recovery                          \
               fate-h264-lossless                                        \
               fate-h264-3386                                            \
@@ -465,6 +468,11 @@ fate-h264-xavc-4389:                              CMD = framecrc -i $(TARGET_SAM
 fate-h264-attachment-631:                         CMD = framecrc -i $(TARGET_SAMPLES)/h264/attachment631-small.mp4 -an -max_error_rate 0.96
 fate-h264-skip-nokey:                             CMD = framecrc -skip_frame nokey -i $(TARGET_SAMPLES)/h264/h264_intra_first-small.ts -vf scale -af aresample
 fate-h264-skip-nointra:                           CMD = framecrc -skip_frame nointra -i $(TARGET_SAMPLES)/h264/h264_intra_first-small.ts -vf scale -af aresample
+# slice data partitioning: A+B+C, and with partition C legitimately absent
+fate-h264-data-partitioning:                      CMD = framecrc -i $(TARGET_SAMPLES)/h264/data_partitioning.h264
+fate-h264-data-partitioning-ab:                   CMD = framecrc -i $(TARGET_SAMPLES)/h264/data_partitioning_ab.h264
+# constrained_intra_pred_flag=1, which changes the nC derivation (9.2.1)
+fate-h264-data-partitioning-cip:                  CMD = framecrc -i $(TARGET_SAMPLES)/h264/data_partitioning_cip.h264
 fate-h264-intra-refresh-recovery:                 CMD = framecrc -i $(TARGET_SAMPLES)/h264/intra_refresh.h264 -frames:v 10
 fate-h264-invalid-ref-mod:                        CMD = framecrc -i $(TARGET_SAMPLES)/h264/h264refframeregression.mp4 -an -frames 10 -pix_fmt yuv420p10le -vf scale
 fate-h264-lossless:                               CMD = framecrc -i $(TARGET_SAMPLES)/h264/lossless.h264
diff --git a/tests/ref/fate/h264-data-partitioning b/tests/ref/fate/h264-data-partitioning
new file mode 100644
index 0000000000..00a1b45f92
--- /dev/null
+++ b/tests/ref/fate/h264-data-partitioning
@@ -0,0 +1,15 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 176x144
+#sar 0: 0/1
+0,          0,          0,        1,    38016, 0x1b6c95f8
+0,          1,          1,        1,    38016, 0x3321d0ef
+0,          2,          2,        1,    38016, 0x8ab31546
+0,          3,          3,        1,    38016, 0x978d5bc8
+0,          4,          4,        1,    38016, 0xe3c1d02b
+0,          5,          5,        1,    38016, 0x11bafd04
+0,          6,          6,        1,    38016, 0x8760741a
+0,          7,          7,        1,    38016, 0x565ba13f
+0,          8,          8,        1,    38016, 0xdf66f465
+0,          9,          9,        1,    38016, 0x54bf06b8
diff --git a/tests/ref/fate/h264-data-partitioning-ab b/tests/ref/fate/h264-data-partitioning-ab
new file mode 100644
index 0000000000..6a430edda5
--- /dev/null
+++ b/tests/ref/fate/h264-data-partitioning-ab
@@ -0,0 +1,15 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 176x144
+#sar 0: 0/1
+0,          0,          0,        1,    38016, 0x1b6c95f8
+0,          1,          1,        1,    38016, 0xc732de4b
+0,          2,          2,        1,    38016, 0x34875d49
+0,          3,          3,        1,    38016, 0x76906edf
+0,          4,          4,        1,    38016, 0xe673e1a7
+0,          5,          5,        1,    38016, 0xc2f1f465
+0,          6,          6,        1,    38016, 0x08f49ac4
+0,          7,          7,        1,    38016, 0xee04b8ff
+0,          8,          8,        1,    38016, 0xf45202b2
+0,          9,          9,        1,    38016, 0x40051425
diff --git a/tests/ref/fate/h264-data-partitioning-cip b/tests/ref/fate/h264-data-partitioning-cip
new file mode 100644
index 0000000000..74f9ac0437
--- /dev/null
+++ b/tests/ref/fate/h264-data-partitioning-cip
@@ -0,0 +1,15 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 176x144
+#sar 0: 0/1
+0,          0,          0,        1,    38016, 0x1b6c95f8
+0,          1,          1,        1,    38016, 0x63e2d92f
+0,          2,          2,        1,    38016, 0x6f631a0f
+0,          3,          3,        1,    38016, 0xfee663ea
+0,          4,          4,        1,    38016, 0x3866d66c
+0,          5,          5,        1,    38016, 0xa918f919
+0,          6,          6,        1,    38016, 0xda56794e
+0,          7,          7,        1,    38016, 0x53d9abfc
+0,          8,          8,        1,    38016, 0x2deff2ec
+0,          9,          9,        1,    38016, 0xae1c0e66
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.