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

Kieran Kunhya via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <CABGuwEn7yerZJwS5jy3ziuShiKA5CiB1XtO6otxwtcMCcHtoJw@mail.gmail.com>
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]
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.