我正在使用AVFoundation captureOutput didOutputSampleBuffer来提取图像,然后用于过滤器。
self.bufferFrameQueue = DispatchQueue(label: "bufferFrame queue", qos: DispatchQoS.background, attributes: [], autoreleaseFrequency: .inherit)
self.videoDataOutput = AVCaptureVideoDataOutput()
if self.session.canAddOutput(self.videoDataOutput) {
self.session.addOutput(videoDataOutput)
self.videoDataOutput!.alwaysDiscardsLateVideoFrames = true
self.videoDataOutput!.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
self.videoDataOutput!.setSampleBufferDelegate(self, queue: self.bufferFrameQueue)
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
connection.videoOrientation = .portrait
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
DispatchQueue.main.async {
self.cameraBufferImage = ciImage
}
}
上面只是更新self.cameraBufferImage每当有一个新的输出样本缓冲区。
然后,当按下过滤器按钮时,我使用self.cameraBufferImage如下:
func filterButtonPressed() {
if var inputImage = self.cameraBufferImage {
if let currentFilter = CIFilter(name: "CISepiaTone") {
currentFilter.setValue(inputImage, forKey: "inputImage")
currentFilter.setValue(1, forKey: "inputIntensity")
if let output = currentFilter.outputImage {
if let cgimg = self.context.createCGImage(output, from: inputImage.extent) {
self.filterImageLayer = CALayer()
self.filterImageLayer!.frame = self.imagePreviewView.bounds
self.filterImageLayer!.contents = cgimg
self.filterImageLayer!.contentsGravity = kCAGravityResizeAspectFill
self.imagePreviewView.layer.addSublayer(self.filterImageLayer!)
}
}
}
}
}
调用上述方法时,它会抓取“当前” self.cameraBufferImage并使用它来应用过滤器。这在正常曝光持续时间(低于 1/15 秒左右......)中工作正常
问题
当曝光时间较慢时,即 1/3 秒时,应用滤镜需要一段时间(约 1/3 秒)。此延迟仅在启动后第一次出现。如果再做一次,就完全没有延迟了。
想法
我了解如果曝光持续时间为 1/3 秒,didOutputSampleBuffer 仅每 1/3 秒更新一次。但是,为什么会出现初始延迟?它不应该只是抓住那个确切时间可用的任何self.cameraBufferImage,而不是等待吗?
- 排队问题?
- CMSampleBuffer 保留问题?(虽然在 Swift 3 上,没有 CFRetain)
更新
每当输出捕获并输出新的视频帧、解码或重新编码由其 videoSettings 属性指定时,代表都会收到此消息。代表可以将提供的视频帧与其他 API 结合使用以进行进一步处理。
此方法在输出的 sampleBufferCallbackQueue 属性指定的调度队列上调用。它会定期调用,因此必须有效地防止捕获性能问题,包括丢帧。
如果您需要在此方法范围之外引用 CMSampleBuffer 对象,则必须先对其进行 CFRetain,然后在完成后对其进行 CFRelease。
为了保持最佳性能,一些样本缓冲区直接引用可能需要由设备系统和其他捕获输入重用的内存池。对于未压缩的设备本机捕获,这种情况经常发生,其中内存块被复制得越少越好。如果多个样本缓冲区引用此类内存池的时间过长,则输入将不再能够将新样本复制到内存中,并且这些样本将被丢弃。
如果您的应用程序由于保留提供的 CMSampleBuffer 对象太久而导致样本被丢弃,但它需要长时间访问样本数据,请考虑将数据复制到新缓冲区中,然后释放样本缓冲区(如果它以前被保留)以便它引用的内存可以被重用。