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]