由于缓冲区不足,请适当释放AVCaptureDataOutputSynchronizerDelegate中的缓冲区。

4
我正在使用AVCaptureDataOutputSynchronizerDelegate来处理视频、深度和元数据的捕获数据。
    private let videoDataOutput = AVCaptureVideoDataOutput()
    private let depthDataOutput = AVCaptureDepthDataOutput()
    private let metadataOutput = AVCaptureMetadataOutput()

所以使用下面的代码,我能够从AVCaptureDataOutputSynchronizerDelegate方法中获取特定的视频数据。

func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) {

    guard let syncedVideoData = synchronizedDataCollection.synchronizedData(for: self.videoDataOutput) as? AVCaptureSynchronizedSampleBufferData else { return }

问题是,当我尝试将videoData保存到数组中时,我会遇到OutOfBuffers错误。如果我尝试保存videoData/相关联的图像/与此数据相关的任何内容,这个问题就会持续存在。
let array:[CMSampleBuffer] = []

...

array.append(syncedVideoData)
//Gets to about 5-6 sets of data, then it runs out of buffers. 
//I think the buffer is being retained permanently since I am saving to a global variable here.
//Leading to out of buffer error

所以,我认为正在发生的是,由于将任何相关数据保存到数组中,它将保留内存中缓冲区中的数据,而通常会释放它。 早先链接的OutOfBuffers网页指出,我可以:
如果需要对捕获的数据执行扩展处理,请将该数据复制到您管理其寿命的缓冲区中,而不是依赖于由捕获输出供应的缓冲区。
我尝试创建一个新的CMSampleBuffer
extension VideoCapture: AVCaptureDataOutputSynchronizerDelegate {

func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) {

    var newData:CMSampleBuffer?

    guard let syncedVideoData = synchronizedDataCollection.synchronizedData(for: self.videoDataOutput) as? AVCaptureSynchronizedSampleBufferData else { return }
    guard !syncedVideoData.sampleBufferWasDropped else {
        print(syncedVideoData.droppedReason.rawValue)
        return
    }
    let videoSampleBuffer = syncedVideoData.sampleBuffer

    CMSampleBufferCreateCopy(allocator: kCFAllocatorDefault, sampleBuffer: videoSampleBuffer, sampleBufferOut: &newData)
    if(newData != nil) {
        self.buffer.append(newData!)
    }
}

但这会导致相同的问题--视频数据仍然停留在缓冲区中。我大约获得了5-6组视频数据,然后就没有了更多数据。

如何"将该数据复制到您管理生命周期的缓冲区中,而不是依赖于由捕获输出供应的缓冲区",有什么指导意见吗?就像outOfBuffers网站上所示的那样?

1个回答

2
我能够按照苹果文档中的这个缓冲区这个指南以及其他一些指南的方法,创建一个缓冲区。最初的回答。
...
guard let imagePixelBuffer = CMSampleBufferGetImageBuffer(videoSampleBuffer) else { fatalError() }


//First lock buffer
CVPixelBufferLockBaseAddress(imagePixelBuffer,
                                     CVPixelBufferLockFlags.readOnly)

//Do something with buffer
self.buffer = createMyBuffer(pixelBuffer: imagePixelBuffer)

//Unlock buffer
CVPixelBufferUnlockBaseAddress(imagePixelBuffer,
                                       CVPixelBufferLockFlags.readOnly)
self.doSomething(self.buffer)

...

func createMyBuffer(pixelBuffer: CVPixelBuffer) -> CVPixelBuffer? {
    let scaleWidth:Int = CVPixelBufferGetWidth(pixelBuffer)
    let scaleHeight:Int = CVPixelBufferGetHeight(pixelBuffer)

    let flags = CVPixelBufferLockFlags(rawValue: 0)
    guard kCVReturnSuccess == CVPixelBufferLockBaseAddress(pixelBuffer, flags) else {
        return nil
    }

    defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, flags) }

    guard let srcData = CVPixelBufferGetBaseAddress(pixelBuffer) else {
        print("Error: could not get pixel buffer base address")
        return nil
    }

    let srcBytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
    var srcBuffer = vImage_Buffer(data: srcData,
                                      height: vImagePixelCount(CVPixelBufferGetHeight(pixelBuffer)),
                                      width: vImagePixelCount(CVPixelBufferGetWidth(pixelBuffer)),
                                      rowBytes: srcBytesPerRow)

    let destBytesPerRow = scaleWidth*4
    guard let destData = malloc(scaleHeight*destBytesPerRow) else {
        print("Error: out of memory")
        return nil
    }

    var destBuffer = vImage_Buffer(data: destData,
                                       height: vImagePixelCount(scaleHeight),
                                       width: vImagePixelCount(scaleWidth),
                                       rowBytes: destBytesPerRow)

    let error = vImageScale_ARGB8888(&srcBuffer, &destBuffer, nil, vImage_Flags(kvImageLeaveAlphaUnchanged))
    if error != kvImageNoError {
        print("Error:", error)
        free(destData)
        return nil
    }

    let releaseCallback: CVPixelBufferReleaseBytesCallback = { _, ptr in
        if let ptr = ptr {
            free(UnsafeMutableRawPointer(mutating: ptr))
        }
    }

    let pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)
    var dstPixelBuffer: CVPixelBuffer?
    let status = CVPixelBufferCreateWithBytes(nil, scaleWidth, scaleHeight,
                                                  pixelFormat, destData,
                                                  destBytesPerRow, releaseCallback,
                                                  nil, nil, &dstPixelBuffer)
    if status != kCVReturnSuccess {
        print("Error: could not create new pixel buffer")
        free(destData)
        return nil
    }
    return dstPixelBuffer
}

这个方法是可行的,但似乎有些冗余。我使用了一个函数来“缩放”缓冲区,但我只是将其缩放到与当前缓冲区完全相同的大小,并返回一个新的缓冲区,当我选择时删除它。虽然重复,但功能是有效的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接