1.
获取当前渲染的图像帧数据要想保存视频,最重要一点就是得到当前渲染好的帧数据。得到帧数据后,下面的工作交给AVFoundation就可以轻松搞定了。
那么,如何得到当前画面的帧数据呢?
可以看到渲染的视图ARSCNView最终是继承自UIView,从UIView截取画面是很容易的。但是这样得到的画面,分辨率和当前视图的frame是一致的,如果要保存高分辨率就得缩放,这样肯定会模糊。所以这个方法最先排除。
再来看看ARSCNView这个类,它的直接父类是SCNView。前面提到SceneKit是苹果自带的游戏框架,这个框架里面或许有API能直接获取。查找了相关资料,确实发现SCNRenderer有个snapshotAtTime:withSize:antialiasingMode:方法可以截取UIImage,而SCNRenderer所需要的场景scene属性可以从ARSCNView中直接获取。既然可以得到UIImage,就可以转换为CVPixelBufferRel扔给AVFoundation框架处理。
if let view = view as? ARSCNView {
guard let mtlDevice = MTLCreateSystemDefaultDevice() else {
logAR.message("ERROR:- This device does not support Metal")
return
}
renderEngine = SCNRenderer(device: mtlDevice, options: nil)
renderEngine.scene = view.scene
gpuLoop = CADisplayLink(target: WeakProxy(target: self),
selector: #selector(renderFrame))
gpuLoop.preferredFramesPerSecond = fps.rawValue
gpuLoop.add(to: .main, forMode: .common)
status = .readyToRecord
}
if view is ARSCNView {
guard let size = bufferSize else { return nil }
//UIScreen.main.bounds.size
var renderedFrame: UIImage?
pixelsQueue.sync {
renderedFrame = renderEngine.snapshot(atTime: self.time, with: size, antialiasingMode: .none)
}
if let _ = renderedFrame {
} else {
renderedFrame = renderEngine.snapshot(atTime: time, with: size, antialiasingMode: .none)
}
guard let buffer = renderedFrame!.buffer else { return nil }
return buffer
}