WEBRTC 接收H264 RTP数据流小结
这篇文章是对webrtc 中,接收H264 RTP包的一个总结,主要分为两个部分:
第一部分,介绍H264打包成RTP包的规范,以及WEBRTC中目前正在使用的几种格式。
第二部分,介绍WEBRTC的数据流,从接收RTP包,到拼装成H264 Frame,最终送入Decoder,获取YUV数据。
第一部分:RTP Payload Format for H.264 Video阅读笔记
参考链接:rfc6184
RTP Payload Format
具体RTP 的协议格式,可以参考RFC 3550。
1.RTP Header
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | sequence number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | contributing source (CSRC) identifiers | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 1. RTP header according to RFC 3550
2.Payload Structures
定义了三种不同的Playload结构类型
Single NAL Unit Packet
在一个RTP Playload中,只包含一个Nal Unit 。
Single NAL Unit Packet
在一个RTP Playload中,聚合了多个Nal Unit。大致包含以下几种:
STAP-A:
STAP-B
MTAP-16
MTAP-24
Fragmentation Unit
把一个Nal Unit 进行拆分,打包到多个RTP 包中。
FU-A
FU-B
Table 1. Summary of NAL unit types and the corresponding packet types NAL Unit Packet Packet Type Name Section Type Type ------------------------------------------------------------- 0 reserved - 1-23 NAL unit Single NAL unit packet 5.6 24 STAP-A Single-time aggregation packet 5.7.1 25 STAP-B Single-time aggregation packet 5.7.1 26 MTAP16 Multi-time aggregation packet 5.7.2 27 MTAP24 Multi-time aggregation packet 5.7.2 28 FU-A Fragmentation unit 5.8 29 FU-B Fragmentation unit 5.8 30-31 reserved -
2.1 NAL Unit Header
--
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F:0表示payload 内容没有错误,1表示payload中的内容可能有错误内容或语法错误。
NRI:00表示没有参考帧。
Type:1-23
2.1.1 Packetization Modes
Single Nal unit mode:
Non-interleaved mode:
Interleaved mode:
--
Table 3. Summary of allowed NAL unit types for each packetization mode (yes = allowed, no = disallowed, ig = ignore) Payload Packet Single NAL Non-Interleaved Interleaved Type Type Unit Mode Mode Mode ------------------------------------------------------------- 0 reserved ig ig ig 1-23 NAL unit yes yes no 24 STAP-A no yes no 25 STAP-B no no yes 26 MTAP16 no no yes 27 MTAP24 no no yes 28 FU-A no yes yes 29 FU-B no no yes 30-31 reserved ig ig ig
2.2 Single NAL Unit Packet
在一个rtp'包中,只包含有一个完整的Nal Unit(视频帧)。
例: 如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容.
封装成 RTP 包将如下:
[ F|NRI| Type ] [ 67 42 A0 1E 23 56 0E 2F ]
a single NAL unit :[ 67 42 A0 1E 23 56 0E 2F ], 即只要去掉 4 个字节的开始码就可以了.
--
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| Type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2. RTP payload format for single NAL unit packet
2.3 Aggregation Packets
在一个rtp包中,会有多个Nul Unit(一个rtp包带多个视频帧)。这种情况会在视频帧比较小的时候采用。
Single-time aggregation packet (STAP):
STAP-A: without DON
例:
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ] [00 00 00 01 68 42 B0 12 58 6A D4 FF ... ]
封装成 RTP 包将如下:
[ STAP-A NAL HDR ] [78 (STAP-A头,占用1个字节)] [第一个NALU长度 (占用两个字节)] [ 67 42 A0 1E 23 56 0E 2F ] [第二个NALU长度 (占用两个字节)] [68 42 B0 12 58 6A D4 FF ... ]
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Data | : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 Size | NALU 2 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 Data | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | :...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 7. An example of an RTP packet including an STAP-A containing two single-time aggregation units <!----> * STAP-B: including DON <!----> 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |STAP-B NAL HDR | DON | NALU 1 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 HDR | NALU 1 Data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 Size | NALU 2 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 Data | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | :...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 8. An example of an RTP packet including an STAP-B containing two single-time aggregation units<!---->
Multi-time aggregation packet (MTAP):
这两种MAPS的区别在于 timestamp offset 的长度不同。
MTAP16:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |MTAP16 NAL HDR | decoding order number base | NALU 1 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 DOND | NALU 1 TS offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 HDR | NALU 1 DATA | +-+-+-+-+-+-+-+-+ + : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 SIZE | NALU 2 DOND | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 TS offset | NALU 2 HDR | NALU 2 DATA | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | :...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 12. An RTP packet including a multi-time aggregation packet of type MTAP16 containing two multi-time aggregation units * MTAP24: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |MTAP24 NAL HDR | decoding order number base | NALU 1 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 DOND | NALU 1 TS offs | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |NALU 1 TS offs | NALU 1 HDR | NALU 1 DATA | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 SIZE | NALU 2 DOND | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 TS offset | NALU 2 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 DATA | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | :...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 13. An RTP packet including a multi-time aggregation packet of type MTAP24 containing two multi-time aggregation units
--
2.4 Fragmentation Units
一个Nal Unit会被分割成,通过多个rtp包进行发送,这样便于传输和以后做fec处理。
FU-A:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| FU indicator | FU header | |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | FU payload | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| :...OPTIONAL RTP padding |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 14. RTP payload format for FU-A
FU-B:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| FU indicator | FU header | DON |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| | | | FU payload | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| :...OPTIONAL RTP padding |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 15. RTP payload format for FU-B
The FU indicator octet has the following format:
+---------------+ |0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+ |F|NRI| Type | +---------------+
The FU header has the following format:
S:1表示第一包
E:1表示是最后一个包
R:1表示中间
Type:类型
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
目前webrtc中使用的打包代码如下:
void RtpPacketizerH264::GeneratePackets() { LOG(LS_VERBOSE) << "RtpPacketizerH264::GeneratePackets packetization_mode " << (int)packetization_mode_ << " max_payload_len " << max_payload_len_ ; for (size_t i = 0; i < input_fragments_.size();) { switch (packetization_mode_) { case H264PacketizationMode::SingleNalUnit: PacketizeSingleNalu(i); ++i; break; case H264PacketizationMode::NonInterleaved: size_t fragment_len = input_fragments_[i].length; if (i + 1 == input_fragments_.size()) { // Pretend that last fragment is larger instead of making last packet // smaller. fragment_len += last_packet_reduction_len_; } if (fragment_len > max_payload_len_) { PacketizeFuA(i); ++i; } else { i = PacketizeStapA(i); } break; } } }
第二部分:接收端的数据流处理
先看一下整理的类图
video_receive_stream.jpg
处理流程如下
首先在cricket::WebRtcVideoChannel中的OnPacketReceived函数中,我们会收到RTP包。这个是通过ICE 建立的UDP链接传来的数据。
RTP数据包一直走到webrtc::RtpVideoStreamReceiver的OnPacketReceived函数中,调用webrtc::RtpReceiverImpl的IncomingRtpPacket进行rtp 包解析。
解析完之后的数据,会通过OnReceivedPayloadData回调上来,webrtc::RtpVideoStreamReceiver会将接收到的rtp包数据,打包成VCMPacket的形式,插入到PacketBuffer中。
PacketBuffer的主要工作就是收集rtp包,并且判断这些rtp包能否组装成一个完整的H264的Frame。主要的实现逻辑就是每次在InsertPacket的最后,都调用FindFrames函数去查是否有合适的帧组成。
如果发现一个完整的帧,PacketBuffer会通过OnReceivedFrame把frame数据回调给webrtc::RtpVideoStreamReceiver。然后再通过video_coding::RtpFrameReferenceFinder的ManageFrame来查找,是否有合适的帧可以送给Decoder解码。这里的合适主要分一下几点:
判断帧是否连续
判断参考帧是有没有丢失
是否是IFRAME
找到decodeble的帧后,video_coding::RtpFrameReferenceFinder通过OnCompleteFrame把frame交给internal::VideoReceiveStream,它会把frame插入到FrameBuffer中。
这里的FrameBuffer,就是以前版本的jitter buffer。在新的webrtc中已经更名。
internal::VideoReceiveStream内部有一个decode 线程,这个线程会定期问是否有合适的Frame可以送给decode解码。如果有,则把它送到vcm::VideoReceiver去解码。
vcm::VideoReceiver中持有decode的外部类,VCMGenericDecoder。我们把数据送给他,如果有decode解码完的YUV数据,他会把数据通过FrameToRender 回调webrtc::VideoStreamDecoder。
最终,webrtc::VideoStreamDecoder把YUV数据通过OnFrame回调给internal::VideoReceiveStream。在这里,webrtc就会把YUV数据传给之前注册的Render。
总结
整体的数据流程在上面的已经做了一个简单的描述,其中比较主要的是还标黄色的几个类。由于时间有限,还是会有很多具体的细节没有扩展,比如packet buffer的拼frame的逻辑,FrameBuffer找decodable frame的逻辑。这些可以在下次的文章中再和大家分享。
作者:木韦杂货店
链接:https://www.jianshu.com/p/9f7cfc185e6e
共同学习,写下你的评论
评论加载中...
作者其他优质文章