PR #22927 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22927
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22927.patch


>From f5d8ac13fda5f95426144bc7f2cefea29790bf14 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sun, 26 Apr 2026 20:14:07 +0800
Subject: [PATCH 1/2] avcodec/hevc: do not reset decoder on non-coding SPS
 changes

Clearing the DPB on every SPS change breaks decoding when only
non-coding parameters change, because reference frames are lost while
subsequent inter frames still depend on.

Add sps_needs_reinit() that checks whether the new SPS differs in any
coding-relevant field. When it does not, update the SPS/VPS references
and re-export stream parameters without touching the DPB.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavcodec/hevc/hevcdec.c | 128 +++++++++++++++++++++++++++++++++++---
 1 file changed, 118 insertions(+), 10 deletions(-)

diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c
index ae5f0fe69d..5758e1980b 100644
--- a/libavcodec/hevc/hevcdec.c
+++ b/libavcodec/hevc/hevcdec.c
@@ -736,6 +736,103 @@ static enum AVPixelFormat get_format(HEVCContext *s, 
const HEVCSPS *sps)
     return 0;
 }
 
+static int sps_needs_reinit(const HEVCSPS *sps_old, const HEVCSPS *sps_new)
+{
+    if (!sps_old)
+        return 1;
+
+    if (sps_old->pix_fmt != sps_new->pix_fmt ||
+        sps_old->width != sps_new->width ||
+        sps_old->height != sps_new->height ||
+        sps_old->bit_depth != sps_new->bit_depth ||
+        sps_old->bit_depth_chroma != sps_new->bit_depth_chroma ||
+        sps_old->pixel_shift != sps_new->pixel_shift ||
+        sps_old->chroma_format_idc != sps_new->chroma_format_idc ||
+        sps_old->separate_colour_plane != sps_new->separate_colour_plane)
+        return 1;
+
+    if (sps_old->log2_ctb_size != sps_new->log2_ctb_size ||
+        sps_old->log2_min_cb_size != sps_new->log2_min_cb_size ||
+        sps_old->log2_min_tb_size != sps_new->log2_min_tb_size ||
+        sps_old->log2_max_trafo_size != sps_new->log2_max_trafo_size ||
+        sps_old->log2_min_pu_size != sps_new->log2_min_pu_size ||
+        sps_old->log2_diff_max_min_coding_block_size !=
+            sps_new->log2_diff_max_min_coding_block_size ||
+        sps_old->log2_diff_max_min_transform_block_size !=
+            sps_new->log2_diff_max_min_transform_block_size)
+        return 1;
+
+    if (sps_old->max_transform_hierarchy_depth_inter !=
+            sps_new->max_transform_hierarchy_depth_inter ||
+        sps_old->max_transform_hierarchy_depth_intra !=
+            sps_new->max_transform_hierarchy_depth_intra)
+        return 1;
+
+    if (sps_old->log2_max_poc_lsb != sps_new->log2_max_poc_lsb ||
+        sps_old->max_sub_layers != sps_new->max_sub_layers ||
+        sps_old->temporal_mvp_enabled != sps_new->temporal_mvp_enabled ||
+        sps_old->sao_enabled != sps_new->sao_enabled ||
+        sps_old->pcm_enabled != sps_new->pcm_enabled ||
+        sps_old->scaling_list_enabled != sps_new->scaling_list_enabled ||
+        sps_old->amp_enabled != sps_new->amp_enabled ||
+        sps_old->strong_intra_smoothing_enabled !=
+            sps_new->strong_intra_smoothing_enabled)
+        return 1;
+
+    if (sps_old->pcm_enabled &&
+        (memcmp(&sps_old->pcm, &sps_new->pcm, sizeof(sps_old->pcm)) ||
+         sps_old->pcm_loop_filter_disabled != 
sps_new->pcm_loop_filter_disabled))
+        return 1;
+
+    if (sps_old->nb_st_rps != sps_new->nb_st_rps ||
+        memcmp(sps_old->st_rps, sps_new->st_rps, sizeof(sps_old->st_rps)))
+        return 1;
+
+    if (sps_old->long_term_ref_pics_present !=
+            sps_new->long_term_ref_pics_present ||
+        sps_old->num_long_term_ref_pics_sps !=
+            sps_new->num_long_term_ref_pics_sps ||
+        memcmp(sps_old->lt_ref_pic_poc_lsb_sps,
+               sps_new->lt_ref_pic_poc_lsb_sps,
+               sizeof(sps_old->lt_ref_pic_poc_lsb_sps)) ||
+        sps_old->used_by_curr_pic_lt != sps_new->used_by_curr_pic_lt)
+        return 1;
+
+    if (memcmp(sps_old->temporal_layer, sps_new->temporal_layer,
+              sizeof(sps_old->temporal_layer)))
+        return 1;
+
+    if (sps_old->scaling_list_enabled &&
+        memcmp(&sps_old->scaling_list, &sps_new->scaling_list,
+               sizeof(sps_old->scaling_list)))
+        return 1;
+
+    if (sps_old->range_extension != sps_new->range_extension ||
+        sps_old->transform_skip_rotation_enabled != 
sps_new->transform_skip_rotation_enabled ||
+        sps_old->transform_skip_context_enabled != 
sps_new->transform_skip_context_enabled ||
+        sps_old->implicit_rdpcm_enabled != sps_new->implicit_rdpcm_enabled ||
+        sps_old->explicit_rdpcm_enabled != sps_new->explicit_rdpcm_enabled ||
+        sps_old->extended_precision_processing != 
sps_new->extended_precision_processing ||
+        sps_old->intra_smoothing_disabled != sps_new->intra_smoothing_disabled 
||
+        sps_old->high_precision_offsets_enabled != 
sps_new->high_precision_offsets_enabled ||
+        sps_old->persistent_rice_adaptation_enabled != 
sps_new->persistent_rice_adaptation_enabled ||
+        sps_old->cabac_bypass_alignment_enabled != 
sps_new->cabac_bypass_alignment_enabled)
+        return 1;
+
+    if (sps_old->scc_extension != sps_new->scc_extension ||
+        sps_old->curr_pic_ref_enabled != sps_new->curr_pic_ref_enabled ||
+        sps_old->palette_mode_enabled != sps_new->palette_mode_enabled ||
+        sps_old->palette_predictor_initializers_present !=
+            sps_new->palette_predictor_initializers_present ||
+        sps_old->intra_boundary_filtering_disabled !=
+            sps_new->intra_boundary_filtering_disabled ||
+        sps_old->motion_vector_resolution_control_idc !=
+            sps_new->motion_vector_resolution_control_idc)
+        return 1;
+
+    return 0;
+}
+
 static int set_sps(HEVCContext *s, HEVCLayerContext *l, const HEVCSPS *sps)
 {
     int ret;
@@ -3230,6 +3327,7 @@ 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;
+        int needs_reinit;
 
         if (l != &s->layers[0]) {
             if (!sps_base) {
@@ -3261,22 +3359,32 @@ static int hevc_frame_start(HEVCContext *s, 
HEVCLayerContext *l,
             }
         }
 
-        ff_hevc_clear_refs(l);
+        needs_reinit = sps_needs_reinit(l->sps, sps);
+        if (needs_reinit) {
+            ff_hevc_clear_refs(l);
 
-        ret = set_sps(s, l, sps);
-        if (ret < 0)
-            return ret;
+            ret = set_sps(s, l, sps);
+            if (ret < 0)
+                return ret;
+        } else {
+            av_log(s->avctx, AV_LOG_DEBUG,
+                   "Keeping DPB for non-coding SPS change\n");
+            av_refstruct_replace(&l->sps, sps);
+            av_refstruct_replace(&s->vps, sps->vps);
+        }
 
         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 (needs_reinit) {
+                ret = get_format(s, sps);
+                if (ret < 0) {
+                    set_sps(s, l, NULL);
+                    return ret;
+                }
 
-            new_sequence = 1;
+                new_sequence = 1;
+            }
         }
     }
 
