You need to enable JavaScript to run this app. 频繁绘制[UInt8]生成的CGImage到CGContext时iOS App崩溃,如何实现CGImage数组转视频
优惠活动
大模型
产品
解决方案
定价
更多

频繁绘制[UInt8]生成的CGImage到CGContext时iOS App崩溃,如何实现CGImage数组转视频

从[UInt8]数据生成视频随机崩溃的核心问题与修复方案

你的实现方向本身没有问题:通过CGImage数组转视频是iOS端生成录制视频的通用标准方案,不需要整体重构换路径,当前的随机崩溃完全是几个具体的代码逻辑漏洞导致的:

核心崩溃原因逐点拆解

  • 第一类:CGDataProvider的野指针内存问题(90%概率是主因)

    你当前创建CGDataProvider时的释放闭包是空实现:

    let provider = CGDataProvider( dataInfo: nil,
                                   data: data,
                                   size: totalBytes,
                                   releaseData: {_, _, _  in })!
    
    这里直接把外部[UInt8]数组的裸内存指针传给了provider,没有做任何内存持有保护。当你把生成的CGImage存在数组里、放到后台队列逐帧渲染时,原有的data变量可能已经被ARC回收,后续context.draw操作会读取已经被系统释放的内存地址,自然会随机崩溃。这也是为什么Bundle内置的图片生成视频正常——这些图片的内存是系统管理的,不会出现裸指针野读的问题。
  • 第二类:位图格式和渲染上下文不匹配
    你创建CGImage时根据通道数动态切换alpha信息:3通道用CGImageAlphaInfo.none、4通道用CGImageAlphaInfo.last,但后续创建CVPixelBuffer、渲染上下文时固定用了kCVPixelFormatType_32ARGB、对应CGImageAlphaInfo.noneSkipFirst的布局。两种位图通道顺序不一致时,CoreGraphics会按预期的内存布局去读图像数据,一旦读超出图像内存边界就会触发崩溃。
  • 第三类:队列逻辑和强解包的崩溃风险

    你当前给媒体写入队列的命名是DispatchQueue.init(label: "Main"),同时在回调里用while(true)空转等待写入就绪,还直接用temp.cgImage!强解包:

    sampleBuffer = self.newPixelBufferFrom(cgImage: temp.cgImage!)
    
    只要任意一帧因为前面的内存问题生成的cgImage是nil,强解包会直接触发崩溃;空转循环也会导致队列CPU占用过高,抢占主线程资源。
  • 第四类:冗余转换增加内存压力
    你当前流程是[UInt8] → CGImage → UIImage → 取UIImage.cgImage,中间多了一层UIImage的容器转换,不仅增加了不必要的内存占用,还会引入UIImage的延迟解码机制——在后台队列解码大图时很容易触发内存警告,进一步增加崩溃概率。

可直接落地的正确实现方案

  • 从[UInt8]生成CGImage时,先把数据拷贝为独立的CFData实例再传给CGDataProvider,完全切断对外部原数组内存的依赖,让provider自己管理图像内存的生命周期,从根源上避免野指针。
  • 统一图像和PixelBuffer的位图格式:不管原数据是3通道还是4通道,统一输出为32位ARGB布局,和CVPixelBuffer的格式完全对齐,避免通道错位的越界读取。
  • 跳过UIImage的中间转换步骤,直接把生成的CGImage传给newPixelBufferFrom函数转成CVPixelBuffer,减少不必要的内存拷贝和对象转换。
  • 替换媒体写入逻辑:用独立的后台串行队列做视频写入,去掉while(true)空转,每次writeInput.isReadyForMoreMediaData为true时只处理一帧就退出回调,所有可能为nil的地方都加可选判断,失败就跳过当前帧,不要强解包。
  • 视频宽高设置为16的倍数,避免H.264编码时的兼容性问题。

快速验证修复效果的小步骤

先只把创建CGDataProvider的逻辑改成下面的写法,再跑原来的流程:

// 把裸指针传值改成拷贝完整内存
let imageData = Data(data)
guard let provider = CGDataProvider(data: imageData as CFData) else { return nil }

如果修改后随机崩溃消失,就可以100%确认是之前的裸指针内存问题导致的崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.03 23:15:11