PR #25399 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25399 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25399.patch
>From dcf2669ded7358389e3c522e60e86e4c224ea157 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Fri, 23 Oct 2026 17:39:52 +0200 Subject: [PATCH 1/4] avcodec/golomb: read Golomb-Rice codes of up to 32 bits in get_ur_golomb() Add a FATE test that codes such a residual at every bit offset. --- libavcodec/golomb.h | 4 ++-- tests/Makefile | 1 + tests/fate/ffv1.mak | 9 +++++++++ tests/ref/fate/ffv1-golomb-float32-26bit | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/fate/ffv1.mak create mode 100644 tests/ref/fate/ffv1-golomb-float32-26bit diff --git a/libavcodec/golomb.h b/libavcodec/golomb.h index 5169a5a4c09..48b9895ece9 100644 --- a/libavcodec/golomb.h +++ b/libavcodec/golomb.h @@ -396,7 +396,7 @@ static inline int get_ur_golomb(GetBitContext *gb, int k, int limit, } #else OPEN_READER(re, gb); - UPDATE_CACHE(re, gb); + UPDATE_CACHE_32(re, gb); buf = GET_CACHE(re, gb); log = av_log2(buf); @@ -411,7 +411,7 @@ static inline int get_ur_golomb(GetBitContext *gb, int k, int limit, return buf; } else { LAST_SKIP_BITS(re, gb, limit); - UPDATE_CACHE(re, gb); + UPDATE_CACHE_32(re, gb); buf = SHOW_UBITS(re, gb, esc_len); diff --git a/tests/Makefile b/tests/Makefile index de71678e5c3..4cdd00dea44 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -197,6 +197,7 @@ include $(SRC_PATH)/tests/fate/astc.mak # Must be included after lavf-video.mak include $(SRC_PATH)/tests/fate/ffmpeg.mak include $(SRC_PATH)/tests/fate/ffprobe.mak +include $(SRC_PATH)/tests/fate/ffv1.mak include $(SRC_PATH)/tests/fate/fifo-muxer.mak include $(SRC_PATH)/tests/fate/filter-audio.mak # Must be included after vcodec.mak diff --git a/tests/fate/ffv1.mak b/tests/fate/ffv1.mak new file mode 100644 index 00000000000..5175c8df961 --- /dev/null +++ b/tests/fate/ffv1.mak @@ -0,0 +1,9 @@ +FATE_FFV1_FLOAT32-$(call TRANSCODE, FFV1, NUT, LAVFI_INDEV WRAPPED_AVFRAME_DECODER COLOR_FILTER FORMAT_FILTER GEQ_FILTER SCALE_FILTER) += \ + fate-ffv1-golomb-float32-26bit + +fate-ffv1-golomb-float32-26bit: CMD = transcode lavfi "color=black:s=352x288:r=1:d=8,format=gbrpf32,geq=r=X+Y*W:g=X+Y*W:b=X+W*if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,if(mod(Y\,2)\,290-(25+N+floor(N/2)+2*floor(N/7))+(Y-1)/2\,290-2*(25+N+floor(N/2)+2*floor(N/7))+Y/2)\,if(eq(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,19\,if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))+16)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+3\,if(lt(Y\,286)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+4\,Y-(25+N+floor(N/2)+2*floor(N/7))+2))))" nut \ + "-c:v ffv1 -strict experimental -coder 0 -remap_mode 1 -slices 1 -g 1" "-pix_fmt gbrpf32le -vf scale" + +FATE_FFMPEG += $(FATE_FFV1_FLOAT32-yes) +fate-ffv1: $(FATE_FFV1_FLOAT32-yes) + diff --git a/tests/ref/fate/ffv1-golomb-float32-26bit b/tests/ref/fate/ffv1-golomb-float32-26bit new file mode 100644 index 00000000000..3fe457cdcb5 --- /dev/null +++ b/tests/ref/fate/ffv1-golomb-float32-26bit @@ -0,0 +1,15 @@ +451a0326abe6e7a074184d3410eef4ce *tests/data/fate/ffv1-golomb-float32-26bit.nut +163524 tests/data/fate/ffv1-golomb-float32-26bit.nut +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 1/1 +0, 0, 0, 1, 1216512, 0x65029b72 +0, 1, 1, 1, 1216512, 0xc7369b72 +0, 2, 2, 1, 1216512, 0x08bc9b72 +0, 3, 3, 1, 1216512, 0x05e29b72 +0, 4, 4, 1, 1216512, 0x027a9b72 +0, 5, 5, 1, 1216512, 0x4f1a9b72 +0, 6, 6, 1, 1216512, 0xe6829b72 +0, 7, 7, 1, 1216512, 0xc9809b72 -- 2.54.0 >From 78ad6477d2de10d0c105458212b19d064a2a86f0 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Fri, 23 Oct 2026 17:39:52 +0200 Subject: [PATCH 2/4] avcodec/ffv1dec: clamp k in get_vlc_symbol() at 20 instead of bits Add a FATE test with such planes. --- libavcodec/ffv1dec.c | 6 ++++-- tests/fate/ffv1.mak | 7 ++++++- tests/ref/fate/ffv1-golomb-float32-1bit | 8 ++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 tests/ref/fate/ffv1-golomb-float32-1bit diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 8de15d24760..c15ae288f6d 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -56,10 +56,12 @@ static inline int get_vlc_symbol(GetBitContext *gb, VlcState *const state, k++; i += i; } - if (k > bits) { + /* get_ur_golomb() reads at most 32 bits, 12 of which the escape limit + * reserves for the prefix */ + if (k > 20) { ff_dlog(NULL, "k-overflow bias:%d error:%d drift:%d count:%d k:%d", state->bias, state->error_sum, state->drift, state->count, k); - k = bits; + k = 20; } v = get_sr_golomb(gb, k, 12, bits); diff --git a/tests/fate/ffv1.mak b/tests/fate/ffv1.mak index 5175c8df961..71e0639e0d1 100644 --- a/tests/fate/ffv1.mak +++ b/tests/fate/ffv1.mak @@ -1,9 +1,14 @@ FATE_FFV1_FLOAT32-$(call TRANSCODE, FFV1, NUT, LAVFI_INDEV WRAPPED_AVFRAME_DECODER COLOR_FILTER FORMAT_FILTER GEQ_FILTER SCALE_FILTER) += \ - fate-ffv1-golomb-float32-26bit + fate-ffv1-golomb-float32-26bit fate-ffv1-golomb-float32-1bit fate-ffv1-golomb-float32-26bit: CMD = transcode lavfi "color=black:s=352x288:r=1:d=8,format=gbrpf32,geq=r=X+Y*W:g=X+Y*W:b=X+W*if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,if(mod(Y\,2)\,290-(25+N+floor(N/2)+2*floor(N/7))+(Y-1)/2\,290-2*(25+N+floor(N/2)+2*floor(N/7))+Y/2)\,if(eq(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,19\,if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))+16)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+3\,if(lt(Y\,286)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+4\,Y-(25+N+floor(N/2)+2*floor(N/7))+2))))" nut \ "-c:v ffv1 -strict experimental -coder 0 -remap_mode 1 -slices 1 -g 1" "-pix_fmt gbrpf32le -vf scale" +# Planes with 2 distinct values are coded with 1 bit residuals while the +# Golomb-Rice parameter k starts at 2. +fate-ffv1-golomb-float32-1bit: CMD = transcode lavfi "color=black:s=64x48:r=1:d=1,format=gbrpf32,geq=r=mod(floor(X*Y/7)\,2):g=mod(floor(X*Y/7)\,2):b=mod(floor(X*Y/7)\,2)" nut \ + "-c:v ffv1 -strict experimental -coder 0 -remap_mode 1" "-pix_fmt gbrpf32le -vf scale" + FATE_FFMPEG += $(FATE_FFV1_FLOAT32-yes) fate-ffv1: $(FATE_FFV1_FLOAT32-yes) diff --git a/tests/ref/fate/ffv1-golomb-float32-1bit b/tests/ref/fate/ffv1-golomb-float32-1bit new file mode 100644 index 00000000000..ddd70d29d4d --- /dev/null +++ b/tests/ref/fate/ffv1-golomb-float32-1bit @@ -0,0 +1,8 @@ +89dee9204df7ed527995102564b73abc *tests/data/fate/ffv1-golomb-float32-1bit.nut +1402 tests/data/fate/ffv1-golomb-float32-1bit.nut +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x48 +#sar 0: 1/1 +0, 0, 0, 1, 36864, 0x538a6520 -- 2.54.0 >From 4ef1f3e83379dfc278f024f7b41e136be265dae4 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Fri, 23 Oct 2026 17:39:52 +0200 Subject: [PATCH 3/4] avcodec/ffv1enc: reject 32bit float Golomb-Rice slices above 2^18 pixels The escape code of a Golomb-Rice residual is 12 + bits long and put_bits() writes at most 31 bits, so bits must stay below 20. The RCT planes of remapped 32bit floats need log2 of the summed remap counts of two planes, which a slice of at most 1<<18 pixels keeps within 19 bits. The default slice size of 360x288 is within that, larger slices requested with -slices were written with escape codes longer than put_bits() supports. The assertion on k follows the same limit. --- libavcodec/ffv1enc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index 1d18f10f05f..869834c2e8f 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -250,7 +250,7 @@ static inline void put_vlc_symbol(PutBitContext *pb, VlcState *const state, i += i; } - av_assert2(k <= 16); + av_assert2(k <= 19); code = v ^ ((2 * state->drift + state->count) >> 31); @@ -1053,6 +1053,18 @@ static av_cold int encode_init_internal(AVCodecContext *avctx) if ((ret = ff_ffv1_encode_determine_slices(avctx)) < 0) return ret; + /* The Golomb-Rice escape code is 12 + bits long and put_bits() + * writes at most 31 bits, so bits must stay below 20. The RCT + * planes of remapped 32bit floats need log2 of the summed remap + * counts of two planes, which a slice of at most 1<<18 pixels + * keeps within 19 bits. */ + if (s->ac == AC_GOLOMB_RICE && s->bits_per_raw_sample == 32 && + ((avctx->width + s->num_h_slices - 1) / s->num_h_slices) * + (int64_t)((avctx->height + s->num_v_slices - 1) / s->num_v_slices) > 1 << 18) { + av_log(avctx, AV_LOG_ERROR, "Slices larger than 262144 pixels are not supported with 32bit floats and the Golomb-Rice coder\n"); + return AVERROR(EINVAL); + } + if ((ret = ff_ffv1_write_extradata(avctx)) < 0) return ret; } -- 2.54.0 >From 4964a46cfb3707ea57c011ead764bdd831a6be27 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Fri, 23 Oct 2026 17:39:52 +0200 Subject: [PATCH 4/4] fate/ffv1: test the default slice layout of 32bit float frames The size and checksum of the encoded file pin the slice layout and the remap coding of a 704x576 float frame, which the 360x288 default slice limit splits into 2x2 slices. --- tests/fate/ffv1.mak | 7 ++++++- tests/ref/fate/ffv1-float32-slices | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/ref/fate/ffv1-float32-slices diff --git a/tests/fate/ffv1.mak b/tests/fate/ffv1.mak index 71e0639e0d1..d26bb657433 100644 --- a/tests/fate/ffv1.mak +++ b/tests/fate/ffv1.mak @@ -1,5 +1,7 @@ FATE_FFV1_FLOAT32-$(call TRANSCODE, FFV1, NUT, LAVFI_INDEV WRAPPED_AVFRAME_DECODER COLOR_FILTER FORMAT_FILTER GEQ_FILTER SCALE_FILTER) += \ fate-ffv1-golomb-float32-26bit fate-ffv1-golomb-float32-1bit +FATE_FFV1_FLOAT32-$(call TRANSCODE, FFV1, NUT, LAVFI_INDEV WRAPPED_AVFRAME_DECODER TESTSRC_FILTER FORMAT_FILTER SCALE_FILTER) += \ + fate-ffv1-float32-slices fate-ffv1-golomb-float32-26bit: CMD = transcode lavfi "color=black:s=352x288:r=1:d=8,format=gbrpf32,geq=r=X+Y*W:g=X+Y*W:b=X+W*if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,if(mod(Y\,2)\,290-(25+N+floor(N/2)+2*floor(N/7))+(Y-1)/2\,290-2*(25+N+floor(N/2)+2*floor(N/7))+Y/2)\,if(eq(Y\,2*(25+N+floor(N/2)+2*floor(N/7))-4)\,19\,if(lt(Y\,2*(25+N+floor(N/2)+2*floor(N/7))+16)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+3\,if(lt(Y\,286)\,Y-2*(25+N+floor(N/2)+2*floor(N/7))+4\,Y-(25+N+floor(N/2)+2*floor(N/7))+2))))" nut \ "-c:v ffv1 -strict experimental -coder 0 -remap_mode 1 -slices 1 -g 1" "-pix_fmt gbrpf32le -vf scale" @@ -9,6 +11,9 @@ fate-ffv1-golomb-float32-26bit: CMD = transcode lavfi "color=black:s=352x288:r=1 fate-ffv1-golomb-float32-1bit: CMD = transcode lavfi "color=black:s=64x48:r=1:d=1,format=gbrpf32,geq=r=mod(floor(X*Y/7)\,2):g=mod(floor(X*Y/7)\,2):b=mod(floor(X*Y/7)\,2)" nut \ "-c:v ffv1 -strict experimental -coder 0 -remap_mode 1" "-pix_fmt gbrpf32le -vf scale" +# Default slice layout of a 32bit float frame larger than 360x288 +fate-ffv1-float32-slices: CMD = transcode lavfi "testsrc=size=704x576:r=1:d=1,format=gbrpf32" nut \ + "-c:v ffv1 -strict experimental" "-pix_fmt gbrpf32le -vf scale" + FATE_FFMPEG += $(FATE_FFV1_FLOAT32-yes) fate-ffv1: $(FATE_FFV1_FLOAT32-yes) - diff --git a/tests/ref/fate/ffv1-float32-slices b/tests/ref/fate/ffv1-float32-slices new file mode 100644 index 00000000000..084e88a7f7d --- /dev/null +++ b/tests/ref/fate/ffv1-float32-slices @@ -0,0 +1,8 @@ +33045e83befdf2085c63bfaea7c60006 *tests/data/fate/ffv1-float32-slices.nut +9585 tests/data/fate/ffv1-float32-slices.nut +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 704x576 +#sar 0: 1/1 +0, 0, 0, 1, 4866048, 0x1b0e9bf0 -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
