我有两个解决方案转换CVPixelBuffer
为UIImage
.
- 使用 CIImage 及其上下文
第一个解决方案是使用CIImage
and CIContext
。
func readAssetAndCache(completion: @escaping () -> Void) {
/** Setup AVAssetReader **/
reader.startReading()
let context = CIContext()
while let sampleBuffer = generator.trackoutput.copyNextSampleBuffer() {
autoreleasepool {
guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let ciImage = CIImage(cvImageBuffer: imageBuffer)
guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return }
if let image = UIImage(cgImage: cgImage) {
CacheManager.default.cache(image: image, forKey: "CachedImages") {
completion()
}
}
}
}
}
- 使用 VTCreateCGImageFromCVPixelBuffer
代码看起来像这样。
func readAssetAndCache(completion: @escaping () -> Void) {
/** Setup AVAssetReader **/
reader.startReading()
while let sampleBuffer = generator.trackoutput.copyNextSampleBuffer() {
autoreleasepool {
guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
if let image = UIImage(pixelBuffer: imageBuffer,
scale: 1.0,
orientation: self.imageOrientation) {
CacheManager.default.cache(image: image, forKey: "CachedImages") {
completion()
}
}
}
}
}
}
在此解决方案中,我使用此扩展方法。
import UIKit
import VideoToolbox
extension UIImage {
public convenience init?(pixelBuffer: CVPixelBuffer, scale: CGFloat, orientation: UIImageOrientation) {
var image: CGImage?
VTCreateCGImageFromCVPixelBuffer(pixelBuffer, nil, &image)
guard let cgImage = image else { return nil }
self.init(cgImage: cgImage, scale: scale, orientation: orientation)
}
}
从视频中提取图像的整个过程如下。
全过程
- 串行提取所有帧。
- 使用 .将每个图像同时缓存在磁盘中
OperationQueue
。
问题
当图像同时缓存时会出现问题,使用CIContext
及其createCGImage(ciImage:rect:)
. 在第一个场景中所有缓存过程结束之前,内存占用会越来越高。但是,第二种方式,它会在每个 workItem 完成时释放它的内存。
我真的不知道为什么。
这是我与此问题相关的缓存方法。
func cache(image: UIImage, forKey key: String, completion: @escaping () -> Void) {
let operation = BlockOperation(block: { [weak self] in
guard let `self` = self else { return }
self.diskCache(image: image)
})
operation.completionBlock = completion
operationQueue.addOperation(operation)
}
func diskCache(image: UIImage) {
let data = UIImageJPEGRepresentation(image, 1.0)
let key = "\(self.identifierPrefix).jpg"
FileManager.default.createFile(atPath: self.cachePath.appendingPathComponent(key).path,
contents: data, attributes: nil)
}