H. 264 через RTP-идентификация кадров SPS и PPS

у меня есть сырой поток H. 264 от IP-камеры, упакованной в рамки RTP. Я хочу получить данные raw H. 264 в файл, чтобы конвертировать его в ffmpeg.

поэтому, когда я хочу записать данные в свой файл raw H. 264, я узнал, что он должен выглядеть так:

00 00 01 [SPS] 
00 00 01 [PPS]
00 00 01 [NALByte]
[PAYLOAD RTP Frame 1]     // Payload always without the first 2 Bytes -> NAL
[PAYLOAD RTP Frame 2]
[... until PAYLOAD Frame with Mark Bit received]  // From here its a new Video Frame
00 00 01 [NAL BYTE]
[PAYLOAD RTP Frame 1]
....

я понимаю SPS и PPS С Session Description Protocol из моего предыдущего RTSP общение. Кроме того, камера отправляет SPS и PPSв двух отдельных сообщениях перед запуском с самим видеопотоком.

поэтому я записываю сообщения в следующем порядке:

1. Preceding RTSP Communication here ( including SDP with SPS and PPS )
2. RTP Frame with Payload: 67 42 80 28 DA 01 40 16 C4    // This is the SPS 
3. RTP Frame with Payload: 68 CE 3C 80                   // This is the PPS
4. RTP Frame with Payload: ...  // Video Data

затем приходят некоторые кадры с полезной нагрузкой и в какой-то момент кадр RTP с Marker Bit = 1. Это означает (если я правильно понял), что у меня есть полный видеокадр. После этого пишу префиксную последовательность (00 00 01 ) и NALиз полезной нагрузки снова и продолжайте ту же процедуру.

теперь моя камера отправляет меня после каждых 8 полных видеокадров SPS и PPS снова. (Снова в двух кадрах RTP, как показано в примере выше ). Я знаю, что особенно PPS может меняться между потоковой передачей, но это не проблема.

мои вопросы:

1. Нужно ли писать SPS / PPS каждый 8-й видеокадр?

если мой SPS и меня PPS не меняйте этого должно быть достаточно, чтобы они были написаны в самом начале моего файла и ничего еще?

2. Как отличить SPS / PPS от обычных кадров RTP?

в моем коде C++, который анализирует передаваемые данные, мне нужно сделать разницу между кадрами RTP с нормальной полезной нагрузкой и теми, которые несут SPS/PPS. Как я могу их различить? Хорошо SPS/PPS кадры обычно меньше, но это не вызов сохранения, на который можно положиться. Потому что если я игнорирую их, мне нужно знать, какие данные я могу выбросить, или если мне нужно написать их, мне нужно поставить 00 00 01 префикс перед ними. ? Или это фиксированное правило, они происходят каждый 8-й кадр?

2 ответов


  1. Если SPS и PPS не меняются, вы можете опустить их, кроме 1-го.
  2. вам нужно проанализировать поле nal_unit_type каждого NAL, для SPS, nal_unit_type==7; для PPS, nal_unit_type==8.

насколько я помню, nal_unit_type это нижние 5 бит 1-го байта кадра.

nal_unit_type = frame[0] & 0x1f;

  1. вы должны написать SPS и PPS в начале потока, и только тогда, когда они меняются в середине потока.

  2. рамки SPS и PPS упакованы в блоке STAP NAL (вообще STAP-A) с типом 24 NAL (STAP-A) или 25 (Stap-B) формат STAP описан в RFC-3984 раздел 5.7.1

  3. не полагайтесь на бит маркера, используйте начальный бит и конечный бит в заголовке NAL.

  4. для фрагментированные кадры вы должны регенерировать NAL-блок, используя 3 бита NAL-блока первого фрагмента (F, NRI) в сочетании с 5 битами типа NAL первого байта в полезной нагрузке (только для пакетов с начальным битом, установленным в 1), Проверьте RFC-3984 раздел 5.8:

    октет типа NAL блока фрагментированного Блок NAL не включен как таковой в полезную нагрузку блока фрагментации, но скорее информация типа октета NAL блока фрагментированная конечная единица передается в полях F и NRI FU индикаторный октет осколочного блока и в поле Тип заголовок FU.

EDIT: больше объяснения о конструкции блока NAL для блоков фрагментации:

Это первые два байта полезной нагрузки FU-A (сразу после заголовка rtp):

|  FU indicator |   FU header   |
+---------------+---------------+
|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |S|E|R|  Type   |
+---------------+---------------+

чтобы построить конечный блок, вы должны взять "Type "из" Fu Header "и" F "и" NRI "из"Fu indicator"

здесь простой реализация