播放VENC编码的码流,亮暗与原始YUV不一致
现象描述
用户使用第三方播放器播放经过VENC编码的码流,发现编码后的码流亮暗与原始YUV不一致。
可能原因
由于播放器的渲染效果不可控,播放器在解码、显示码流内容时,播放器的目标像素值域范围与VENC编码时设置的video_full_range_flag标志位不一致,导致发生像素值域映射,进而出现码流亮暗与原始YUV不一致。
关于full_range的原理介绍请参见参考信息。
处理步骤
不建议用播放器验收亮暗效果,因为播放器的渲染效果不可控,建议将VENC码流解码为YUV文件后再与原始YUV对比,同时需确保编码和解码时使用一致的video_full_range_flag标志位。
- 在VENC编码时,指定video_full_range_flag参数值。
在Atlas 推理系列产品上,对于H.264、H.265码流,当前VENC编码时video_full_range_flag默认值为0(表示limited_range)。
在Atlas 200I/500 A2推理产品上,对于H.265码流,当前VENC编码时video_full_range_flag默认值为0(表示limited_range);对于H.264码流,当前VENC编码时video_full_range_flag默认值为1(表示full_range)。
若默认值不满足要求,用户可以调用hi_mpi_venc_set_h264_vui或hi_mpi_venc_set_h265_vui接口修改video_full_range_flag参数值。
- 确认VENC编码后码流的video_full_range_flag标志位。
通过码流分析工具查看SPS字段的video_full_range_flag,flag=1表示原始YUV是full_range的,flag=0表示是limited_range的。
- 解码时指定video_full_range_flag,然后再将解码后YUV文件后与原始YUV对比亮暗。
不同解码器的video_full_range_flag使用方式不同,此处仅以FFmpeg为例,可以通过-vf参数指定目标输出video_full_range_flag,默认为输出limited_range。
此处以ffmpeg 为例,示例指令如下,供参考:
ffmpeg -i ${instream} -pix_fmt nv12 -vf scale=out_range=full/limited -y ${outyuv}
此处举例说明亮暗对比情况。现在的YUV一般都是full_range的,暂时排除源YUV本身为limited_range的情况,考虑Venc和FFmpeg参数组合4种情况,vui表示VENC编码时设置的video_full_range_flag,ffmpeg表示FFmpeg解码时设置的video_full_range_flag:
- vui0_ffmpeg0和vui1_ffmpeg1结果一致,并且对比过源YUV也是一致的,这是因为vui和ffmpeg参数一致,直接解压输出YUV;
- vui0_ffmpeg1图像"更暗",这是因为ffmpeg认为需要从limited_range转换为full_range,所以对像素值分布进行了往两侧拉伸,而该场景本身偏暗,像素值更多地往0值靠拢,表现为变暗;
- vui1_ffmpeg0图像"更亮",因为ffmpeg做了标准的值域压缩,从0~255压到16~235,图中大量低像素值往中间区间抬升,图像表现为变亮。
参考信息
电视机一般支持240个色阶,从16~255,也就是limited_range,对应YUV值域:Y[16, 235],UV [16, 240]。
现代电脑显示支持255个色阶,从0~255,也就是full_range,对应YUV值域:YUV[0, 255]。
以下标记等价,是在不同软件或者模块中各自的表达方式:
- “full range” = “jpeg” = “pc” = “cg” = “high rgb”
- “limited range” = “mpeg” = “tv” = “broadcast” = “low rgb”
H.265码流中VUI字段的video_full_range_flag = 0表示源YUV是limited_range,video_full_range_flag = 1表示源YUV是full_range。注意这个标记位不影响VENC编码过程,编码生成的码流数据只由输入YUV的实际像素值决定,编码阶段不会发生像素值映射。
例如,ffmpeg解码时会判断源YUV格式和输出YUV格式是否匹配,仅在两者不匹配时触发像素值重映射。当原始YUV是full_range的,此时VENC编码设置了video_full_range_flag = 1,若ffmpeg解码输出YUV格式是limited_range的,它发现YUV格式从full_range降为limited_range,于是在解码后对像素值进行了映射,从 [0, 255] 缩小到 [16, 235],此时就会发现解出来的YUV和原始YUV存在亮暗差异。