Владение 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 до завершения декодирования, но это недокументированная территория, по которой я блуждаю, поэтому ищу какое-то направление. Результаты, которые я получаю, подразумевают, что моя интуиция может быть ошибочной, поэтому мне нужно исключить управление памятью как проблему, чтобы сохранить рассудок...


person Jim B.    schedule 29.11.2016    source источник


Ответы (1)


CMSampleBuffers и CMBlockBuffers — сами объекты — следуют типичной семантике CF Retain/Release. Вы должны удерживать удержание до тех пор, пока вам нужны эти объекты, и предполагать, что интерфейсы, которые их принимают, делают то же самое. Это означает, что вы можете освободить CMBlockBuffer, как только передадите его CMSampleBuffer, и вы можете освободить CMSampleBuffer после того, как передадите его в цепочку рендеринга.

Память, на которую указывает CMBlockBuffer, созданная с помощью CMBlockBufferCreateWithMemoryBlock(), следует немного другим правилам. Во-первых, этот метод не копирует данные, на которые указывает memoryBlock; он использует этот указатель напрямую. Это означает, что BlockBuffer должен знать, как следует управлять этой памятью. Это обрабатывается либо четвертым, либо пятым аргументом CMBlockBufferCreateWithMemoryBlock(): если какой-либо из них не является kCFAllocatorNull/NULL, BlockBuffer вызовет освободитель одного из них, когда закончит с памятью. Обычно это делается в Finalize() BlockBuffer. Если они оба имеют значение kCFAllocatorNull/NULL (которое есть в вашем фрагменте кода), BlockBuffer просто бросит указатель на пол, когда это будет сделано с памятью.

Это означает, что если вы создаете CMBlockBuffer с помощью CMBlockBufferCreateWithMemoryBlock() и намереваетесь высвободить сохранение в этом BlockBuffer после передачи его по конвейеру рендеринга, вы должны использовать аргументы, отличные от NULL, для распределителя/освобождения, чтобы память можно было восстановить позже. Реализации этих распределителей/освобождений, конечно же, зависят от происхождения memoryBlock.

person DSaracino    schedule 01.12.2016
comment
Спасибо, это полезно. Итак, если я использую malloc для выделения CMBlockBuffer, каким будет правильное значение распределителя? - person Jim B.; 02.12.2016
comment
Еще одно примечание: я заставил это работать, но я обнаружил, что другая структура данных, переданная VTDecompressionSessionCreate, CMVideoFormatDescription, по-видимому, не копируется сеансом, и мне нужно было убедиться, что я также удерживаю этот блок, иначе я получаю авария. Сбой для этих условий появляется в сборке в подпрограмме CFEqual, и если вы посмотрите на один уровень выше, вы увидите, что в это время он пытался прочитать CMVideoFormatDescription. - person Jim B.; 02.12.2016
comment
В документации к kCFAllocatorMalloc говорится, что он использует malloc, realloc и free. Однако, если бы я пошел по этому пути, я бы, вероятно, использовал тот же самый распределитель для непосредственного выделения памяти, а не предполагал, что мой вызов malloc() соответствует вызову распределителя к free(). Кажется более безопасным и более явным/более простым для понимания, хотя в документации утверждается, что они одинаковы. - person DSaracino; 02.12.2016
comment
Что касается сбоя: Хмм... Похоже на то, что множество людей столкнулись бы с этим, если бы VTDecompressionSession не удерживал FormatDescription столько, сколько нужно. Это трудно понять, не глядя на клиентский код. Вы уверены, что у вас нет дополнительного релиза? Подать радар? - person DSaracino; 02.12.2016
comment
Я постараюсь лучше изолировать состояние аварии. Не регистрировал радар с тех пор, как работал в Apple 23 года назад. Я все еще могу это сделать? - person Jim B.; 02.12.2016
comment
Спасибо, потрясающий ответ. Это заставило меня понять, что в моем коде я использовал неправильный блокаллокатор для CMBlockBufferCreateWithMemoryBlock! - person bojan; 18.12.2020