[PR] avcodec/hevcdec: only reinit the decoder when the SPS config changes (PR #23998)
sopparus via ffmpeg-devel <[email protected]> Mon, 03 Aug 2026 21:57:15 -0000
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <178579423622.59.9388352341053574539@29965ddac10e> |
PR #23998 opened by sopparus URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23998 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23998.patch hevc_frame_start() detects a new sequence by comparing the active SPS by pointer. ff_hevc_decode_nal_sps() only keeps the existing SPS on an exact byte match, so an SPS that is re-emitted with any byte changed - including fields the decoder does not act on, such as the VUI HRD bit_rate and cpb_size values that some encoders update per frame - is treated as a new sequence. That clears the DPB and calls get_format(), which unconditionally tears down and re-initialises any hwaccel. A stream which repeats parameter sets can therefore reinit the decoder continuously; a 2160p10 sample carrying two SPS that differ only in those HRD values did so at ~0.6 Hz, which is fatal on hwaccels backed by a contiguous memory allocator. Reconfigure only when a parameter the configuration depends on has changed. set_sps() and export_stream_params() remain unconditional, so the new SPS is still adopted and descriptive changes still reach the caller, and IDR still clears the DPB further down. Signed-off-by: sopparus <[email protected]> # Summary of changes Briefly describe what this PR does and why. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> >From 8cf743a0923be9358d3d6bfba08cd0ef5b80b17a Mon Sep 17 00:00:00 2001 From: sopparus <[email protected]> Date: Mon, 3 Aug 2026 22:59:36 +0200 Subject: [PATCH] avcodec/hevcdec: only reinit the decoder when the SPS config changes hevc_frame_start() detects a new sequence by comparing the active SPS by pointer. ff_hevc_decode_nal_sps() only keeps the existing SPS on an exact byte match, so an SPS that is re-emitted with any byte changed - including fields the decoder does not act on, such as the VUI HRD bit_rate and cpb_size values that some encoders update per frame - is treated as a new sequence. That clears the DPB and calls get_format(), which unconditionally tears down and re-initialises any hwaccel. A stream which repeats parameter sets can therefore reinit the decoder continuously; a 2160p10 sample carrying two SPS that differ only in those HRD values did so at ~0.6 Hz, which is fatal on hwaccels backed by a contiguous memory allocator. Reconfigure only when a parameter the configuration depends on has changed. set_sps() and export_stream_params() remain unconditional, so the new SPS is still adopted and descriptive changes still reach the caller, and IDR still clears the DPB further down. Signed-off-by: sopparus <[email protected]> --- libavcodec/hevc/hevcdec.c | 49 +++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c index 475c2738b1..921ffcbe66 100644 --- a/libavcodec/hevc/hevcdec.c +++ b/libavcodec/hevc/hevcdec.c @@ -3223,6 +3223,36 @@ static int find_finish_setup_nal(const HEVCContext *s) return nal_idx; } +/* + * Whether a newly activated SPS differs from the current one in a way that + * requires reconfiguration. Encoders may re-emit the SPS with only + * descriptive fields changed - e.g. the VUI HRD bitrate/CPB values - and + * reconfiguring on those drops the DPB and re-inits any hwaccel. + */ +static int sps_config_differs(const HEVCSPS *a, const HEVCSPS *b) +{ + if (a->pix_fmt != b->pix_fmt || + a->width != b->width || + a->height != b->height || + a->bit_depth != b->bit_depth || + a->bit_depth_chroma != b->bit_depth_chroma || + a->chroma_format_idc != b->chroma_format_idc || + a->max_sub_layers != b->max_sub_layers || + a->vps != b->vps) + return 1; + + for (int i = 0; i < a->max_sub_layers; i++) { + if (a->temporal_layer[i].max_dec_pic_buffering != + b->temporal_layer[i].max_dec_pic_buffering || + a->temporal_layer[i].num_reorder_pics != + b->temporal_layer[i].num_reorder_pics) + return 1; + } + + return memcmp(&a->output_window, &b->output_window, + sizeof(a->output_window)) != 0; +} + static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l, unsigned nal_idx) { @@ -3246,6 +3276,8 @@ static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l, if (l->sps != sps) { const HEVCSPS *sps_base = s->layers[0].sps; enum AVPixelFormat pix_fmt = sps->pix_fmt; + /* Evaluated before set_sps() below drops the reference to l->sps. */ + const int reconfig = !l->sps || sps_config_differs(l->sps, sps); if (l != &s->layers[0]) { if (!sps_base) { @@ -3277,7 +3309,8 @@ static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l, } } - ff_hevc_clear_refs(l); + if (reconfig) + ff_hevc_clear_refs(l); ret = set_sps(s, l, sps); if (ret < 0) @@ -3286,13 +3319,15 @@ static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l, if (l == &s->layers[0]) { export_stream_params(s, sps); - ret = get_format(s, sps); - if (ret < 0) { - set_sps(s, l, NULL); - return ret; - } + if (reconfig) { + ret = get_format(s, sps); + if (ret < 0) { + set_sps(s, l, NULL); + return ret; + } - new_sequence = 1; + new_sequence = 1; + } } } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]