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
This commit is contained in:
Aras Pranckevicius
2024-11-18 12:27:50 +01:00
committed by Aras Pranckevicius
parent 704d34fe0f
commit bbbe08d0b0
3 changed files with 8 additions and 4 deletions

View File

@@ -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,

View File

@@ -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")