当前位置: 首页 > 面试题库 >

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

晏昀
2023-03-14
问题内容

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

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

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

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

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

一个可能的答案是“我终于想出了如何使用它创建一个深层克隆。所有复制方法都重复使用了保留在堆中的数据,这将锁定AVCaptureSession。因此,我不得不将数据拉出到NSMutableData对象中,然后创建了一个新的样本缓冲区。”归功于罗布。但是,我不知道如何正确地做到这一点。

如果您有兴趣,这是的输出print(sampleBuffer)。没有提及blockBuffer,akaCMSampleBufferGetDataBuffer返回nil。有一个imageBuffer,但是使用CMSampleBufferCreateForImageBuffer创建“副本”似乎也不会释放CMSampleBuffer。

编辑:由于已发布此问题,我一直在尝试更多的复制内存的方法。

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

  • memcpy指令上的EXC_BAD_ACCESS。试图从应用程序的内存之外进行访问也可能导致段错误。
  • 或者,内存将成功复制,但是CMSampleBufferCreateReadyWithImageBuffer()将失败,并显示结果代码-12743,结果代码为-12743,它指示给定媒体的格式与给定格式说明不匹配。例如,与CVImageBuffer配对的格式说明使CMVideoFormatDescriptionMatchesImageBuffer失败。

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


问题答案:

好吧,我想我终于明白了。我创建了一个辅助扩展程序来制作的完整副本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)

但是请注意,一个这样的pixelBuffer会占用大约3MB的内存(1080p),这意味着在100帧中您已经拥有了300MB的内存,这大约是iPhone所说的STAHP(并崩溃)的时间。

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



 类似资料:
  • 我不确定我的问题是出在代码的深度复制部分,还是我在将元素添加到原始列表时犯了错误。到目前为止我所掌握的是: ...其中与此相关联的测试用例是:

  • 问题内容: 我想使用构造函数制作对象数组的深层副本。 但是,由于某种原因,我上面的内容不起作用。我有运行的自动化测试,但未通过这些测试。所以这里有一个错误,我不确定是什么。 问题答案: 您已实现的是 浅表 副本。要实现 深度 复制,您必须进行更改 一些事情,分配一个 副本 的到。您如何执行此操作取决于班级。可能的替代方法是: 复制构造函数: 工厂方法: 克隆: 笔记: 上面假设复制构造函数,工厂方

  • 我使用Angular调用外部API。Json数据的格式如下: 既然json数据是嵌套的,我如何在typescript中创建一个类来读取它? 请指导如果PermissionProfile,PermissionProfile将是独立的嵌套类? 我该如何申报?

  • 我有一个数据框,比如说一些投资数据。我需要根据某些条件(比如说,U类型)从这个数据帧中提取数据。有许多可用的基金类型,我只需要提取与特定基金类型匹配的数据。 funding_type有风险、种子、天使、股权等价值。我只需要数据匹配资金类型比如种子和天使 我试着跟着 这里MF1是我的数据帧。这将提供与种子基金类型相关的所有数据 我需要的条件有点像 MF1[MF1['funding_round_typ

  • 问题内容: 我知道它是一个数组,但是我对JSON完全陌生,需要帮助理解它的结构,这是我提取数据的尝试: 我拥有的JSON数据如下所示: 我对这些东西的掌握并不强,因此感谢所有帮助。 问题答案: 这是个主意: 它应该可以工作(如果有编译错误,请随时投诉)

  • 问题内容: 我正在寻找一种在Linux Shell环境中从HTML获取某些信息的方法。 这是我感兴趣的一点: 我想将它们存储在shell变量中或在从html上面提取的键值对中回显这些变量。范例: 目前,我可以做的是创建一个Java程序,该程序将使用sax解析器或html解析器(例如jsoup)来提取此信息。 但是在这里使用Java似乎很麻烦,因为要在您要执行的“包装器”脚本中包含可运行的jar。