最近在使用WebRTC进行直播时,遇到了主播画面曝光过度的问题,查了很久原因才查到,估分享出来避免大家采坑
- iOS中的像素格式
iOS视频采集支持三种数据格式输出:420v,420f,BGRA
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange = '420f', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */
kCVPixelFormatType_32BGRA = 'BGRA', /* 32 bit BGRA */
其中kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange中的YpCbCr分别指Y、U、V三个分量,即YUV格式的数据,后面的8指以8bit来保存一个分量,420指使用YUV的4:2:0格式存储。BiPlanar指双平面模式,即将Y和UV分开存储,VideoRange指颜色空间
420f和420v都是YUV格式的。YUV是一种颜色编码方法,分为三个分量,Y表示亮度(Luma),也称为灰度。U和V表示色度(chroma)描述色彩与饱和度 用于WebRTC编解码的像素格式是kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 420f和420v的区别在于Color Space。f指Full Range,v指Video Range。 Full Range的Y分量取值范围是[0,255] Video Range的Y分量取值范围是[16,235] 从采集编码到解码渲染,整个过程中,颜色空间的设置都必须保持一致,如果采集用了Full Range 而播放端用Video Range,那么就有可能看到曝光过度的效果。
- WebRTC70 branch会产生画面曝光过度问题原因
在WebRTC70 branch中,使用了iOS的新的activeFormat设置fps等formate格式,我们在选取AVCaptureDeviceFormat时,只判断了分辨率和帧率,未判断OutputPixelFormat,所以会选择第一个符合分辨率和帧率的formate格式进行设置,是Video Range格式,
Format: <AVCaptureDeviceFormat: 0x174006680 'vide'/'420v' 1280x 720, { 3-120 fps}, fov:58.080, binned, supports vis, max zoom:52.00 (upscales @1.16), AF System:1, ISO:34.0-1088.0, SS:0.000012-0.333333>
Format: <AVCaptureDeviceFormat: 0x174006680 'vide'/'420f' 1280x 720, { 3-120 fps}, fov:58.080, binned, supports vis, max zoom:52.00 (upscales @1.16), AF System:1, ISO:34.0-1088.0, SS:0.000012-0.333333>
因为iOS系统formate数组中取到的数组中包含420v和420f格式,所以要进行选择,WebRTC是内部使用kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,所以要选取420f, 如果选择错误,因Full Range和Video Range的Y分量取值范围不同,会导致后续美颜,和显示对曝光值进行错误处理,从而导致曝光异常
修改代码
for (AVCaptureDeviceFormat *format in formats) {
CMVideoDimensions dimension = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
if ((dimension.width == targetWidth &&
dimension.height == targetHeight) || (dimension.width == targetHeight && dimension.height == targetWidth)) {
for (AVFrameRateRange* frameRateRange in
[format videoSupportedFrameRateRanges]) {
if (frameRateRange.minFrameRate <= self.fps &&
self.fps <= frameRateRange.maxFrameRate) {
if (CMFormatDescriptionGetMediaSubType(format.formatDescription) == [self preferredOutputPixelFormat]) {
selectedFormat = format;
}
break;
}
}
}
if(selectedFormat){
break;
}
}