PR #24342 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24342 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24342.patch
Decoding a 4690 frame 720x576 stream single threaded runs each: full decode: 2.973 +- 0.014 s metadata only: 0.058 +- 0.007 s >From 5f3b824fb389feaedf3b62c078634291a6c2c2f9 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 1 Sep 2026 02:04:15 +0200 Subject: [PATCH 1/2] avcodec: add AVCodecContext.skip_pred Skipping prediction complements skip_idct and skip_loop_filter: with both skip_pred and skip_idct discarding a frame, no pixel of that frame is needed, so a decoder can drop all pixel operations and output the frame with only its metadata valid. This allows callers to determine timestamps, frame types and the reorder delay of a stream at a small fraction of the cost of a full decode. --- doc/APIchanges | 3 +++ doc/codecs.texi | 10 +++++++++- libavcodec/avcodec.h | 11 +++++++++++ libavcodec/options_table.h | 1 + libavcodec/pthread_frame.c | 1 + libavcodec/version.h | 2 +- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 7d878a630c..9cf1f85702 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23. API changes, most recent first: +2026-09-01 - xxxxxxxxxx - lavc 63.10.100 - avcodec.h + Add AVCodecContext.skip_pred. + 2026-08-28 - xxxxxxxxxx - lavc 63.9.100 - avcodec.h Add FF_BUG_H264_DP_NNZ. diff --git a/doc/codecs.texi b/doc/codecs.texi index e905282f1e..136462e13c 100644 --- a/doc/codecs.texi +++ b/doc/codecs.texi @@ -724,13 +724,21 @@ Set max macroblock lagrange factor (VBR). @item skip_loop_filter @var{integer} (@emph{decoding,video}) @item skip_idct @var{integer} (@emph{decoding,video}) +@item skip_pred @var{integer} (@emph{decoding,video}) @item skip_frame @var{integer} (@emph{decoding,video}) Make decoder discard processing depending on the frame type selected by the option value. @option{skip_loop_filter} skips frame loop filtering, @option{skip_idct} -skips frame IDCT/dequantization, @option{skip_frame} skips decoding. +skips frame IDCT/dequantization, @option{skip_pred} skips prediction +(intra prediction and motion compensation), @option{skip_frame} skips +decoding. + +@option{skip_pred} takes effect only together with @option{skip_idct}: +with both set to @samp{all} the decoder (currently only h264) skips all +pixel operations and outputs frames with uninitialized pixels whose +metadata, such as timestamps and frame types, is valid. Possible values: @table @samp diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index cf2257f3c9..55ea151074 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1936,6 +1936,17 @@ typedef struct AVCodecContext { * - decoding: Set by libavcodec */ enum AVAlphaMode alpha_mode; + + /** + * Skip prediction (intra prediction and motion compensation) for + * selected frames. Decoders honor this only when skip_pred and + * skip_idct both discard a frame: all pixel operations of that frame + * are then skipped and it is output with valid metadata and + * uninitialized pixels. + * - encoding: unused + * - decoding: Set by user. + */ + enum AVDiscard skip_pred; } AVCodecContext; /** diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 23fe7d3409..e18d0d4c46 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -255,6 +255,7 @@ static const AVOption avcodec_options[] = { {"mblmax", "maximum macroblock Lagrange factor (VBR)", OFFSET(mb_lmax), AV_OPT_TYPE_INT, {.i64 = FF_QP2LAMBDA * 31 }, 1, FF_LAMBDA_MAX, V|E}, {"skip_loop_filter", "skip loop filtering process for the selected frames", OFFSET(skip_loop_filter), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, {"skip_idct" , "skip IDCT/dequantization for the selected frames", OFFSET(skip_idct), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"skip_pred" , "skip prediction for the selected frames", OFFSET(skip_pred), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, {"skip_frame" , "skip decoding for the selected frames", OFFSET(skip_frame), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, {"none" , "discard no frame", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, {"default" , "discard useless frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 1814ab2768..5ec9a8a974 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -476,6 +476,7 @@ static int update_context_from_user(AVCodecContext *dst, const AVCodecContext *s dst->skip_loop_filter = src->skip_loop_filter; dst->skip_idct = src->skip_idct; + dst->skip_pred = src->skip_pred; dst->skip_frame = src->skip_frame; dst->frame_num = src->frame_num; diff --git a/libavcodec/version.h b/libavcodec/version.h index 230d5fa13e..80e2ae630d 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 9 +#define LIBAVCODEC_VERSION_MINOR 10 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ -- 2.52.0 >From ebe403eb039e1859fccf1fc9b2fbde886bf8c11f Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 1 Sep 2026 02:04:20 +0200 Subject: [PATCH 2/2] avcodec/h264dec: decode only metadata when prediction and IDCT are discarded Decoding a 4690 frame 720x576 stream single threaded, 12 interleaved runs each: full decode: 2.973 +- 0.014 s metadata only: 0.058 +- 0.007 s 51x faster, p = 5e-37 (Welch) --- libavcodec/h264_slice.c | 8 +++++++- libavcodec/h264dec.c | 3 ++- libavcodec/h264dec.h | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 4f4cf3f66b..501e637dae 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -525,6 +525,7 @@ static int h264_frame_start(H264Context *h) pic->needs_fg = h->sei.common.film_grain_characteristics && h->sei.common.film_grain_characteristics->present && + !ff_h264_skip_all_pixels(h->avctx) && !h->avctx->hwaccel && !(h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN); @@ -1578,7 +1579,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, if (h->short_ref[0]->field_picture) ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 1); } else if (!h->frame_recovered) { - if (!h->avctx->hwaccel) + if (!h->avctx->hwaccel && !ff_h264_skip_all_pixels(h->avctx)) color_frame(h->short_ref[0]->f, c); h->short_ref[0]->gray = 1; } @@ -2886,6 +2887,11 @@ int ff_h264_execute_decode_slices(H264Context *h) if (h->avctx->hwaccel || context_count < 1) return 0; + if (ff_h264_skip_all_pixels(avctx)) { + h->mb_y = h->mb_height; + goto finish; + } + av_assert0(context_count && h->slice_ctx[context_count - 1].mb_y < h->mb_height); if (context_count == 1) { diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 0892850fe0..6b9b6981ee 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -830,7 +830,8 @@ end: * past end by one (callers fault) and resync_mb_y != 0 * causes problems for the first MB line, too. */ - if (!FIELD_PICTURE(h) && h->current_slice && h->enable_er) { + if (!FIELD_PICTURE(h) && h->current_slice && h->enable_er && + !ff_h264_skip_all_pixels(h->avctx)) { H264SliceContext *sl = h->slice_ctx; int use_last_pic = h->last_pic_for_ec.f->buf[0] && !sl->ref_count[0]; diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h index 7bce897021..715b16d9c8 100644 --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -683,6 +683,13 @@ static av_always_inline int get_chroma_qp(const PPS *pps, int t, int qscale) return pps->chroma_qp_table[t][qscale]; } +static inline int ff_h264_skip_all_pixels(const AVCodecContext *avctx) +{ + return avctx->skip_pred >= AVDISCARD_ALL && + avctx->skip_idct >= AVDISCARD_ALL && + !(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS); +} + int ff_h264_field_end(H264Context *h, H264SliceContext *sl, int in_setup); int ff_h264_ref_picture(H264Picture *dst, const H264Picture *src); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
