今天我们要讨论的是关键帧的音视频开发圈的一位朋友在社群里提的问题,如下:
遇到了视频转码后有色差,这种一般如何处理呢?
以下是回答,欢迎大家留言讨论补充:
1、色差是如何产生的?
1)有损压缩产生的质量损失。
2)颜色空间转换产生的损失。
color_range
、color_space
。$ ffprobe -show_streams -i test.mp4
2、如何做颜色空间转换?
颜色空间转换每个模块都会有所涉及,播放器、转码、获取缩略图等,但按照底层模块划分如下:
1)解码模块:需要获取出正确的 ColorSpace
、ColorRange
,然后传递给后面的模块。
CVPixelBuffer
,ColorSpace 为 CVImageBufferYCbCrMatrix
,ColorRange 在 iOS15 后包含了 FullRange
参数,之前需要根据 CVPixelBufferGetPixelFormatType(buffer) == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
判断。MediaFormat
获取,ColorSpace 为 KEY_COLOR_STANDARD
,ColorRange 为 KEY_COLOR_RANGE
。colorspace
,ColorRange 为 color_range
。2)编码模块:根据外层输入的 ColorSpace
、ColorRange
,设置给编码器即可。
kVTCompressionPropertyKey_YCbCrMatrix
。KEY_COLOR_STANDARD
、KEY_COLOR_RANGE
。vui.b_fullrange
、vui.i_colmatrix
。3)YUV 数据转换 RGBA 纹理模块。
4)RGBA 纹理转换 YUV 数据模块。
5)RGB 数据与 YUV 数据转换模块。
libyuv
,例如 I420 转换 RGBA,方法为 I420ToARGBMatrix
,参数支持设置矩阵 YuvConstants
。3、其他建议
1)尽量减少自定义处理颜色空间转换。
Android 平台尽量使用 Surface 解码与编码,好处就是不需要手动处理。
2)ByteBuffer 编码必须设置 ColorSpace
、ColorRange
。
如果不设置底层不清楚输入进来的数据颜色格式,只能根据默认值随意发挥了。
3)ColorSpace
、ColorRange
默认值。
ColorSpace
为空,则直接默认为 601
即可。ColorRange
为空,则直接默认为非 FullRange
。[1]
CPUImage: https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageColorConversion.m
[2]
libyuv: https://github.com/lemenkov/libyuv/blob/master/source/row_common.cc
[3]
RGB2YUV: https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV444_to_RGB888_conversion