本文介绍一下rtp协议和rtcp协议。因为ffmpeg的rtp/rtcp协议实现比较简单,这里改用其他开源代码(mediasoup)来介绍。
作者:音视频小话
原文:https://mp.weixin.qq.com/s/pdw9OLFWfg8uJdH-xUqcjg
本文主要两部分:
- rtp协议详解rtp固定头部分;rtp扩展头部分;
- rtp协议代码实现这里以mediasoup的开源为例进行介绍。
https://github.com/versatica/mediasoup
文章有点长,可以先点赞收藏,有空当资料看。
本文先介绍Rtp报文结构,下一篇文章再介绍H264的Rtp封装,和Vp8/Vp9的Rtp封装。
1 Rtp协议
1.1 Rtp协议公共Header
RTP公共Header格式如下:
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 |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
每个rtp报文头有固定的12个字节。很多字段的值是在sdp交互阶段定义的,这些字段的含义如下:
- version (V): 2 bits该字段是RTP的版本,含2bits。(值1表示rtp第一个rtp草案版本,值0表示”vat”音频工具)
- padding (P): 1 bit如果padding字段有被设置1,报文尾部含有一个或多个额外补齐的字节,但是这些补齐字节不是有效载荷。补齐字节的最后一个字节标识多少个补齐字节,包含其本身。补齐常用于加密算法中必须完整size大小,或者底层协议承载多个RTP报文。
- extension (X): 1 bit如果extension被设置1,固定的rtp头必须跟着一个扩展头,其格式定义在后面的rtp扩展字段有介绍
- CSRC count (CC): 4 bitsCSRC字段是CSRC列表集合,其紧跟着固定rtp头。CSRC list: 每个32bits,0~15个。这个由cc标志位使能,代表为SSRC的媒体提供贡献的源列表。但在webrtc中,该位长期为0
- marker (M): 1 bitmarker字段定义rtp载荷分片是否结束。
- payload type (PT): 7 bits这个字段表示RTP载荷格式类型,由上层应用来决定其含义。在webrtc中payloadtype是在sdp中定义好的。Sdp中定义payload:m=audio 7 UDP/TLS/RTP/SAVPF 111a=rtpmap:111 opus/48000/2m=video 7 UDP/TLS/RTP/SAVPF 96 97a=rtpmap:96 VP8/90000a=rtpmap:97 rtx/90000Opus payloadtype: 111; Vp8 payloadtype: 96; Rtx重传payloadtype: 97, 用于Vp8的重传payloadtype
- sequence number 16: 是RTP数据报文的增长计数,接受者可以用其来检测丢包和重建报文序列。 sequence number开始数字应该是随机的,这样可以让已知明文攻击变得困难些。
- timestamp: 32 bits时间戳是RTP报文第一个字节的采样时间点。采样时间必须由一个单调线性增长的时钟,其可以完成时间同步和jitter的计算。timestamp应该是个随机值,就像sequence number一样。如果几个连续的RTP报文是同一时刻产生的,它们应该有相等的时间戳,如同一个视频帧。常规视频vp9/vp8/h264的timestamp单位: 1/90000秒。常规音频opus的timestamp单位: 1/48000秒
- SSRC: 32bits唯一标识一个媒体流。m=video 7 UDP/TLS/RTP/SAVPF 96 97a=ssrc-group:FID 1575745693 271424139a=ssrc:1575745693 cname:NpOm4i8SHMDg1rzGa=ssrc:271424139 cname:NpOm4i8SHMDg1rzGm=audio 7 UDP/TLS/RTP/SAVPF 111a=ssrc:1030411845 cname:NpOm4i8SHMDg1rzG如上,video有两个ssrc:1575745693(主),271424139(rtx丢包重传)。audio也有一个ssrc:1030411845
1.2 Rtp Extesion Header
Rtp扩展头是紧跟着Rtp公共头。如果公共头中的x位被使能,一个可变长度的扩展头就会追加在rtp header里面,其放在SSRC后面。其包含16bit的长度字段,表示有几个word(32bits)的扩展头,这个长度不包括长度在内的4个字节。头16bits表示不同扩展头的profile信息。
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 |
| .... |
如上,相关rfc文档rfc5285。
- 单字节扩展Header一个字节的扩展格式,16bits的profile定义为0xBEDE,那么后面的扩展项目格式就如下
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| ID | len |
+-+-+-+-+-+-+-+-+
- 双字节扩展Header
profile信息如下
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x100 |appbits|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1.2.1 单字节扩展Header
一个字节的扩展格式,16bits的profile定义为0xBEDE。
每一个项由4bits的ID和4bits的len组成:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| ID | len |
+-+-+-+-+-+-+-+-+
4bits的ID常用值:1-14,如果是0,表示pad;而15是保留为未来协议定义使用。如果遇到id是15,后面就不用解析len,直接结束扩展头的解析。
4bit长度表示扩展数据长度减一。如len=0表示扩展数据为1byte,如果len=15(最大值)表示扩展数据长度为16bytes。
举例如下,有三个扩展项,内含padding:
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1.2.2 双字节扩展Header
在双字节的头中,会有16bits的字段描述扩展信息。
其定义的profile信息如下:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x100 |appbits|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
appbits有4bits,应该有应用定义,如果没有特殊的定义需要,appbits为0。每个扩展项开始有一个字节的ID和一个字节的长度:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8bits的ID有效范围1-255,0表示pad,而256表示上面的”appbits“的profile开头。8bits的长度表示真实的byte大小,值0就是没有数据。
举例如下,3个扩展项,一些padding:
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1.2.3 webrtc中的常用rtp extension
webrtc中的常用rtp extension。
Audio的sdp中对header的常用信息:
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
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:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
Video的sdp中对header的常用信息:
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
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:13 urn:3gpp:video-orientation
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
这里给出两个举例:
- abs-send-time格式描述:1字节的扩展,3字节的数据,每个报文4个字节。编码方式:timestamp是24bits,64秒一次翻转,3.8us一个单位精度。NTP timestamp是高32bits为秒,低32bits为秒的小数。
abs_send_time_24 = (ntp_timestamp_64 >> 14) & 0x00ffffff
这里用于计算tcc的rtp packet的ntp时间戳,之前有一篇号内介绍Tcc的博文: webrtc tcc详解
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=1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID | L=2 | abs_send_time(24bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- transport-wide-cc-extensions
格式如下:
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=1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID | L=1 |transport-wide sequence number | zero padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.RTP头扩展中有一个全局的sequence number(包含音视频的增量),只要是用同一个socket发出的,也就是同一个PeerConnection发出的。也是用于Tcc拥塞算法使用。
2 Rtp代码实现
这里以开源mediasoup中rtp/rtcp代码为例子
https://github.com/versatica/mediasoup
关注文件
worker\include\RTC\RtpPacket.hpp
worker\src\RTC\RtpPacket.cpp
Rtp::Parse函数解析Rtp数据,见代码和注释:
RtpPacket* RtpPacket::Parse(const uint8_t* data, size_t len){
auto* ptr = const_cast<uint8_t*>(data);
//先获取Rtp公共头
//Rtp公共头,就是包含SSRC的公共头部分.
auto* header = reinterpret_cast<Header*>(ptr);
// 12 bytes的Rtp公共头.
ptr += HeaderSize;
// 如果Rtp公共头中的extension==1,即使能.
HeaderExtension* headerExtension{ nullptr };
size_t extensionValueSize{ 0u };
if (header->extension == 1u) {
//获取Rtp扩展头
//16bits的appbits,16bits的len
headerExtension = reinterpret_cast<HeaderExtension*>(ptr);
// 获取extersion的总长度(不包括:16bits的appbits,16bits的len)
extensionValueSize = static_cast<size_t>(ntohs(headerExtension->length) * 4);
//指针跳到Rtp extension数据块后面
ptr += 4 + extensionValueSize; }
// Get payload.
uint8_t* payload = ptr; size_t payloadLength = len - (ptr - data);
return new RtpPacket(header, headerExtension, payload, payloadLength, payloadPadding, len);
}
再Parse函数后,得到Rtp公共头的指针,Rtp extension头的指针和长度,Payload的指针和长度。
通过以上数据,就可以得到一个RtpPacket类指针。
RtpPacket::RtpPacket(
Header* header,
HeaderExtension* headerExtension,
const uint8_t* payload,
size_t payloadLength,
uint8_t payloadPadding,
size_t size)
: header(header), headerExtension(headerExtension), payload(const_cast<uint8_t*>(payload)), payloadLength(payloadLength), payloadPadding(payloadPadding), size(size)
{
ParseExtensions();
}
这样一个RtpPacket数据对象就能ready,并能被传递和使用。
3 总结
本文介绍Rtp报文结构,包括:
- Rtp公共头结构
- Rtp扩展头结构,及其在Sdp中的定义
下一篇文章介绍H264的Rtp封装,和Vp8/Vp9的Rtp封装。
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。