2015-10-17 12 views
4

Это будет вопрос, ответ на который я отвечу, потому что он провел меня в течение всей недели, и я хочу избавить своих программистов от разочарования, которое я пережил.Как передавать видео H.264 через UDP с помощью аппаратного кодера NVidia NVEnc?

Ситуация такова: вы хотите использовать аппаратный кодер NVEnc от NVidia (доступный на картах Kepler и Maxwell, т. Е. GT (x) 7xx и GT (x) 9xx соответственно) для потокового вывода графического приложения через UDP , Это не тривиальный путь, но он может быть очень эффективным, поскольку он обходит необходимость «загружать» кадры из видеопамяти в системную память до этапа кодирования, поскольку NVEnc имеет возможность напрямую обращаться к видеопамяти.

Я уже успел выполнить эту работу, чтобы сгенерировать файл .h26, просто наложив на него выходные буферы NVEnc, кадр за кадром. У VLC не было проблем с воспроизведением такого файла, за исключением того, что время было отключено (я не пытался это исправить, так как мне нужен только этот файл для отладки).

Проблема возникла, когда я попытался передать кодированные кадры через UDP: ни VLC, ни MPlayer не смогли отобразить видео. Оказалось, что для этого есть две причины, которые я объясню в своем ответе.

ответ

7

Как я уже сказал в вопросе, было две (ну, фактически три) причины, по которым MPlayer не смог воспроизвести мой поток UDP.

Первая причина связана с пакетированием. NVEnc заполняет свои выходные буферы блоками данных, называемыми NALU, которые он разделяет на «начальные коды», в основном предназначенные для синхронизации битового потока. (Перейдите к szatmary's отличным SO answer, если вы хотите узнать больше о Приложении B - и его конкуренте AVCC).

Проблема заключается в том, что NVEnc иногда обеспечивает более одного такого NALU в одном выходном буфере. Хотя большинство NALU содержат закодированные видеофрагменты, иногда необходимо (и обязательно в начале потока) отправлять некоторые метаданные, такие как разрешение, частота кадров и т. Д. NVEnc помогает в этом, генерируя эти специальные NALU (более на этом ниже).

Как оказалось, программное обеспечение проигрывателя не поддерживает получение более одного NALU в одном пакете UDP. Это означает, что вам нужно запрограммировать простой цикл, который ищет стартовые коды (два или три «0» байта, за которым следует «1» байт), чтобы вырезать выходной буфер и отправлять каждый NALU в свой собственный UDP-пакет. (Обратите внимание, однако, что UDP-пакеты все равно должны включать эти стартовые коды.)

Еще одна проблема с пакетизацией заключается в том, что IP-пакеты обычно не могут превышать определенный размер. Опять же, SO answer дает ценное представление о том, что эти ограничения находятся в разных контекстах. Важным здесь является то, что в то время как вы не должны справиться с этим самостоятельно, вы должны сказать NVEnc на «срез» свой выход, установив следующие параметры при создании объекта датчика:

m_stEncodeConfig.encodeCodecConfig.h264Config.sliceMode = 1; 
m_stEncodeConfig.encodeCodecConfig.h264Config.sliceModeData = 1500 - 28; 

m_stEncodeConfig являющийся структурой параметров, которая будет передана в NvEncInitializeEncoder(), 1500 - MTU Ethernet-пакетов, а 28 - добавленные размеры заголовка IP4 и UDP-заголовка).

Вторая причина, по которой MPlayer не может воспроизводить мой поток, связана с природой потокового видео, а не с хранением в файле. Когда проигрыватель начнет воспроизведение файла H.264, он найдет необходимые NALU метаданных, содержащие разрешение, частоту кадров и т. Д., Сохранит эту информацию и, следовательно, никогда не понадобится. Принимая во внимание, что, когда его попросят воспроизвести поток, он пропустит начало этого потока и не сможет начать играть до тех пор, пока отправитель не отправит метаданные.

И вот в чем проблема: если не сказано иначе, NVEnc будет генерировать только метаданные NALU в самом начале сеанса кодирования. Здесь параметр конфигурации датчика, который должен быть установлен:

m_stEncodeConfig.encodeCodecConfig.h264Config.repeatSPSPPS = 1; 

Это говорит NVEnc повторно генерировать SPS/PPS на время элементы уровня сетевой абстракции от времени (я думаю, что по умолчанию, это означает, что с каждым кадром IDR).

И вуаля! С устранением этих препятствий вы сможете оценить мощность генерации сжатых видеопотоков, в то же время практически не облагая налогом процессор.

EDIT: Я понимаю, что такой ультрасовременный поток UDP обескуражен, так как он не соответствует ни одному стандарту. Mplayer будет играть такой поток, но VLC, который в противном случае способен играть практически во все, не будет. Основная причина заключается в том, что в потоке данных нет ничего, что даже указывает тип отправляемого носителя (в данном случае - видео). В настоящее время я занимаюсь исследованиями, чтобы найти самый простой способ, который удовлетворит принятые стандарты.

+0

Если вы идете по стандарту IETF, вы смотрите на rfc3550 (RTP/UDP) + rfc6184 (формат полезной нагрузки H.264). Вероятно, вы захотите реализовать режим пакетирования = 0 (единичный режим NAL), поскольку вы уже настроили кодировщик в соответствии с сетевым MTU, хотя потребуется пакетный режим = 1 (без чередования), если вы хотите агрегировать/фрагментировать NAL-модули по нескольким RTP-пакетам. – Ralf

+0

Спасибо @ Ralf. RFC6184 остается вариантом, но я решил, что пока что потоковая передача UDP прекрасна, поскольку поток будет генерироваться и потребляться пользовательским программным обеспечением. – JPNotADragon

+0

@JpNotADragon - можете ли вы пинговать (email) мне вашу контактную информацию @ gmail.com, я хотел отбросить некоторые вопросы от вас по этому вопросу (спасибо!) –