WebRTC: Что такое RTPFragmentationHeader в реализации кодировщика?

Я модифицировал h264_encoder_impl использовать аппаратный кодировщик nvidia grid. Это делается путем замены определенных вызовов OpenH264 вызовами API Nvidia. Закодированный поток может быть успешно записан в файл, но запись _buffer а также _size из encoded_image_ не хватает и RTPFragmentationHeader также должен быть заполнен.

// RtpFragmentize(EncodedImage* encoded_image,
//                       std::unique_ptr<uint8_t[]>* encoded_image_buffer,
//                       const VideoFrameBuffer& frame_buffer,
//                       SFrameBSInfo* info,
//                      RTPFragmentationHeader* frag_header)

// encode
openh264_->Encode(input, &info /*out*/);

// fragmentize ?
RtpFragmentize(&encoded_image_ /*out*/, &encoded_image_buffer_, *frame_buffer, 
               &info, &frag_header /*out*/); 

// ...

// send 
encoded_image_callback_->OnEncodedImage(encoded_image_, &codec_specific, &frag_header);

Текущая реализация на основе Openh264 заполняет frag_header в RTPFragmentize() и VP8 заполняет это по-другому. Я могу увидеть что-то с NAL Untis и слоев, который также рассчитывает encoded_image->_length но я понятия не имею как.

Я не могу найти какую-либо документацию по нему нигде. Реализации VP8 и OpenH264 - это все, что у меня есть.

Так что же RTPFragmentationHeader? Что это делает? Что такое encoded_image->_length? Как правильно его заполнить при использовании нестандартного кодера H264? Я могу найти стартовый код, но что дальше? Как заполнить всех своих членов?

1 ответ

Решение

После прохождения RTPFragmentize() в h264_encoder_impl Я понял это.

В закодированном кадре есть несколько NALU. Существуют различные NALU, включая AUD, SPS (67), PPS (68) и IDR. Каждый NALU отделен 4-байтовым начальным кодом, который 00 00 00 01,

Для OpenH264 заголовок выглядел так для первого кадра

[00 00 00 01 67 42 c0 20 8c 8d 40 20 03 09 00 f0  
 88 46 a0 00 00 00 01 68 ce 3c 80]00 00 00 01.. 

Вы можете увидеть стартовый код жирным шрифтом. Только байты между квадратными скобками принадлежат заголовку, последний стартовый код для данных кадра.

RTPFragmentationHeader для выше:

frag_header->fragmentationVectorSize = 3     // 2 fragments for header
                                             // 3rd fragment for frame buffer

frag_header->fragmentationOffset[0]  = 4     
frag_header->fragmentationLength[0]  = 15

frag_header->fragmentationOffset[1]  = 23    // 4 + 15 + sizeof(startcode)
frag_header->fragmentationLength[1]  = 4    

frag_header->fragmentationOffset[2]  = 31   
frag_header->fragmentationLength[2]  = 43218 // last fragment is frame buffer

Следующие кадры всегда имели только один фрагмент, который выглядел следующим образом

00 00 00 01 67 b8 .. .. ..

encoded_image->_length размер фактического закодированного кадра буфера и
encoded_image->_size максимальный размер буфера закодированного кадра.

OpenH264 API дает количество NALU в кодированном фрейме, которое используется для вычисления фрагментов, в то время как API, который я использовал, предоставлял только заголовок и его размер, независимо от того, был ли заголовок фактически добавлен с фреймом или нет. Поиск байтов кадра только по размеру заголовка позволил правильно рассчитать фрагментацию.

После этого, наконец, были отправлены закодированные данные, и они были правильно декодированы в браузере клиента.

Обновление: по сути, мне пришлось пропустить RTPFragmentize() целиком, потому что это сделано специально для OpenH264, и рассчитать frag_header сам на основе вышеуказанных наблюдений.

Другие вопросы по тегам