H.264 over RTP-识别SPS和PPS帧
H.264 over RTP - Identify SPS and PPS Frames
我有一个来自IP相机的原始H.264流,它封装在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]
....
因此,我从先前的RTSP
通信中从Session Description Protocol
获得SPS
和PPS
。此外,在开始视频流本身之前,摄像机在两个单独的消息中发送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
然后出现了一些具有有效载荷的帧,并且在某个时刻出现了具有Marker Bit = 1
的RTP帧。这意味着(如果我做对了)我有一个完整的视频帧。在此之后,我再次从有效载荷中写入前缀序列(00 00 01
)和NAL
,并继续进行相同的过程。
现在,我的相机在每8个完整的视频帧后再次发送SPS
和PPS
。(同样在两个RTP帧中,如上面的示例所示)。我知道,尤其是PPS
可以在流媒体之间切换,但这不是问题所在。
我现在的问题是:
1.是否需要每8个视频帧写入一次SPS/PPS
如果我的SPS
和PPS
没有更改,那么在我的文件一开始就把它们写出来就足够了,什么都不做?
2.如何区分SPS/PPS和普通RTP帧
在我解析传输数据的C++代码中,我需要在具有正常有效载荷的RTP帧和携带SPS/PPS
的RTP框架之间做出区分。我如何区分它们?好吧,SPS/PPS
帧通常要小得多,但这不是一个可以依赖的保存调用。因为如果我忽略它们,我需要知道我可以丢弃哪些数据,或者如果我需要写入它们,我就需要在它们前面加上00 00 01
前缀?还是每8个视频帧出现一次是固定的规则?
- 如果SPS和PPS没有更改,则可以省略它们(第一个除外)
- 您需要解析每个nal的nal_unit_type字段,对于SPS,nal_unitype==7;对于PPS,nal_unit_type==8
我记得,nal_unit_type是帧的第一个字节的低5位。
nal_unit_type = frame[0] & 0x1f;
-
您应该在流的开头写入SPS和PPS,并且只有当它们在流的中间发生变化时才写入。
-
SPS和PPS帧被封装在NAL类型为24(STAP-a)或25(STAP-B)的STAP NAL单元(通常为STAP-a
-
不要依赖标记位,在NAL头中使用起始位和结束位。
-
对于分段视频帧,您应该使用第一个片段(F,NRI)的3个NAL单元位与有效载荷中第一个字节的5个NAL类型位组合来重新生成NAL单元(仅适用于起始位设置为1的数据包),请查看RFC-3984第5.8节:
分段的NAL单元类型八位字节NAL单元不包括在分段单元有效载荷中,而是NAL单元类型八位字节的信息分段NAL单元在FU的F和NRI字段中传输分段单元的指示符八位字节,并且在的类型字段中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指示器";
这里有一个简单的实现