From bbbe08d0b05c39f7e9791e5aec86b8e38b84d900 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Mon, 18 Nov 2024 12:27:50 +0100 Subject: [PATCH] Fix #111703: banding and color shift when decoding some videos Tell ffmpeg swscale to do accurate YUV->RGB conversion, instead of slightly faster but not really accurate one. Fixes banding and some color shifts in video files, particularly in dark regions. The accurate conversion is a bit slower though, on 4K resolution video, time taken to convert video frame from YUV to RGB: - x64 (Ryzen 5950X): 2.3ms -> 3.7ms - arm64 (M1 Max): 0.6ms -> 2.9ms My take is that paying 1-2ms per 4K video playback is acceptable since the result is obviously "more correct" and matches what VLC/ffplay produces. From what I can tell, "accurate conversion" turns off some dedicated assembly code paths within ffmpeg. Maybe someday ffmpeg would get accurate and assembly-optimized routines for that. With more accurate decoding, we can now lower the expected render test threshold again, since x64 & arm64 decoding is much closer now. Comparison screenshots in the PR. Pull Request: https://projects.blender.org/blender/blender/pulls/130383 --- source/blender/imbuf/intern/anim_movie.cc | 8 ++++++-- tests/data | 2 +- tests/python/sequencer_render_tests.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/source/blender/imbuf/intern/anim_movie.cc b/source/blender/imbuf/intern/anim_movie.cc index adce91aed41..a9044c2aba2 100644 --- a/source/blender/imbuf/intern/anim_movie.cc +++ b/source/blender/imbuf/intern/anim_movie.cc @@ -414,14 +414,18 @@ static int startffmpeg(ImBufAnim *anim) 1); } + /* Use full_chroma_int + accurate_rnd YUV->RGB conversion flags. Otherwise + * the conversion is not fully accurate and introduces some banding and color + * shifts, particularly in dark regions. See issue #111703 or upstream + * ffmpeg ticket https://trac.ffmpeg.org/ticket/1582 */ anim->img_convert_ctx = BKE_ffmpeg_sws_get_context(anim->x, anim->y, anim->pCodecCtx->pix_fmt, anim->x, anim->y, anim->pFrameRGB->format, - SWS_BILINEAR | SWS_PRINT_INFO | - SWS_FULL_CHR_H_INT); + SWS_POINT | SWS_FULL_CHR_H_INT | + SWS_ACCURATE_RND); if (!anim->img_convert_ctx) { fprintf(stderr, diff --git a/tests/data b/tests/data index 950d2829147..4e1ce05417f 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 950d28291474521feeb9c2304efb8daf546bf2df +Subproject commit 4e1ce05417f0e195bdae72738e9f6770f31642a6 diff --git a/tests/python/sequencer_render_tests.py b/tests/python/sequencer_render_tests.py index 686f20fc935..0f713619baa 100644 --- a/tests/python/sequencer_render_tests.py +++ b/tests/python/sequencer_render_tests.py @@ -50,7 +50,7 @@ def main(): report = render_report.Report("Sequencer", output_dir, oiiotool) report.set_pixelated(True) # Default error tolerances are quite large, lower them. - report.set_fail_threshold(3.0 / 255.0) + report.set_fail_threshold(2.0 / 255.0) report.set_fail_percent(0.01) report.set_reference_dir("reference")