将UIImage转换为AVURLAsset视频时的可靠性问题排查
问题分析与解决方案
核心原因
pixelBufferPool 偶发为 nil,本质是 AVAssetWriterInput 未完全进入就绪状态就被访问,或是输入配置与 writer 配置不匹配导致适配器无法初始化缓冲池。即便调用了 startWriting() 和 startSession(atSourceTime:),input 的就绪状态仍可能存在异步延迟,这是偶发问题的主要诱因。
具体修复方案
1. 等待 Input 就绪后再访问缓冲池
不要在启动 session 后直接获取 pixelBufferPool,改用 requestMediaDataWhenReady(on:) 回调等待输入就绪,此时缓冲池大概率已初始化:
// 替换直接访问逻辑,通过回调等待input就绪 assetWriterInput.requestMediaDataWhenReady(on: DispatchQueue.global()) { [weak self] in guard let self = self, assetWriterInput.isReadyForMoreMediaData else { return } // 此时再尝试获取缓冲池 guard let pixelBufferPool = self.pixelBufferAdaptor.pixelBufferPool else { // 异常处理:取消写入或重试 self.assetWriter?.cancelWriting() return } // 开始遍历UIImage执行写入逻辑 for image in self.imageArray { // ... 你的像素缓冲区生成与写入代码 } }
2. 修正适配器与输入的配置匹配
确保 AVAssetWriterInputPixelBufferAdaptor 的像素格式、尺寸与 AVAssetWriterInput 完全匹配,同时开启实时数据预期:
// 正确的视频输入配置 let inputSettings: [String: Any] = [ AVVideoCodecKey: AVVideoCodecType.h264, AVVideoWidthKey: targetSize.width, AVVideoHeightKey: targetSize.height, AVVideoCompressionPropertiesKey: [ AVVideoAverageBitRateKey: 5000000, AVVideoMaxKeyFrameIntervalKey: 30 ] ] let assetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: inputSettings) assetWriterInput.expectsMediaDataInRealTime = true // 必须开启,否则适配器无法生成缓冲池 // 适配器配置要与输入像素格式一致 let adaptorSettings: [String: Any] = [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA ] let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: assetWriterInput, sourcePixelBufferAttributes: adaptorSettings)
3. 严格遵循 Writer 启动流程
确保启动顺序无差错,避免异步时序冲突:
- 初始化
AVAssetWriter并指定输出路径 - 将
AVAssetWriterInput添加到 writer - 调用
writer.startWriting() - 调用
writer.startSession(atSourceTime: CMTime.zero) - 触发 input 的媒体数据请求回调
4. 增加偶发失败的重试机制
检测到 pixelBufferPool 为 nil 时,清理临时文件后重新初始化写入流程:
func retryVideoExport() { // 清理之前的临时输出文件 try? FileManager.default.removeItem(at: outputURL) // 重新执行writer、input、adaptor的初始化与写入流程 // ... 你的初始化代码 }
额外注意事项
- 所有写入操作放在同一个串行队列执行,避免多线程竞争导致状态异常
- 确保所有
UIImage的尺寸与输入配置的宽高一致,尺寸不匹配也会导致缓冲池初始化失败 - 写入完成后必须调用
finishWriting(completionHandler:)释放资源
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

