AVFoundation captureOutput didOutputSampleBuffer 延迟

Giz*_*odo 5 avfoundation ios cmsamplebuffer swift avcaptureoutput

我正在使用AVFoundation captureOutput didOutputSampleBuffer提取图像,然后用于过滤器。

\n\n
  self.bufferFrameQueue = DispatchQueue(label: "bufferFrame queue", qos: DispatchQoS.background,  attributes: [], autoreleaseFrequency: .inherit)\n\n  self.videoDataOutput = AVCaptureVideoDataOutput()\n  if self.session.canAddOutput(self.videoDataOutput) {\n       self.session.addOutput(videoDataOutput)\n       self.videoDataOutput!.alwaysDiscardsLateVideoFrames = true\n       self.videoDataOutput!.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]\n           self.videoDataOutput!.setSampleBufferDelegate(self, queue: self.bufferFrameQueue)\n  }\n\n\n\n func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {\n\n        connection.videoOrientation = .portrait\n\n        let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!\n        let ciImage = CIImage(cvPixelBuffer: pixelBuffer)\n\n        DispatchQueue.main.async {\n            self.cameraBufferImage = ciImage\n        }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

上面只是更新了self.cameraBufferImage上面只是在有新的输出样本缓冲区时

\n\n

然后,当按下滤镜按钮时,我使用self.cameraBufferImage如下:

\n\n
  func filterButtonPressed() {\n\n    if var inputImage = self.cameraBufferImage {\n\n      if let currentFilter = CIFilter(name: "CISepiaTone") {\n        currentFilter.setValue(inputImage, forKey: "inputImage")\n        currentFilter.setValue(1, forKey: "inputIntensity")\n        if let output = currentFilter.outputImage {\n          if let cgimg = self.context.createCGImage(output, from: inputImage.extent) {\n\n            self.filterImageLayer = CALayer()\n            self.filterImageLayer!.frame = self.imagePreviewView.bounds\n            self.filterImageLayer!.contents = cgimg\n            self.filterImageLayer!.contentsGravity = kCAGravityResizeAspectFill\n            self.imagePreviewView.layer.addSublayer(self.filterImageLayer!)\n\n          }\n        }\n      }\n    }\n  }\n
Run Code Online (Sandbox Code Playgroud)\n\n

当调用上述方法时,它会获取“当前” self.cameraBufferImage并使用它来应用滤镜。这在正常曝光时间(低于 1/15 秒左右...)下效果很好。

\n\n

问题

\n\n

当曝光时间较慢(即 1/3 秒)时,需要一段时间(约 1/3 秒)才能应用滤镜。这种延迟仅在启动后第一次出现。如果再做一次,根本就没有耽搁。

\n\n
\n\n

想法

\n\n

据我了解,如果曝光持续时间为 1/3 秒,则 didOutputSampleBuffer 仅每 1/3 秒更新一次。然而,为什么会出现最初的延迟呢?难道它不应该只获取当时可用的self.cameraBufferImage而不是等待吗?

\n\n
    \n
  1. 队列问题?
  2. \n
  3. CMSampleBuffer 保留问题?(虽然在 Swift 3 上,没有 CFRetain)
  4. \n
\n\n
\n\n

更新

\n\n

苹果的文档

\n\n
\n

每当输出捕获并输出新的视频帧时,代表就会收到此消息,并按照其 videoSettings 属性的指定对其进行解码或重新编码。代表们可以将提供的视频帧\n 与其他 API 结合使用以进行进一步处理。

\n\n

在由output\xe2\x80\x99s\nsampleBufferCallbackQueue 属性指定的调度队列上调用此方法。它会定期调用,因此必须有效防止捕获性能问题,包括丢帧。

\n\n

如果您需要在此方法范围之外引用 CMSampleBuffer 对象,则必须 CFRetain 它,然后在使用完毕后 CFRelease 它。

\n\n

为了保持最佳性能,一些示例缓冲区直接引用设备系统和其他捕获输入可能需要重用的内存池。这通常是未压缩设备本机捕获的情况,其中内存块被复制得尽可能少。如果多个样本缓冲区引用此类内存池的时间过长,则输入将无法再将新样本复制到内存中,并且这些样本将被删除。

\n\n

如果您的应用程序因保留提供的 CMSampleBuffer 对象时间过长而导致样本被删除,但它需要长时间访问样本数据,请考虑将数据复制到新的缓冲区中,然后释放样本缓冲区(如果之前保留过),以便可以重用它引用的内存。

\n
\n