我需要从视频的每个帧中提取UIImage,并在UICollectionView中使用这些图像。我的问题是图像生成过程花费的时间太长。如何让这个更快,以便用户看不到图像加载?
这些图像显示在一个视图中,该视图有两个UICollectionViews:一个大的分页视图和一个小的缩略图大小的轮播,所以我为每个集合视图生成两个图像。当使用集合视图进入视图时,我立即开始生成图像,但是每个单元格生成图像时可能需要长达2秒才能生成图像(屏幕上只有1.5个大单元格和5-7个缩略图单元格)。滚动时,我看到空单元格太久(再次长达2秒)。在不同的场景中,我使用来自Photos框架的图像填充这些集合视图,这些图像表现出色。在用户选择使用集合视图进入视图之前,我无法预先加载图像,因为如果用户不选择输入,则可能不需要加载图像。
我以前使用AVAssetImageGenerator来提取图像,但经过一些研究后,我了解到该方法效率不高,并转而使用以下方法。
我确实在集合视图中实现了图像缓存,但这只有在图像已经慢慢生成后才有用。
用于从视频生成图像的集合视图单元调用的代码。
public func image(at index: Int, resizedTo targetSize: CGSize = CGSize(), completed: @escaping (Int, UIImage?, Error?) -> ()) {
if sampleBuffers.isEmpty {
self.loadSampleBuffers()
}
DispatchQueue.global().async {
let pixelBuffer = CMSampleBufferGetImageBuffer(self.sampleBuffers[index])
let ciImage = CIImage(cvImageBuffer: pixelBuffer!)
if targetSize == CGSize.zero {
let context = CIContext()
let cgImage = context.createCGImage(ciImage, from: ciImage.extent)
let image = UIImage(cgImage: cgImage!)
DispatchQueue.main.async {
completed(index, image.rotate(), nil)
}
} else {
let image = UIImage(ciImage: ciImage)
let rect = AVMakeRect(aspectRatio: image.size, insideRect: CGRect(origin: CGPoint(), size: targetSize))
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
image.draw(in:rect)
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
DispatchQueue.main.async {
completed(index, resizedImage, nil)
}
}
}
}
以下是加载样本缓冲区的功能:
public func loadSampleBuffers() {
var reader: AVAssetReader! = nil
do {
reader = try AVAssetReader(asset: self._asset)
} catch {
print("could not initialize reader.")
return
}
let readerOutputSettings: [String: Any] = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
let readerOutput = AVAssetReaderTrackOutput(track: self._track, outputSettings: readerOutputSettings)
reader.add(readerOutput)
reader.startReading()
// read in samples
var samples: [CMSampleBuffer] = []
while let sample = readerOutput.copyNextSampleBuffer() {
samples.append(sample)
}
self.sampleBuffers = samples
self.frameCount = samples.count
}