Catalog
  1. 1. 概要
  2. 2. webrtc branch
  3. 3. rtp/rtcp/STUN 协议头
  4. 4. 数据入口
    1. 4.1. if STUN
    2. 4.2. if dtls
    3. 4.3. if rtp
  5. 5. srtp or srtcp ?
webrtc如何处理多协议

概要

webrtc 中会用到多种协议,包括 STUN、rtp、rtcp, 但是他们会共用一个端口,这里记录一下 webrtc 中是如何分流数据的。

webrtc branch

4147 (m84), 本文代码属于该分支。

rtp/rtcp/STUN 协议头

这里再贴一下协议头,方便查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 // STUN
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0| STUN Message Type | Message Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie (0x2112A442) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// rtcp
// 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| RC | PT=RR=201 | length | header
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+


// rtp
// 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 |
// | .... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

数据入口

1
Connection::OnReadPacket

if STUN

第一步,判断该数据是不是 STUN 协议消息。

1
2
3
4
5
6
// 从二进制中构造 STUN 消息对象
Port::GetStunMessage

// 分析数据,判断是否是 STUN 消息
StunMessage::IsStunMethod
StunMessage::ValidateFingerprint

判断一个消息是否为 STUN 的条件:

  1. 消息长度是 4 字节的整数倍
  2. magic_cookie 匹配(0x2112A442, 可参考 STUN 协议规范)
  3. 匹配 STUN Message Type
  4. 匹配 FINGERPRINT 值 (STUN 消息的 CRC-32 校验值)
  5. 消息是否完整

如果是 STUN 消息,则进入 STUN 处理的逻辑,如果不是则往下。

if dtls

第二步,判断是否为 dtls 包, 这里所说的是 dtls 握手包。

1
DtlsTransport::OnReadPacket

判断是否为 dtls 包的条件:

  1. 第一个字节的值范围 [20, 63]

如果不是握手包,且 dtls 状态是 connected 的话,继续下一步。

if rtp

第三步, 判断是否为 rtp 包, 条件如代码所示。长度 >= 12, 第一个字节 & 0xC0 == 0x80, 其实就是 RTP/RTCP 的前两位,也就是 V = 2, 这是一个固定的值。这里要注意一下,实际上用的是 srtp/srtcp 协议,但是他们的消息头和 rtp/rtcp 是一样的,仅仅是 payload 被加密了,所以其头解析过程是一致的。

1
2
3
4
static bool IsRtpPacket(const char* data, size_t len) {
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80);
}

如果判断是 rtp/rtcp 包的话,继续下一步。

srtp or srtcp ?

最后分流 srtp 包和 srtcp 包。根据 payload type 来区分
看下列代码,解释一下如何判断 RTCP 就够了。

  1. 长度 >= 4, V = 2
  2. 判断 payload type, 也就是第一个字节,主要是通过这个字节来区分 RTCP 和 RTP。
1
2
3
4
5
// rtpc
| PT=RR=201 |

// rtp
|M| PT |

如上所述, 第一个字节在 rtp 或 rtcp 中,是不太一样的。 在 rtpc 中就是 payload type, 而在 rtp 中,第一位是 M 标识, 剩余位才表示 payload type。 其区分规则是 packet[1] & 0x7F 的值在 (63, 96) 这个区间的话,则表示这个包是 rtcp 包。至于为什么是这个区间,实际上它是一个协商值,大家商量好,哪些是 rtp 用,哪些是 rtcp 用,这可以参考文档

如果条件不满足,则接下来判断长度 >=12, V=2 则是 rtp 包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
RtpPacketType InferRtpPacketType(rtc::ArrayView<const char> packet) {
// RTCP packets are RTP packets so must check that first.
if (IsRtcpPacket(packet)) {
return RtpPacketType::kRtcp;
}
if (IsRtpPacket(packet)) {
return RtpPacketType::kRtp;
}
return RtpPacketType::kUnknown;
}

// rtcp
bool IsRtcpPacket(rtc::ArrayView<const char> packet) {
// 长度 >= 4, V = 2
if (packet.size() < kMinRtcpPacketLen ||
!HasCorrectRtpVersion(
rtc::reinterpret_array_view<const uint8_t>(packet))) {
return false;
}

// 将第一个字节(从 0 开始) & 0x7F, 其含义是将最高为去掉。
char pt = packet[1] & 0x7F;
return (63 < pt) && (pt < 96);
}

// rtp
bool IsRtpPacket(rtc::ArrayView<const char> packet) {
return packet.size() >= kMinRtpPacketLen &&
HasCorrectRtpVersion(
rtc::reinterpret_array_view<const uint8_t>(packet));
}
Author: 42
Link: http://blog.ikernel.cn/2020/11/19/webrtc%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E5%A4%9A%E5%8D%8F%E8%AE%AE/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment