On Tue, 25 Aug 2026, 16:41 guy-with-a-why via ffmpeg-devel, <
[email protected]> wrote:

> 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
>

I don't think FFmpeg should be implementing theoretical H.264 decode
features.

So theoretical that even JM doesn't follow the spec.

Kieran

>
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to