如何通过AVFoundation从视频流获取原始格式的图像缓冲区?
如何通过AVFoundation从视频流获取原始格式的图像缓冲区?
好的,我来一步步帮你解决这个问题——用AVFoundation获取视频流里的原始格式图像缓冲区,核心就是用好AVAssetReaderTrackOutput的配置,结合你提到的文档细节,我整理了具体的实现步骤、代码示例和关键注意事项:
核心配置要点:设置outputSettings为nil
首先要明确Apple官方文档里的关键说明:
将
AVAssetReaderTrackOutput实例化时的outputSettings参数设为nil,可配置输出使其以指定轨道存储的原始格式提供样本。
这是获取原始格式的核心,因为如果传入具体的输出设置字典,AVFoundation会自动对样本进行转码,就得不到原始格式的缓冲区了。
具体实现步骤
1. 准备资源和轨道
先获取目标视频的AVAsset(比如本地文件用AVURLAsset),然后提取其中的视频轨道:
let asset = AVURLAsset(url: yourVideoURL) guard let videoTrack = asset.tracks(withMediaType: .video).first else { // 处理无视频轨道的错误 return }
2. 初始化阅读器和输出对象
创建AVAssetReader,并实例化AVAssetReaderTrackOutput时传入nil作为outputSettings,再将输出对象添加到阅读器:
do { let reader = try AVAssetReader(asset: asset) let trackOutput = AVAssetReaderTrackOutput(assetReaderTrack: videoTrack, outputSettings: nil) if reader.canAdd(trackOutput) { reader.add(trackOutput) } else { // 处理无法添加输出的错误 return } } catch { // 处理阅读器初始化错误 print("阅读器初始化失败:\(error.localizedDescription)") }
3. 读取原始样本并提取图像缓冲区
启动阅读器后,循环读取样本缓冲区,从中提取原始格式的CVImageBuffer:
reader.startReading() while reader.status == .reading { guard let sampleBuffer = trackOutput.copyNextSampleBuffer() else { // 没有更多样本,退出循环 break } // 从样本缓冲区中获取原始格式的图像缓冲区 if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { // 这里就是你要的原始格式图像缓冲区,可以做后续处理 // 比如查看像素格式: let pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer) print("原始像素格式:\(pixelFormat)") } // 记得释放样本缓冲区,避免内存泄漏 CMSampleBufferInvalidate(sampleBuffer) CFRelease(sampleBuffer) } // 处理读取结束后的状态 switch reader.status { case .completed: print("读取完成") case .failed: print("读取失败:\(reader.error?.localizedDescription ?? "未知错误")") default: break }
重点注意:解码顺序 vs 显示顺序
你提到的MP4视频按解码顺序输出的情况确实存在——当使用原始格式输出时,AVFoundation会直接按视频文件中存储的解码顺序返回样本(因为B帧需要依赖前后帧解码,所以存储顺序是解码顺序),而不是我们通常需要的显示顺序。
如果需要转换成显示顺序,有两种方案:
- 方案一(放弃原始格式):给
outputSettings传入具体的格式配置(比如指定像素格式为kCVPixelFormatType_32BGRA),这时AVFoundation会自动帮你转码并按显示顺序输出,但代价是不再是原始格式。 - 方案二(保留原始格式):自己根据每个样本的显示时间戳排序。每个
CMSampleBuffer都可以通过CMSampleBufferGetPresentationTimeStamp()获取显示时间戳,你可以收集所有样本后按时间戳排序,再按顺序处理。
额外注意事项
- 权限问题:如果读取的是相册内的视频,需要提前申请照片访问权限;读取本地文件要确保路径正确且有访问权限。
- 内存管理:务必在处理完
CMSampleBuffer后调用CMSampleBufferInvalidate和CFRelease,否则会造成严重的内存泄漏。 - 原始格式兼容性:不同视频的原始像素格式可能不同,比如H.264通常是
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange或kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,处理时要注意格式兼容。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