-- 
2.52.0


>From e3a9a03c41e655bf80f489d704fb705d39a3131f Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sun, 26 Apr 2026 20:15:12 +0800
Subject: [PATCH 2/2] fate/hevc: add test for non-coding SPS update

Test that the decoder preserves reference frames when an SPS update
changes only VUI parameters.

Signed-off-by: Zhao Zhili <[email protected]>
---
 tests/fate/hevc.mak                |  4 ++++
 tests/ref/fate/hevc-sps-vui-change | 13 +++++++++++++
 2 files changed, 17 insertions(+)
 create mode 100644 tests/ref/fate/hevc-sps-vui-change

diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak
index 351f188968..ac6dde77dd 100644
--- a/tests/fate/hevc.mak
+++ b/tests/fate/hevc.mak
@@ -314,6 +314,10 @@ FATE_HEVC-$(call FRAMECRC, MOV, HEVC) += fate-hevc-alpha
 fate-hevc-color-reserved: CMD = framecrc -bsf:v 
hevc_metadata=colour_primaries=0:transfer_characteristics=0:matrix_coefficients=3
 -i $(TARGET_SAMPLES)/hevc-conformance/AMP_A_Samsung_4.bit -vf 
scale,format=nv12 -frames:v 1
 FATE_HEVC-$(call FRAMECRC, HEVC, HEVC, HEVC_METADATA_BSF SCALE_FILTER) += 
fate-hevc-color-reserved
 
+# SPS update mid-stream with only VUI changes (no IDR follows).
+fate-hevc-sps-vui-change: CMD = framecrc -i 
$(TARGET_SAMPLES)/hevc/sps_vui_change.hevc
+FATE_HEVC-$(call FRAMECRC, HEVC, HEVC, HEVC_PARSER) += fate-hevc-sps-vui-change
+
 FATE_SAMPLES_AVCONV += $(FATE_HEVC-yes)
 FATE_SAMPLES_FFPROBE += $(FATE_HEVC_FFPROBE-yes)
 
diff --git a/tests/ref/fate/hevc-sps-vui-change 
b/tests/ref/fate/hevc-sps-vui-change
new file mode 100644
index 0000000000..b300f8739d
--- /dev/null
+++ b/tests/ref/fate/hevc-sps-vui-change
@@ -0,0 +1,13 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 416x240
+#sar 0: 0/1
+0,          0,          0,        1,   149760, 0x8edb27d7
+0,          1,          1,        1,   149760, 0x187419b5
+0,          2,          2,        1,   149760, 0x61100980
+0,          3,          3,        1,   149760, 0xd799123e
+0,          4,          4,        1,   149760, 0x9011c8d0
+0,          5,          5,        1,   149760, 0xeafe7d99
+0,          6,          6,        1,   149760, 0x94f32245
+0,          7,          7,        1,   149760, 0x8185910a
-- 
2.52.0

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

Reply via email to