JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
JCHub
Code as My Sword, Lost in Obsession
  1. Main page
  2. WebRTC
  3. Main content

WebRTC研究:RTP报头扩展

2020年10月25日 7542hotness 10likes 7comments

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.

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: WebRTC
Last updated:2023年9月28日

Jeff

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

Tip the author Like
< Last article
Next article >

Comments

  • 觉烨

    ID的取值是怎么来的?

    2021年5月6日
    Reply
    • Jeff

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

      2021年5月11日
      Reply
      • 嗑瓜子

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

        2023年7月28日
        Reply
        • Jeff

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

          2023年9月28日
          Reply
  • 觉烨

    首先是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日
    Reply
    • Jeff

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

      2021年5月11日
      Reply
      • ouyang

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

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

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    文章目录
    • RTP Header
    • RTP Header Extension
      • One-Byte Header
      • Two-Byte Header
    • 常见RTP Header Extension
    • SDP
    • 相关代码
    • 总结
    • 参考
    Related Posts
    • 浅谈基于SFU实现一对一效果
    • WebRTC资讯:H265支持进展
    • Protected: WebRTC硬件编解码器出错无缝切换软编软解
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    Categories

    COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang