You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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 启动流程

确保启动顺序无差错,避免异步时序冲突:

  1. 初始化 AVAssetWriter 并指定输出路径
  2. 将 AVAssetWriterInput 添加到 writer
  3. 调用 writer.startWriting()
  4. 调用 writer.startSession(atSourceTime: CMTime.zero)
  5. 触发 input 的媒体数据请求回调

4. 增加偶发失败的重试机制

检测到 pixelBufferPool 为 nil 时,清理临时文件后重新初始化写入流程:

func retryVideoExport() {
    // 清理之前的临时输出文件
    try? FileManager.default.removeItem(at: outputURL)
    
    // 重新执行writer、input、adaptor的初始化与写入流程
    // ... 你的初始化代码
}

额外注意事项

  • 所有写入操作放在同一个串行队列执行,避免多线程竞争导致状态异常
  • 确保所有 UIImage 的尺寸与输入配置的宽高一致,尺寸不匹配也会导致缓冲池初始化失败
  • 写入完成后必须调用 finishWriting(completionHandler:) 释放资源

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 02:16:04