剑痴乎

剑痴乎
代码为剑,如痴如醉
  1. 首页
  2. WebRTC
  3. 正文

WebRTC研究:RTP报头扩展

2020年10月25日 5431点热度 10人点赞 7条评论

RTP Header

RTP协议中,RTP Header(报头)包括固定报头(Fixed Header)与报头扩展(Header extension,可选)。

RTP Fixed Header结构如下,其中前12字节内容必须包含的。

1
2
3
4
5
6
7
8
9
10
11
12
    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             |
   |                             ....                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

但是这Fixed Header携带的信息满足不了更复杂的需求。所以引入了RTP Header Extension,可以携带更多的信息,同时方便各种扩展。

RTP Header Extension

如果RTP Fixed Header中,X字段为1,说明后面跟着RTP Header Extension。RTP Header Extension结构如下:

1
2
3
4
5
6
7
    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
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      defined by profile       |           length              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        header extension                       |
   |                             ....                              |

  • defined by profile:决定使用哪种Header Extension:one-byte或者two-byte header
  • length:表示Header Extension的长度:length x 4字节

根据Header Extension中ID+len字段所占字节,RTP Header Extension分为One-Byte Header和Two-Byte Header类型。

One-Byte Header

对于One-Byte Header,"defined by profile"字段为固定的0xBEDE。接着后面的结构如下,占用1个byte:

1
2
3
4
5
       0
       0 1 2 3 4 5 6 7
      +-+-+-+-+-+-+-+-+
      |  ID   |  len  |
      +-+-+-+-+-+-+-+-+

  • ID:4-bit长度的ID表示本地标识符
  • len:表示extension data长度,范围:0~15,为0表示长度为1字节,15表示16字节

所以叫做One-Byte Header。

如下是一个One-Byte Header的示例:

1
2
3
4
5
6
7
8
9
10
11
       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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       0xBE    |    0xDE       |           length=3            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  ID   | L=0   |     data      |  ID   |  L=1  |   data...
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            ...data   |    0 (pad)    |    0 (pad)    |  ID   | L=3   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                          data                                 |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

首先是0xBEDE固定字段开头,接着length长度为3,说明后面跟着3x4字节长度的header extension 。对于第一个header extension:L=0,表示data长度为1字节。对于第二个header extension:L=1,表示data长度为2字节。由于按4字节对齐,所以接着是值为0的填充数据。最后一个header extension:L=3,表示data长度为4字节。

WireShark抓包分析

如下是WireShark抓包WebRTC视频流解析的一个RTP包结构:

Defined by profile字段为0xBEDE,表示One-Byte Header,Extension length为1,表示Header Extension长度为1x4字节,对于Header Extension:ID为3,Lengh为2。

相关代码Review

  • 构造相关代码位于RtpPacket::AllocateRawExtension中
  • 解析相关代码位于RtpPacket::ParseBuffer中

大家可以对着WebRTC相关代码熟悉下协议。

Two-Byte Header

"defined by profile"字段结构如下:

1
2
3
4
5
       0                   1
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |         0x100         |appbits|
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

接着后面跟着的每个扩展元素结构如下,占用2个byte:

1
2
3
4
5
       0                   1
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       ID      |     length    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • ID:本地标识符
  • length:表示extension data长度,范围1~255

如下是一个Two-Byte Header示例:

1
2
3
4
5
6
7
8
9
10
11
       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
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       0x10    |    0x00       |           length=3            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |      ID       |     L=0       |     ID        |     L=1       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       data    |    0 (pad)    |       ID      |      L=4      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                          data                                 |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

首先"defined by profile"字段为0x1000,length为3,后面跟着3x4字节长度扩展,对于第一个header extension:L=0,数据长度为0,对于第二个header extension:L=1,data长度为1,接着是填充数据,对于第三个header extension:L=4,后面跟着4字节长度数据。

由于WebRTC中默认都是One-Byte Header,所以就不抓包分析了,具体构造解析代码跟One-Byte Header位于同一地方。

