Подтвердить что ты не робот

H.264 через RTP - Определение SPS и PPS-фреймов

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

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

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

Теперь мои вопросы:

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

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

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

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

4b9b3361

Ответ 1

  • Если SPS и PPS не изменяются, вы можете опустить их, кроме 1-го.
  • Вам нужно разобрать поле 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;

Ответ 2

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

  • Кадры SPS и PPS упаковываются в блок STAP NAL (обычно STAP-A) с форматом STAP-A NAL типа 24 (STAP-A) или 25 (STAP-B), описанным в RFC-3984 раздел 5.7.1

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

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

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

РЕДАКТИРОВАТЬ: больше пояснений о конструкции блока 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   |
+---------------+---------------+

чтобы построить блок NAL, вы должны взять "Тип" из "Заголовка FU" и "F" и "NRI" из "Индикатора FU"

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