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.
?YCbCr matrix is selected for AVCOL_SPC_YCOCG, and incorrect color is produced.
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.