常见RTP Header Extension

在WebRTC中定义了很多RTP Header Extension,最常见的要数用于带宽估计的Transport-CC扩展,记录传输层序列号:TransportSequenceNumber,默认每个RTP包都带有此扩展。

还有记录音频能量大小的AudioLevel扩展,记录发送时间的AbsoluteSendTime扩展等等。

看下目前WebRTC支持的几种Header Extension:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
constexpr ExtensionInfo kExtensions[] = {
    CreateExtensionInfo<TransmissionOffset>(),
    CreateExtensionInfo<AudioLevel>(),
    CreateExtensionInfo<CsrcAudioLevel>(),
    CreateExtensionInfo<AbsoluteSendTime>(),
    CreateExtensionInfo<AbsoluteCaptureTimeExtension>(),
    CreateExtensionInfo<VideoOrientation>(),
    CreateExtensionInfo<TransportSequenceNumber>(),
    CreateExtensionInfo<TransportSequenceNumberV2>(),
    CreateExtensionInfo<PlayoutDelayLimits>(),
    CreateExtensionInfo<VideoContentTypeExtension>(),
    CreateExtensionInfo<RtpVideoLayersAllocationExtension>(),
    CreateExtensionInfo<VideoTimingExtension>(),
    CreateExtensionInfo<RtpStreamId>(),
    CreateExtensionInfo<RepairedRtpStreamId>(),
    CreateExtensionInfo<RtpMid>(),
    CreateExtensionInfo<RtpGenericFrameDescriptorExtension00>(),
    CreateExtensionInfo<RtpDependencyDescriptorExtension>(),
    CreateExtensionInfo<ColorSpaceExtension>(),
    CreateExtensionInfo<InbandComfortNoiseExtension>(),
    CreateExtensionInfo<VideoFrameTrackingIdExtension>(),
};
};

SDP

如下是某个示例SDP内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 102 0 8 106 105 13 110 112 113 126
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 119 125 118 124 107 108 109 117 116 123
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id

SDP中,a=extmap开头即为RTP Header Extension,

对于RFC中定义的RTP Header Extension,SDP格式如下:

1
a=extmap:<value> urn:ietf:params:rtp-hdrext:<extensionattributes>

对于WebRTC中自定义的RTP Header Extension,SDP格式如下:

1
a=extmap:<value> <URI>

value值就是Header extension的本地标识符(ID值),ID值中0是预留位,所以我们可以看到从1开始递增,对于One-Byte Header来说,15也是预留位。

下面结合该SDP看下某个视频包的Wireshark解析:

可以看到该RTP包共有4个Header extension,这四个Header extension ID分别为:2,3,4,10。根据示例SDP,可知分别是:abs-send-time,transport-wide-cc-extensions,mid,rtp-stream-id扩展。

相关代码

RTP header extension的解析以及构造代码位于rtp_header_extensions.cc中。这里以TransportSequenceNumber扩展为例,看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// TransportSequenceNumber
//
//   0                   1                   2
//   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  |  ID   | L=1   |transport-wide sequence number |
//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
constexpr RTPExtensionType TransportSequenceNumber::kId;
constexpr uint8_t TransportSequenceNumber::kValueSizeBytes;
 
bool TransportSequenceNumber::Parse(rtc::ArrayView<const uint8_t> data,
                                    uint16_t* transport_sequence_number) {
  if (data.size() != kValueSizeBytes)
    return false;
  *transport_sequence_number = ByteReader<uint16_t>::ReadBigEndian(data.data());
  return true;
}
 
bool TransportSequenceNumber::Write(rtc::ArrayView<uint8_t> data,
                                    uint16_t transport_sequence_number) {
  ByteWriter<uint16_t>::WriteBigEndian(data.data(), transport_sequence_number);
  return true;
}

前面SDP章节提到了,Header extensin ID跟Header extensin类型的对应。它们的对应关系处理在RtpHeaderExtensionMap类中,当进行SetLocalSdp操作时候,会调用:

