Opened 11 months ago
Last modified 11 months ago
#10839 reopened defect
incorrect YCoCg to RGB conversion in swscale
Reported by: | Connor Worley | Owned by: | |
---|---|---|---|
Priority: | wish | Component: | swscale |
Version: | unspecified | Keywords: | |
Cc: | Blocked By: | ||
Blocking: | Reproduced by developer: | no | |
Analyzed by developer: | no |
Description
Per https://trac.ffmpeg.org/wiki/colorspace
The term "YUV" is ambiguous and often used wrongly, including the definition of pixel formats in FFmpeg. A more accurate term for how color is stored in digital video would be YCbCr.
When converting AV_PIX_FMT_YUV* to RGB in swscale, a YCbCr matrix is selected for AVCOL_SPC_YCOCG, and incorrect color is produced.
This happens here: https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libswscale/yuv2rgb.c;h=0a84b662f9c15a3ee3b4e482ed91212e3f99ff43;hb=HEAD#l64
To fix this, AVCOL_SPC_YCOCG should not use a YCbCr conversion matrix. The YCoCg conversion matrix can be found at https://en.wikipedia.org/wiki/YCoCg
Attachments (1)
Change History (20)
comment:1 by , 11 months ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
comment:2 by , 11 months ago
We do not support YCoCg. Use mpv instead.
I suggest adding some user-facing warning when AVCOL_SPC_YCOCG is used in places where it is intentionally unsupported.
comment:3 by , 11 months ago
by , 11 months ago
Attachment: | dice_ycg6.mov added |
---|
comment:5 by , 11 months ago
That is not true of the swscale code path. The attached file does not display a warning:
ffplay started on 2024-01-31 at 16:01:17 Report written to "ffplay-20240131-160117.log" Log level: 48 Command line: ffplay /Users/cworley/Downloads/dice_ycg6.mov -report -autoexit ffplay version 6.1.1 Copyright (c) 2003-2023 the FFmpeg developers built with Apple clang version 15.0.0 (clang-1500.1.0.2.5) configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.1.1_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopenvino --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoo libavutil 58. 29.100 / 58. 29.100 libavcodec 60. 31.102 / 60. 31.102 libavformat 60. 16.100 / 60. 16.100 libavdevice 60. 3.100 / 60. 3.100 libavfilter 9. 12.100 / 9. 12.100 libswscale 7. 5.100 / 7. 5.100 libswresample 4. 12.100 / 4. 12.100 libpostproc 57. 3.100 / 57. 3.100 Initialized metal renderer. [AVFormatContext @ 0x151b35420] Opening '/Users/cworley/Downloads/dice_ycg6.mov' for reading [file @ 0x600002e98c60] Setting default whitelist 'file,crypto,data' [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] Format mov,mp4,m4a,3gp,3g2,mj2 probed with size=2048 and score=100 [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] ISO: File Type Major Brand: qt [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] Unknown dref type 0x206c7275 size 12 [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] Processing st: 0, edit list 0 - media time: 0, duration: 522 [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] Before avformat_find_stream_info() pos: 103588 bytes read:33443 seeks:1 nb_streams:1 [dxv @ 0x14200bc00] YOCOCG6 compression with YCG6 texture (version 3.0) [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] All info found [mov,mp4,m4a,3gp,3g2,mj2 @ 0x151b35420] After avformat_find_stream_info() pos: 102913 bytes read:136320 seeks:2 frames:1 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/cworley/Downloads/dice_ycg6.mov': Metadata: major_brand : qt minor_version : 512 compatible_brands: qt encoder : Resolume Duration: 00:00:00.03, start: 0.000000, bitrate: 24861 kb/s Stream #0:0[0x1], 1, 1/15360: Video: dxv (DXD3 / 0x33445844), yuv420p(ycgco/unknown/unknown), 800x600, 24690 kb/s, 30 fps, 30 tbr, 15360 tbn (default) Metadata: handler_name : VideoHandler vendor_id : FFMP detected 12 logical cores [dxv @ 0x14200bfb0] YOCOCG6 compression with YCG6 texture (version 3.0) Video frame changed from size:0x0 format:none serial:-1 to size:800x600 format:yuv420p serial:1 [ffplay_buffer @ 0x600002b94420] Setting 'video_size' to value '800x600' [ffplay_buffer @ 0x600002b94420] Setting 'pix_fmt' to value '0' [ffplay_buffer @ 0x600002b94420] Setting 'time_base' to value '1/15360' [ffplay_buffer @ 0x600002b94420] Setting 'pixel_aspect' to value '0/1' [ffplay_buffer @ 0x600002b94420] Setting 'frame_rate' to value '30/1' [ffplay_buffer @ 0x600002b94420] w:800 h:600 pixfmt:yuv420p tb:1/15360 fr:30/1 sar:0/1 [AVFilterGraph @ 0x600003899a40] query_formats: 2 queried, 1 merged, 0 already done, 0 delayed Created 800x600 texture with SDL_PIXELFORMAT_IYUV. 0.01 M-V: 0.000 fd= 0 aq= 0KB vq= 0KB sq= 0B f=0/0 [AVIOContext @ 0x151a1fb20] Statistics: 136320 bytes read, 2 seeks
comment:6 by , 11 months ago
See my comment in https://trac.ffmpeg.org/ticket/9132#comment:2
Identity (and YCgCo) are very different matrices, as the order of operations are different (MatrixCoefficients 0 (Identity) or 8 (YCgCo) not only use different equations for the full-to-limited range adjustment but also perform the full-to-limited range adjustment before applying the transfer matrix).
There is no point in implementing that, Chrome/Chromium implemented it and so did Nvidia. See https://github.com/mpv-player/mpv/issues/4340
Mpv has much better code for this that just works. Ffplay uses SDL, that cannot support it, see #8862
comment:7 by , 11 months ago
Oh, I suppose you do not get it how to enable it in MPV. So the software decoder does not support YCgCo, of course. Use:
mpv.com --target-colorspace-hint=yes -vo=gpu-next -gpu-api=d3d11 -hwdec=d3d11va dice_ycg6.mov
That is for Windows.
comment:8 by , 11 months ago
I am aware the colorspace is broken in swscale and not in mpv. If the difficulties in fixing it in swscale are insurmountable, I believe it would be better to add a warning or deprecate and remove it rather than leaving it silently broken.
comment:9 by , 11 months ago
The warning does work, just not everywhere. Why I dunno, known bug. Try this file (please download it, ffplay the url will not work) https://web.archive.org/web/20210329043315/http://files.1f0.de/samples/bunny_444_YCgCo.mkv
comment:10 by , 11 months ago
The vf_scale filter you linked is automatically added to the filtergraph depending on the input format: https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavfilter/avfiltergraph.c;h=e4f59f56e2b102372178c693d8439059565d207f;hb=HEAD#l467 -- this is not always necessary, so it does not always happen.
Both this filtergraph behavior and the vf_scale filter itself are unrelated to the lack of a warning in swscale.
comment:11 by , 11 months ago
Resolution: | wontfix |
---|---|
Status: | closed → reopened |
comment:12 by , 11 months ago
I misspoke -- the sample I attached circumvents the swscale path entirely. Still, the warning should not be coupled to avfilter. Another warning should be added in the non-swscale path.
follow-up: 15 comment:13 by , 11 months ago
Summarizing what I believe is wrong:
- In ffplay, going through vf_scale should cause an error after 45e09a3041 (vf_scale: use colorspace negotiation API), but ffplay does not set the colorspace parameter on the buffersrc filter so some default conversion is chosen.
- In ffplay, automatic conversion can erroneously happen without going through vf_scale because the buffersink filter accepts YCoCg as a mergeable colorspace.
- swscale silently returns nonsense coefficients for YCoCg
I would like for these things to work, but if they can't, I would much rather they error than produce incorrect output.
comment:14 by , 11 months ago
the sample I attached circumvents the swscale path entirely
Yeah, I got that. :) DXV Version 3 is interesting.
comment:15 by , 11 months ago
Replying to Connor Worley:
Summarizing what I believe is wrong:
- In ffplay, going through vf_scale should cause an error after 45e09a3041 (vf_scale: use colorspace negotiation API), but ffplay does not set the colorspace parameter on the buffersrc filter so some default conversion is chosen.
- In ffplay, automatic conversion can erroneously happen without going through vf_scale because the buffersink filter accepts YCoCg as a mergeable colorspace.
- swscale silently returns nonsense coefficients for YCoCg
I would like for these things to work, but if they can't, I would much rather they error than produce incorrect output.
This sounds like a reasonable assessment. Definitely, ffplay should only advertise supported colorspaces that it can actually output.
I actually have a patch on the ML for fixing the first point: https://patchwork.ffmpeg.org/project/ffmpeg/patch/20240110090547.69151-1-ffmpeg@haasn.xyz/
There were some comments on the ML but no real opposal or approval. I'll merge it after a 24h grace period.
comment:16 by , 11 months ago
Check dcc7263b0e7935b6864e43465cfeca423bdf9e77 though I cannot see anything changing, hm.
comment:17 by , 11 months ago
The change is in this scenario:
$ ./ffplay -v verbose -vf format=color_spaces=bt709 dice_ycg6.mov
[ffplay_buffer @ 0x7f1458003200] w:800 h:600 pixfmt:yuv420p tb:1/15360 fr:30/1 sar:0/1 csp:ycgco range:unknown
[auto_scale_0 @ 0x7f1458004dc0] w:iw h:ih flags: interl:0
[Parsed_format_0 @ 0x7f1458003c80] auto-inserting filter 'auto_scale_0' between the filter 'ffplay_buffer' and the filter 'Parsed_format_0'
Impossible to convert between the formats supported by the filter 'ffplay_buffer' and the filter 'auto_scale_0'
It now correctly errors out.
Or:
$ ./ffplay -v verbose -vf format=rgb24 dice_ycg6.mov
[Parsed_format_0 @ 0x7f3288003c00] auto-inserting filter 'auto_scale_0' between the filter 'ffplay_buffer' and the filter 'Parsed_format_0'
Impossible to convert between the formats supported by the filter 'ffplay_buffer' and the filter 'auto_scale_0'
I will submit a patch for the second point, which would just require making ffplay request only formats it actually supports.
comment:18 by , 11 months ago
I merged a patch for the second point to master, which means that ffplay dice_ycg6.mov
now simply gives you:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'dice_ycg6.mov':=0/0
Metadata:
major_brand : qt
minor_version : 512
compatible_brands: qt
encoder : Resolume
Duration: 00:00:00.03, start: 0.000000, bitrate: 24861 kb/s
Stream #0:0[0x1]: Video: dxv (DXD3 / 0x33445844), yuv420p(ycgco/unknown/unknown), 800x600, 24690 kb/s, 30 fps, 30 tbr, 15360 tbn (default)
Metadata:
handler_name : VideoHandler
vendor_id : FFMP
Impossible to convert between the formats supported by the filter 'ffplay_buffer' and the filter 'auto_scale_0'
and exits out. This is, at least, preferable to displaying a broken image *by default*. If you know what you're doing, you can still use vf_setparams to override the colorspace explicitly... is what I *would* say, but vf_setparams doesn't use the YUV negotiation API yet. Let me fix that.
comment:19 by , 11 months ago
Fixed vf_setparams
, so now the only thing that can be done to improve the status quo would be to actually implement YCgCo in libswscale.
We do not support YCoCg. Use mpv instead.
Not that simple. YUV refers to pixel format used that is called AV_PIX_FMT_YUV*. The correct name is indeed AV_PIX_FMT_YUV*, not AV_PIX_FMT_YCBCR*. On the other hand the matrices used here https://github.com/FFmpeg/FFmpeg/blob/fa469545ba0782a9245474ecca873aa8984f506e/libavutil/pixfmt.h#L608
are indeed YCbCr, we do not support BT.407 analog TV's YUV.