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

我正在尝试创build一个由AVCaptureVideoDataOutputSampleBufferDelegate中的captureOutput返回的CMSampleBuffer的副本。

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

为了保持最佳性能,一些样本缓冲区直接引用可能需要由设备系统和其他捕获input重用的内存池。 对于未压缩的设备本机捕获而言,这种情况经常是这样的,即尽可能less地复制存储器块。 如果多个采样缓冲区参考这样的内存池的时间太长,input将不再能够将新的采样拷贝到内存中,并且这些采样将被丢弃。

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

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

这个问题已经被问了好几次,但没有回答。

CMImageBuffer或CVImageBuffer的深度拷贝和在Swift 2.0中创build一个CMSampleBuffer的副本

一个可能的答案是“我终于想出了如何使用这个来创build一个深度克隆,所有的复制方法都重复使用了堆中的数据,这些数据会lockingAVCaptureSession,所以我不得不把数据拉出到一个NSMutableData对象中,然后创build了一个新的样本缓冲区。“ 信贷给罗布 。 但是,我不知道如何正确地做到这一点。

如果你有兴趣, 这是print(sampleBuffer)的输出。 没有提到blockBuffer,又名CMSampleBufferGetDataBuffer返回nil。 有一个imageBuffer,但使用CMSampleBufferCreateForImageBuffer创build一个“复制”似乎并没有释放CMSampleBuffer。


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

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

  • memcpy指令中的EXC_BAD_ACCESS。 AKA尝试访问应用程序内存之外的段错误。
  • 或者,内存将成功复制,但CMSampleBufferCreateReadyWithImageBuffer()将失败,并显示结果代码“12743”,表示给定介质的格式与给定的格式描述不匹配。例如,与CVImageBuffer配对的格式描述与CMVideoFormatDescriptionMatchesImageBuffer “。

您可以看到Kametrixom和我都使用CMSampleBufferGetFormatDescription(sampleBuffer)来尝试复制源缓冲区的格式描述。 因此,我不知道为什么给定媒体的格式不符合给定的格式描述。

好吧,我想我终于明白了。 我创build了一个辅助扩展来完成一个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 } } 

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

 guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let copy = pixelBuffer.copy() toProcess.append(copy) 

但请注意,一个这样的像素缓冲区占用了大约3MB的内存(1080p),这意味着在100帧中已经达到了300MB左右,这大约是iPhone说STAHP(和崩溃)的时间点。

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

这是最高评价答案的Swift 3解决scheme。

 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 } }