Владение CMBlockBuffer в CMSampleBuffer
Я пишу код для распаковки собственного потока H.264 приложения B, и я прохожу процесс синтаксического анализа потока, создания CMVideoFormatDescription из NALU SPS/PPS и упаковки других NALU, которые я извлекаю из потока в CMSampleBuffers.
Я страдаю из-за ментального блока, как обращаться с памятью 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 до тех пор, пока не завершит декодирование, но это недокументированная территория, по которой я блуждаю, поэтому ищу какое-то направление. Результаты, которые я получаю, подразумевают, что моя интуиция может быть неправильной, поэтому мне нужно исключить управление памятью как проблему, чтобы сохранить мое здравомыслие...
1 ответ
CMSampleBuffers и CMBlockBuffers - сами объекты - следуют типичной семантике CF Retain/Release. Вы должны хранить retain, пока вам нужны эти объекты, и предполагать, что интерфейсы, которые их принимают, делают то же самое. Это означает, что вы можете освободить CMBlockBuffer, как только вы передадите его CMSampleBuffer, и вы сможете освободить CMSampleBuffer после того, как передадите его в цепочку рендеринга.
Память, на которую указывает CMBlockBuffer, созданная с помощью CMBlockBufferCreateWithMemoryBlock(), следует немного другим правилам. Во-первых, этот метод не копирует данные, на которые указывает memoryBlock; он использует этот указатель напрямую. Это означает, что BlockBuffer должен знать, как управлять этой памятью. Это обрабатывается либо четвертым, либо пятым аргументами CMBlockBufferCreateWithMemoryBlock(): если они не являются kCFAllocatorNull/NULL, BlockBuffer вызовет освобождение одного из них, когда это будет сделано с памятью. Обычно это делается в Finalize() BlockBuffer. Если они оба kCFAllocatorNull/NULL (которые есть в вашем фрагменте кода), BlockBuffer просто уронит указатель на пол, когда это будет сделано с памятью.
Это означает, что если вы создаете CMBlockBuffer с помощью CMBlockBufferCreateWithMemoryBlock() и намереваетесь освободить свое сохранение для этого BlockBuffer после передачи его по конвейеру рендеринга, вы должны использовать не-NULL аргументы для распределителя / освобождающих средств, чтобы память могла быть освобождена позже. Реализация этих распределителей / разветвителей, конечно, зависит от источника блока памяти.