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 ответов
- Если 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;
вы должны написать SPS и PPS в начале потока, и только тогда, когда они меняются в середине потока.
рамки SPS и PPS упакованы в блоке STAP NAL (вообще STAP-A) с типом 24 NAL (STAP-A) или 25 (Stap-B) формат STAP описан в RFC-3984 раздел 5.7.1
не полагайтесь на бит маркера, используйте начальный бит и конечный бит в заголовке NAL.
-
для фрагментированные кадры вы должны регенерировать 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"
здесь простой реализация