C++
1
2
3
4
5
RtpHeaderExtensionMap::Register(int id,
                                RTPExtensionType type,
                                absl::string_view uri) {
  ids_[type] = static_cast<uint8_t>(id);
}

绑定它们的关系。

总结

本文介绍了RTP包中的Header Extension,通过本文可以了解下Header Extension的结构,以及如果根据SDP以及Wireshark分析。

参考

[1] RTP: A Transport Protocol for Real-Time Applications.https://tools.ietf.org/html/rfc3550.
[2] A General Mechanism for RTP Header Extensions.https://tools.ietf.org/html/rfc5285.

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
标签: WebRTC
最后更新:2023年9月28日

Jeff

管理员——代码为剑,如痴如醉

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 觉烨

    ID的取值是怎么来的?

    2021年5月6日
    回复
    • Jeff

      @觉烨 看SDP中的取值,也就是a=extmap:后面的值,一般从1开始递增,比如本文Wireshark截图中,ID值为3,结合我给出的示例SDP,可知为TransportSequenceNumber扩展

      2021年5月11日
      回复
      • 嗑瓜子

        @Jeff 请教博主,SDP中的取值,也就是a=extmap:后面的值是怎么来的,会和RTPExtensionType中的定义产生关联吗?如何产生关联的?看sdp中transportcc是3,貌似和RTPExtensionType对应不上 :smile:

        2023年7月28日
        回复
        • Jeff

          @嗑瓜子 a=extmap:后面的值也就是ID值,根据SDP定义来的。有个RtpHeaderExtensionMap类,会绑定header extension id跟RTPExtensionType,在setlocalsdp操作中绑定的。sdp中transportcc是3,这个3是ID值,RTPExtensionType中的值是枚举的值,不一样。

          2023年9月28日
          回复
  • 觉烨

    首先是0xBEDE固定字段开头,接着length长度为3,说明后面跟着3x4字节长度的header extension 。对于第一个header extension:L=0,表示data长度为1字节。对于第二个header extension:L=1,表示data长度为2字节。由于按4字节对齐,所以接着是值为0的填充数据。最后一个header extension:L=3,表示data长度为4字节。

    ---L=0,表示data长度为1字节 为什么不需要补齐

    2021年5月7日
    回复
    • Jeff

      @觉烨 按RFC定义,这个padding数据可以放在extension间或者最后一个extension后,你要放第一个后也可以

      2021年5月11日
      回复
      • ouyang

        @Jeff 假如放中间是否有歧义了,例如我中间说2个字节,解析完2个字节后,究竟是ID为0,长度为1的,还是填充字段

        2023年2月2日
        回复
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    取消回复

    这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。

    版权声明

    为支持原创,创作更好的文章,未经许可,禁止任何形式的转载与抄袭,如需转载请邮件私信!本人保留所有法定权利。违者必究!

    文章目录
    • RTP Header
    • RTP Header Extension
      • One-Byte Header
      • Two-Byte Header
    • 常见RTP Header Extension
    • SDP
    • 相关代码
    • 总结
    • 参考
    最近评论
    ztt 发布于 3 周前(04月05日) 你好,想看里面的视频和图片为什么没有显示呢?需要下flash吗还是什么。
    huowa222 发布于 4 周前(03月26日) 同问
    邱国禄 发布于 2 个月前(02月17日) Receive Delta以0.25ms为单位,reference time以64ms为单位,kDe...
    啊非 发布于 4 个月前(12月30日) 大神,请教一个问题: constexpr int kBaseScaleFactor = Tran...
    啊非 发布于 4 个月前(12月30日) reference time:3字节,表示参考时间,以64ms为单位,但是 代码里面是 Trans...
    相关文章
    • WebRTC资讯:H265支持进展
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    • WebRTC研究:RTP时间戳的产生
    • WebRTC研究:统计参数之丢包率

    COPYRIGHT © 2024 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang