从CMSampleBuffer中提取数据以创建深层副本

ben*_*yty 18 pool deep-copy ios cmsamplebuffer swift

我正在尝试在AVCaptureVideoDataOutputSampleBufferDelegate中创建captureOutput返回的CMSampleBuffer副本.

由于CMSampleBuffers来自预先分配的(15)缓冲池,如果我附加对它们的引用,则无法重新收集它们.这会导致所有剩余帧被丢弃.

为了保持最佳性能,某些样本缓冲区直接引用可能需要由设备系统和其他捕获输入重用的内存池.对于未压缩的设备本机捕获,通常会出现这种情况,其中尽可能少地复制内存块.如果多个样本缓冲区长时间引用此类内存池,则输入将无法再将新样本复制到内存中,并且这些样本将被丢弃.

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

显然我必须复制CMSampleBuffer,但CMSampleBufferCreateCopy()只会创建一个浅拷贝.因此,我得出结论,我必须使用CMSampleBufferCreate().我填写了12个!构造函数需要的参数但遇到了我的CMSampleBuffers不包含blockBuffer的问题(不完全确定那是什么,但似乎很重要).

这个问题已被问过好几次但没有回答.

CMImageBuffer或CVImageBuffer的深层副本在Swift 2.0中创建CMSampleBuffer的副本

一个可能的答案是"我终于想出如何使用它来创建一个深度克隆.所有复制方法都重用了堆中的数据,这些数据会保留AVCaptureSession.所以我不得不将数据拉入NSMutableData对象然后创建了一个新的样本缓冲区." 在SO上归功于Rob.但是,我不知道如何正确地做到这一点.

如果你有兴趣,是输出print(sampleBuffer).没有提到blockBuffer,又名CMSampleBufferGetDataBuffer返回nil.有一个imageBuffer,但使用CMSampleBufferCreateForImageBuffer创建"副本"似乎也没有释放CMSampleBuffer.


编辑:由于这个问题已经发布,我一直在尝试更多的方式来复制内存.

我做了用户Kametrixom尝试的相同的事情.是我尝试同样的想法,首先复制CVPixelBuffer然后使用CMSampleBufferCreateForImageBuffer创建最终的样本缓冲区.但是,这会导致两个错误之一:

  • memcpy指令上的EXC_BAD_ACCESS.AKA试图访问应用程序内存之外的段错误.
  • 或者,内存将成功复制,但CMSampleBufferCreateReadyWithImageBuffer()将失败,结果代码为-12743,"表示给定媒体的格式与给定的格式描述不匹配.例如,与CVImageBuffer配对的格式描述与CMVideoFormatDescriptionMatchesImageBuffer失败."

你可以看到Kametrixom和我都CMSampleBufferGetFormatDescription(sampleBuffer)试图复制源缓冲区的格式描述.因此,我不确定为什么给定媒体的格式与给定的格式描述不匹配.

Kam*_*xom 13

好吧,我想我终于明白了.我创建了一个帮助扩展来制作一个完整的副本CVPixelBuffer:

extension CVPixelBuffer {
    func copy() -> CVPixelBuffer {
        precondition(CFGetTypeID(self) == CVPixelBufferGetTypeID(), "copy() cannot be called on a non-CVPixelBuffer")

        var _copy : CVPixelBuffer?
        CVPixelBufferCreate(
            nil,
            CVPixelBufferGetWidth(self),
            CVPixelBufferGetHeight(self),
            CVPixelBufferGetPixelFormatType(self),
            CVBufferGetAttachments(self, kCVAttachmentMode_ShouldPropagate)?.takeUnretainedValue(),
            &_copy)

        guard let copy = _copy else { fatalError() }

        CVPixelBufferLockBaseAddress(self, kCVPixelBufferLock_ReadOnly)
        CVPixelBufferLockBaseAddress(copy, 0)

        for plane in 0..<CVPixelBufferGetPlaneCount(self) {
            let dest = CVPixelBufferGetBaseAddressOfPlane(copy, plane)
            let source = CVPixelBufferGetBaseAddressOfPlane(self, plane)
            let height = CVPixelBufferGetHeightOfPlane(self, plane)
            let bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(self, plane)

            memcpy(dest, source, height * bytesPerRow)
        }

        CVPixelBufferUnlockBaseAddress(copy, 0)
        CVPixelBufferUnlockBaseAddress(self, kCVPixelBufferLock_ReadOnly)

        return copy
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以在didOutputSampleBuffer方法中使用它:

guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

let copy = pixelBuffer.copy()

toProcess.append(copy)
Run Code Online (Sandbox Code Playgroud)

但请注意,一个像pixelBuffer占用大约3MB的内存(1080p),这意味着在100帧中你已经有大约300MB,这大约是iPhone说STAHP(和崩溃)的时间点.

请注意,您实际上并不想复制CMSampleBuffer它,因为它只包含一个,CVPixelBuffer因为它是一个图像.


小智 5

这是Swift 3评分最高的答案。

extension CVPixelBuffer {
func copy() -> CVPixelBuffer {
    precondition(CFGetTypeID(self) == CVPixelBufferGetTypeID(), "copy() cannot be called on a non-CVPixelBuffer")

    var _copy : CVPixelBuffer?
    CVPixelBufferCreate(
        kCFAllocatorDefault,
        CVPixelBufferGetWidth(self),
        CVPixelBufferGetHeight(self),
        CVPixelBufferGetPixelFormatType(self),
        nil,
        &_copy)

    guard let copy = _copy else { fatalError() }

    CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags.readOnly)
    CVPixelBufferLockBaseAddress(copy, CVPixelBufferLockFlags(rawValue: 0))


    let copyBaseAddress = CVPixelBufferGetBaseAddress(copy)
    let currBaseAddress = CVPixelBufferGetBaseAddress(self)

    memcpy(copyBaseAddress, currBaseAddress, CVPixelBufferGetDataSize(self))

    CVPixelBufferUnlockBaseAddress(copy, CVPixelBufferLockFlags(rawValue: 0))
    CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags.readOnly)


    return copy
}
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

4410 次

最近记录:

7 年 前