PR #25517 opened by Marti2203 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25517 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25517.patch
`vp3_update_thread_context()` compares each `s->qps[i]` with `s1->qps[1]` instead of `s1->qps[i]`. If a thread's old `qps[i]` happens to equal the new `qps[1]`, `qmat[i]` is not copied. `qps[]` is still copied when another index differs, so the next frame with the same quantizers skips `init_dequantizer()` and is dequantized with a stale matrix. Theora output then depends on the thread count, and nothing is logged. Only streams that use more than one quantizer per frame and change them between frames are affected. Single-quantizer streams always take the copy path for index 0 and never use indices 1 and 2. The fix is the one-character index change. Verification (master a497ab4d07, release and ASan builds): - None of the Theora/VP3 samples in fate-suite (`vp3/*`, `ogg/*.ogv`) uses changing multi-quantizer frames. They decode identically with `-threads 1/4/8`, before and after the fix. - `ogg/empty_theora_packets.ogv` uses three quantizers (31, 20, 41) in every frame. After remuxing it to Matroska, I set the first quantizer of the first frame to 20 (one byte in the frame header). Before the fix, the framemd5 differs between `-threads 1`, `2`, `3`, `4` and `8` (5 of 9 frames are wrong with 4 threads). After the fix, all thread counts match `-threads 1`. - `fate-vp3` and `fate-oggtheora-demux` pass unchanged. - The new `fate-theora-frame-threads-qps` (second patch) needs no new sample. It does the same remux and one-byte patch with `run_with_patched_temp` and decodes with 4 frame threads. Its reference equals the single-threaded output of unpatched master. It fails without the fix and passes with it. This issue was found by an LLM-Enhanced Static Analysis Engine and verified manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) From 8c6e1079723cfc8c79549f1d89c572321e187bd5 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Tue, 27 Oct 2026 11:45:20 +0800 Subject: [PATCH 1/2] avcodec/vp3: fix quantizer matrix update with frame threading vp3_update_thread_context() compared every s->qps[i] with s1->qps[1] instead of s1->qps[i]. When a thread's previous qps[i] happened to equal the new qps[1], qmat[i] was not copied while qps[] still was, so the next frame with the same quantizers was dequantized with a stale matrix and the output depended on the number of threads. This only affects Theora streams that use more than one quantizer per frame. Signed-off-by: Martin Mirchev <[email protected]> --- libavcodec/vp3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c index ca468b047c1..9b6b9901d89 100644 --- a/libavcodec/vp3.c +++ b/libavcodec/vp3.c @@ -2517,7 +2517,7 @@ static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext * if (s != s1) { // copy qscale data if necessary for (int i = 0; i < 3; i++) { - if (s->qps[i] != s1->qps[1]) { + if (s->qps[i] != s1->qps[i]) { qps_changed = 1; memcpy(&s->qmat[i], &s1->qmat[i], sizeof(s->qmat[i])); } -- 2.54.0 From 550045cd97d56f467bfa4c0b53a2e12072bf7a66 Mon Sep 17 00:00:00 2001 From: Martin Mirchev <[email protected]> Date: Tue, 27 Oct 2026 11:45:32 +0800 Subject: [PATCH 2/2] fate/vpx: add a test for Theora quantizer changes with frame threading Patch the first quantizer of the first frame of a remuxed existing sample that uses three quantizers per frame, and decode it with four frame threads. Signed-off-by: Martin Mirchev <[email protected]> --- tests/fate/vpx.mak | 11 +++++++++++ tests/ref/fate/theora-frame-threads-qps | 14 ++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ref/fate/theora-frame-threads-qps diff --git a/tests/fate/vpx.mak b/tests/fate/vpx.mak index 764db4ce8bb..57d137d4104 100644 --- a/tests/fate/vpx.mak +++ b/tests/fate/vpx.mak @@ -28,6 +28,17 @@ fate-theora-coeff-level64: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/ FATE_VP3-$(call FRAMECRC, OGG, THEORA) += fate-theora-offset fate-theora-offset: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/vp3/offset_test.ogv +# Remux a sample that uses three quantizers per frame (31, 20, 41) and set the +# first quantizer of the first frame to 20. With frame threading, the matrix +# for the first quantizer must then be updated for the following frames. +FATE_VP3-$(call FRAMECRC, OGG MATROSKA, THEORA, MATROSKA_MUXER) += fate-theora-frame-threads-qps +fate-theora-frame-threads-qps: CMD = run_with_patched_temp \ + "$(FFMPEG) -nostdin -v error -i $(TARGET_SAMPLES)/ogg/empty_theora_packets.ogv \ + -map 0:v -c copy -fflags +bitexact -write_crc32 0 -y" \ + "$(FFMPEG) -nostdin -v error -bitexact -f framecrc - \ + -flags +bitexact -threads 4 -thread_type frame -i" \ + mkv 3751 "\0024" + FATE_VP3-$(call FRAMECRC, AVI, VP3) += fate-vp31 fate-vp31: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/vp3/vp31.avi diff --git a/tests/ref/fate/theora-frame-threads-qps b/tests/ref/fate/theora-frame-threads-qps new file mode 100644 index 00000000000..042961253e5 --- /dev/null +++ b/tests/ref/fate/theora-frame-threads-qps @@ -0,0 +1,14 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 320x240 +#sar 0: 1/1 +0, 0, 0, 1, 115200, 0xa9a6e9db +0, 30, 30, 1, 115200, 0xdc0e7882 +0, 38, 38, 1, 115200, 0x217ca531 +0, 41, 41, 1, 115200, 0xf1f098c4 +0, 71, 71, 1, 115200, 0xf1f098c4 +0, 79, 79, 1, 115200, 0x6791ee7f +0, 82, 82, 1, 115200, 0x63d6731c +0, 112, 112, 1, 115200, 0x63d6731c +0, 142, 142, 1, 115200, 0x63d6731c -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
