为何FFmpeg读取的YUV数据与原始输入不一致?HDR视频制作存疑
问题分析与解决方案
你遇到的YUV数据不一致和HDR视频验证问题,我可以从编码流程、位深处理和验证方法这几个方面来帮你拆解解决:
一、YUV提取数据不一致的原因与解决方法
1. 核心原因:位深转换+有损编码
你的编码流程里有两个关键因素导致数据偏差:
- 位深转换:输入是8位
yuv420p,但编码时通过format=yuv420p10le转成了10位格式,而提取时又用-pix_fmt yuv420p强制转回8位,这个过程会丢失部分精度。 - 有损压缩:即使
crf=12是高质量设置,libx265的默认模式仍是有损压缩,会对像素值做微小调整来优化码率,所以原始全白帧的Y值(235)会出现234/235的波动。
2. 解决方法
方法1:提取时保持10位格式
修改提取命令,直接提取10位YUV,避免位深转换带来的损失:
ffmpeg -i test.mp4 -vframes 1 -c:v rawvideo -pix_fmt yuv420p10le read_yuv_10bit.yuv
读取时用uint16类型解析10位数据:
read_yuv_10bit = np.fromfile("read_yuv_10bit.yuv", dtype='uint16') print("read_yuv_10bit", read_yuv_10bit[:10])
原始8位Y值235转成10位后是235 << 2 = 940(8位到10位是扩展量化范围,像素值左移2位),你会看到提取的10位Y值接近940(有损编码下有微小波动是正常的)。
方法2:使用无损编码
如果需要完全一致的YUV数据,在x265参数中添加lossless=1开启无损压缩:
ffmpeg -y -s 100*100 -pix_fmt yuv420p -threads 4 -r 1 -stream_loop -1 -f rawvideo -i write_yuv.yuv -vf \ scale=out_h_chr_pos=0:out_v_chr_pos=0,format=yuv420p10le \ -c:v libx265 -tag:v hvc1 -t 10 -pix_fmt yuv420p10le -preset medium -x265-params \ crf=12:colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc:master-display=\"G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1)\":max-cll=\"1000,400\":lossless=1 \ -an test_lossless.mp4
此时提取10位YUV数据,会和原始转10位后的结果完全一致。
关于imageio读取的结果
imageio读取HDR视频时会自动做色调映射(Tone Mapping),把HDR的SMPTE2084转换函数映射到SDR的RGB空间,所以得到的[253,253,253]是正常的映射结果,并非原始HDR像素值。
二、HDR视频的验证方法
1. 检查HDR元数据
用FFmpeg查看视频的色彩参数,确认HDR元数据是否正确写入:
ffmpeg -i test.mp4 -hide_banner
你应该在输出中看到类似以下的信息,重点确认color_primaries、color_trc、color_space以及master display、max-cll和你设置的一致:
Stream #0:0(und): Video: hevc (Main 10) (hvc1 / 0x31637668), yuv420p10le(tv, bt2020nc/bt2020/smpte2084), 100x100, 1 fps, 1 tbr, 16384 tbn, 1 tbc (default) Metadata: encoder : Lavc60.31.100 libx265 color_range : tv color_space : bt2020nc color_primaries : bt2020 color_trc : smpte2084 chroma_location : left Mastering display metadata: Red: (0.708,0.292), Green: (0.170,0.797), Blue: (0.131,0.046), White point: (0.3127,0.329) Luminance: min=0.0001 cd/m2, max=10000 cd/m2 Max content light level: 1000 cd/m2, Max frame average light level: 400 cd/m2
2. 检查编码位深
用ffprobe确认视频是10位编码:
ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of default=noprint_wrappers=1:nokey=1 test.mp4
输出应为yuv420p10le,说明是符合HDR标准的10位视频。
3. 播放验证
用支持HDR的播放器测试:
- MPV:命令行添加
--hdr-compute-peak参数,播放时会显示HDR相关状态; - Windows Movies & TV:若显示器支持HDR,播放时会自动切换到HDR模式,视频亮度会明显高于普通SDR内容;
- VLC:需要在设置中开启“HDR support”,播放时查看是否识别为HDR视频。
4. 提取SEI信息验证
用FFmpeg导出HDR相关的SEI补充增强信息,确认元数据是否正确嵌入:
ffmpeg -i test.mp4 -dump_sections hdr -f null -
输出中会包含master display和max CLL的详细数据,和你设置的参数对比即可。
内容的提问来源于stack exchange,提问作者sianyi Huang




