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;
}
}
}
第二部分:接收端的數據流處理
先看一下整理的類圖
處理流程如下
- 首先在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的邏輯。這些可以在下次的文章中再和大家分享。
喜歡的同學可以掃碼進群討論: