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