On Tue, 1 Sept 2026, 04:48 michaelni via ffmpeg-devel, <
[email protected]> wrote:

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

Why?

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

Reply via email to