获取YpCbCr8BiPlanar格式CVPixelBuffer亮度数据时函数崩溃求助
问题分析:CVPixelBuffer平均亮度计算时EXC_BAD_ACCESS崩溃
我尝试获取CVPixelBuffer的averageLuminance(平均亮度),但函数在处理若干行后因EXC_BAD_ACCESS错误崩溃。我认为自己对苹果YpCbCr8BiPlanar格式的使用存在误解,以下是我的实现函数,请问问题出在哪里?
func averageLuminance(pixelBuffer: CVPixelBuffer) -> Float? { guard CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarFullRange else { print("Pixel buffer format mismatch.") return nil } CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) } guard let baseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) else { print("Unable to get base address.") return nil } let lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0) let lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0) let lumaRowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) var totalLuminance: Float = 0.0 for row in 0..<lumaHeight { let rowBaseAddress = baseAddress.advanced(by: row * lumaRowBytes) let rowPointer = rowBaseAddress.assumingMemoryBound(to: UInt8.self) var rowSum: Float = 0.0 for col in 0..<lumaWidth { rowSum += Float(rowPointer[col]) } let rowAverageLuminance = rowSum / Float(lumaWidth) totalLuminance += rowAverageLuminance } let averageLuminance = totalLuminance / Float(lumaHeight) return averageLuminance }
问题根源
你误用了有损压缩的YUV格式kCVPixelFormatType_Lossy_420YpCbCr8BiPlanarFullRange:
这个格式的内存存储的是H.264/HEVC编码后的压缩数据,并非原始的逐像素亮度字节数组。直接把它当成原始Y平面数据访问,必然会超出有效内存范围,触发EXC_BAD_ACCESS错误。
你的内存访问逻辑本身是正确的,但只适用于无损原始YUV格式,比如kCVPixelFormatType_420YpCbCr8BiPlanarFullRange或kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange。
解决方案
方案一:切换到无损原始YUV格式
修改格式校验逻辑,匹配原始未压缩的平面YUV格式:
func averageLuminance(pixelBuffer: CVPixelBuffer) -> Float? { let supportedFormats: [OSType] = [ kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ] guard supportedFormats.contains(CVPixelBufferGetPixelFormatType(pixelBuffer)) else { print("Pixel buffer format mismatch.") return nil } CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) } guard let baseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) else { print("Unable to get base address.") return nil } let lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0) let lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0) let lumaRowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) var totalLuminance: Float = 0.0 for row in 0..<lumaHeight { let rowBaseAddress = baseAddress.advanced(by: row * lumaRowBytes) let rowPointer = rowBaseAddress.assumingMemoryBound(to: UInt8.self) var rowSum: Float = 0.0 for col in 0..<lumaWidth { rowSum += Float(rowPointer[col]) } totalLuminance += rowSum / Float(lumaWidth) } return totalLuminance / Float(lumaHeight) }
方案二:解码Lossy格式为原始YUV(若必须处理该格式)
如果输入的pixelBuffer确实是Lossy格式,需要先用VTDecompressionSession解码压缩数据,得到原始YUV buffer后再计算亮度。核心步骤:
- 创建视频解码会话并配置参数
- 将Lossy格式的pixelBuffer送入解码器
- 获取解码后的原始YUV buffer,再调用上述亮度计算逻辑
性能优化建议
双重循环计算效率较低,可以用苹果Accelerate框架的vDSP库加速求和:
import Accelerate func averageLuminance(pixelBuffer: CVPixelBuffer) -> Float? { let supportedFormats: [OSType] = [ kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ] guard supportedFormats.contains(CVPixelBufferGetPixelFormatType(pixelBuffer)) else { print("Pixel buffer format mismatch.") return nil } CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) } guard let baseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) else { print("Unable to get base address.") return nil } let lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0) let lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0) let lumaRowBytes = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) var totalSum: Float = 0.0 let totalPixels = Float(lumaWidth * lumaHeight) for row in 0..<lumaHeight { let rowAddress = baseAddress.advanced(by: row * lumaRowBytes).assumingMemoryBound(to: UInt8.self) var rowSum: Float = 0.0 // 用vDSP快速计算一行的总和 vDSP_sve(rowAddress, 1, &rowSum, vDSP_Length(lumaWidth)) totalSum += rowSum } return totalSum / totalPixels }
内容的提问来源于stack exchange,提问作者houdini w
相关产品推荐
相关产品推荐

