PR #25233 opened by ffmpeg-devel URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25233 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25233.patch
**Backport:** https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24974 With `-export_side_data film_grain`, the native AV1 decoder exports `uv_mult` and `uv_mult_luma` 128 too high and `uv_offset` 256 too high. libdav1d exports the same stream unbiased. This PR subtracts the coding bias in `export_film_grain()`, so both decoders export identical film grain side data. ## Why The bitstream codes `cb_mult`, `cb_luma_mult` and `cb_offset` (and the Cr equivalents) as unsigned values with a bias (`cbs_av1_syntax_template.c:1300-1309`). `av1dec.c:1129-1134` copied them into `AVFilmGrainAOMParams` unchanged. Everywhere else, the struct holds them unbiased: - `libavutil/film_grain_params.h:108-112` documents `uv_offset` as a 9-bit value in [-256, 255]. - The AFGS1 parser stores `get_bits(8) - 128` and `get_bits(9) - 256` (`aom_film_grain.c:330-332`). - The synthesizer uses them as signed multipliers and offsets (`aom_film_grain_template.c:373-376`). - `libdav1d.c:546-551` copies dav1d's values, which dav1d parses as `- 128` / `- 256` (dav1d 1.5.4 `src/obu.c:1133-1135`). - `av1dec.c:1121-1126` already unbiases the AR coefficients in the same function. Any consumer that applies the exported grain therefore scaled chroma noise wrong. One example is libplacebo's `pl_map_avframe()`, which copies these fields into `pl_av1_grain_data` (`int8_t uv_mult`, `int16_t uv_offset`, `libplacebo/utils/libav_internal.h:441-443`). ## How it was tested The native decoder needs a hwaccel, so this cannot be a FATE test. I decoded with VAAPI on an AMD Raphael iGPU (radeonsi, Mesa 26.2.4) and compared every field of the `showinfo` film grain line, frame by frame, against libdav1d 1.5.4 on master `7bc3576910`: ```sh ffmpeg -export_side_data film_grain -c:v libdav1d -i IN.ivf -vf showinfo -f null - ffmpeg -init_hw_device vaapi=va:/dev/dri/renderD129 -hwaccel vaapi -hwaccel_device va \ -export_side_data film_grain -c:v av1 -i IN.ivf -vf showinfo -f null - ``` | sample | before | after | | --- | --- | --- | | `av1-test-vectors/av1-1-b8-23-film_grain-50.ivf` | 6 fields differ on all 10 frames (`uv_mult[0] = 128` vs `0`) | identical | | `av1-test-vectors/av1-1-b10-23-film_grain-50.ivf` | same | identical | | `av1/film_grain.ivf` | same | identical | | aomenc 3.15.1 with a grain table, raw cb 100/200/300, cr 150/170/200 | `uv_mult[0] = 100` vs `-28`, `uv_offset[1] = 200` vs `-56` | identical | The FATE vectors all code the neutral values (128/192/256). The crafted stream adds negative results and distinct Cb/Cr values. `make fate-source` passes. ## Notes - For a plane without scaling points these fields are not coded, and the decoder now exports -128/-256 there instead of 0. Those values are unused, as are the AR coefficients the function already exports for such a plane. - release/9.0 has the same code (`av1dec.c:1090`). The patch applies there unchanged, so it is a backport candidate. >From 6dfaba896fddaea098abec8ac84ff67375a7c6f1 Mon Sep 17 00:00:00 2001 From: lusoris <[email protected]> Date: Fri, 9 Oct 2026 18:19:54 +0200 Subject: [PATCH] avcodec/av1dec: remove the coding bias from exported chroma grain params export_film_grain() copied cb_mult, cb_luma_mult and cb_offset (and the Cr equivalents) straight from the bitstream, where they are coded with a bias of 128, 128 and 256. AVFilmGrainAOMParams holds them unbiased: the uv_offset documentation gives a range of [-256, 255], the AFGS1 parser in aom_film_grain.c subtracts the bias, the film grain synthesizer uses the values as signed multipliers and offsets, and libdav1d exports them unbiased. The AR coefficients a few lines above are already unbiased the same way. With -export_side_data film_grain and a hwaccel, the native decoder therefore exported values 128 or 256 higher than libdav1d for the same stream, and any consumer applying the exported grain scaled chroma noise wrong. Signed-off-by: lusoris <[email protected]> (cherry picked from commit 2a8c6d30b934f9bd8429f96f605e5fc7c5751679) --- libavcodec/av1dec.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c index d614c8d68ab..e4255d0745d 100644 --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -1087,12 +1087,12 @@ static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) aom->ar_coeffs_uv[1][i] = film_grain->ar_coeffs_cr_plus_128[i] - 128; } - aom->uv_mult[0] = film_grain->cb_mult; - aom->uv_mult[1] = film_grain->cr_mult; - aom->uv_mult_luma[0] = film_grain->cb_luma_mult; - aom->uv_mult_luma[1] = film_grain->cr_luma_mult; - aom->uv_offset[0] = film_grain->cb_offset; - aom->uv_offset[1] = film_grain->cr_offset; + aom->uv_mult[0] = film_grain->cb_mult - 128; + aom->uv_mult[1] = film_grain->cr_mult - 128; + aom->uv_mult_luma[0] = film_grain->cb_luma_mult - 128; + aom->uv_mult_luma[1] = film_grain->cr_luma_mult - 128; + aom->uv_offset[0] = film_grain->cb_offset - 256; + aom->uv_offset[1] = film_grain->cr_offset - 256; return 0; } -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
