CMSampleBuffer 中的 CMBlockBuffer 所有权

CMBlockBuffer ownership in CMSampleBuffer

我正在编写代码来解压缩原生 annex-B H.264 流,我正在经历解析流、从 SPS/PPS NALU 创建 CMVideoFormatDescription 并包装我从 CMSampleBuffers 中的流中提取的其他 NALU。

我对如何为解码器处理 CMBlockBuffer 和 CMSampleBuffer 内存感到困惑。我相信我的问题更多的是缺乏对 CF 如何处理内存的透彻理解,而不是其他任何问题,所以我的问题实际上更多是关于这个,但我希望上下文对您有所帮助。

如果我像这样创建 CMBlockBuffer:

CMBlockBufferRef blockBuffer;

OSStatus status = CMBlockBufferCreateWithMemoryBlock(NULL,
                                                     memoryBlock,                       
                                                     blockBufferLength,
                                                     kCFAllocatorNull,
                                                     NULL,
                                                     0, 
                                                     blockBufferLength,          
                                                     kCMBlockBufferAlwaysCopyDataFlag | kCMBlockBufferAssureMemoryNowFlag,
                                                     &blockBuffer);

并将其添加到 CMSampleBuffer 中,如下所示:

CMSampleBufferRef sampleBuffer;

status = CMSampleBufferCreate(kCFAllocatorDefault,
                              blockBuffer,
                              true,
                              NULL,
                              NULL,
                              formatDescription,
                              1,
                              0,
                              NULL,
                              1,
                              &sampleSize,
                              &sampleBuffer);

我应该如何处理块缓冲区? SampleBuffer 是否保留了块缓冲区的内存,或者我是否需要做一些事情来确保它没有被释放?

此外,关于异步解码过程,是否有一种明智的方法来知道解码器何时使用 CMSampleBuffer 完成,以便我可以处理它?

我的直觉告诉我 CMSampleBuffer 将保留 CMBlockBuffer,而 VTDecodeSession 将保留 CMSampleBuffer 直到它完成解码,但这是我正在徘徊的无证领域,因此正在寻找方向。我得到的结果暗示我的直觉可能是错误的,所以我需要排除内存管理问题以保持理智...

CMSampleBuffers 和 CMBlockBuffers - 对象本身 - 遵循典型的 CF Retain/Release 语义。只要您需要这些对象,就应该保持保留,并假设接受它们的接口也是如此。这意味着您可以在将 CMBlockBuffer 传递给 CMSampleBuffer 后立即释放它,并且在将其传递到渲染链后可以自由释放 CMSampleBuffer。

使用 CMBlockBufferCreateWithMemoryBlock() 创建的 CMBlockBuffer 指向的内存遵循稍微不同的规则。首先,该方法不会复制 memoryBlock 指向的数据;它直接使用该指针。这意味着 BlockBuffer 需要了解应该如何管理该内存。这是由 CMBlockBufferCreateWithMemoryBlock() 的第四个或第五个参数处理的:如果其中一个是 non-kCFAllocatorNull/NULL,BlockBuffer 将在完成内存处理后调用其中一个的释放器。这通常在 BlockBuffer 的 Finalize() 中完成。如果它们都是 kCFAllocatorNull/NULL(您的代码片段中有),BlockBuffer 将在完成内存处理后将指针放在地板上。

这意味着如果您使用 CMBlockBufferCreateWithMemoryBlock() 创建一个 CMBlockBuffer 并打算在将其传递到渲染管道后释放对该 BlockBuffer 的保留,则您应该为 allocator/deallocators 使用非 NULL 参数,因此内存可以稍后回收。当然,这些 allocators/deallocators 的实现取决于 memoryBlock 的来源。