RTP的会话过程
数据传输协议RTP,用于实时传输数据。
当应用程序建立一个RTP会话时,应用程序将确定一对目的传输地址。
目的传输地址由一个网络地址和一对端口组成,有两个端口:一个给RTP包,一个给RTCP包,使得RTP/RTCP数据能够正确发送。RTP数据发向偶数的UDP端口,而对应的控制信号RTCP数据发向相邻的奇数UDP端口(偶数的UDP端口+1),这样就构成一个UDP端口对。
RTP的发送过程如下,接收过程则相反。
-
1)RTP协议从上层接收流媒体信息码流(如H.264),封装成RTP数据包;
-
2)RTCP从上层接收控制信息,封装成RTCP控制包。
-
3)RTP将RTP 数据包发往UDP端口对中偶数端口;RTCP将RTCP控制包发往UDP端口对中的奇数端口。
RTP实际应用中的细节
-
发送端,上层应用程序以分组形式将编码后的媒体数据传给RTP通信模块,作为RTP报文的有效载荷,RTP通信模块将根据上层应用提供的参数在有效载荷前添加RTP报头,形成 RTP报文,通过Socket接口选择UDP协议发送出去。
-
接收端,RTP通信模块通过Socket接口接收到RTP报文后,将RTP报头分离出来作相应处理,再将RTP报文的有效载荷作为数据分组传递给上层应用。
考虑到在Internet这种复杂的环境中举行视频会议,RTP定义了两种中间系统:混合器(Mixer)和转换器(Translator)。
用户网络带宽不同
在Internet上举行视频会议时,可能有少数参加者通过低速链路与使用高速网络的多数参加者相连接。为了不强制所有会议参加者都使用低带宽和低质量的数据编码,RTP允许在低带宽区域附近使用混合器作为RTP级中继器。
混合器从一个或多个信源接收RTP 报文,对到达的数据报文进行重新同步和重新组合,这些重组的数据流被混合成一个数据流,将数据编码转化为在低带宽上可用的类型,并通过低速链路向低带宽区域转发。
- 为了对多个输入信源进行统一的同步,混合器在多个媒体流之间进行定时调整,产生它自己的定时同步,因此所有从混合器输出的报文都把混合器作为同步信源。
- 为了保证接收者能够正确识别混合器处理前的原始报文发送者,混合器在RTP报头中设置了CSRC标识符队列,以标识那些产生混和报文的原始同步信源。
用户是否被防火墙隔离
在Internet环境中,一些会议的参加者可能被隔离在应用级防火墙的外面,这些参加者被禁止直接使用 IP组播地址进行访问,虽然他们可能是通过高速链路连接的。
在这些情况下,RTP允许使用转换器作为RTP级中继器。在防火墙两端分别安装一个转换器,防火墙之外的转换器过滤所有接收到的组播报文,并通过一条安全的连接传送给防火墙之内的转换器,内部转换器将这些组播报文再转发送给内部网络中的组播组成员。
RTP结构解析
RTP报文由两部分组成:报头和有效载荷
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identitifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (SCRC) identifiers |
| ...... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
前12字节是固定的,CSRC可以有多个或者0个。
-
- V(version):RTP协议的版本号
占2位,当前协议版本号为2
- V(version):RTP协议的版本号
-
- P(padbit):填充标志
占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
- P(padbit):填充标志
-
- X(extbit):扩展标志
占1位,如果X=1,则在RTP报头后跟有一个扩展报头
- X(extbit):扩展标志
-
- CC:CSRC计数器
占4位,指示CSRC 标识符的个数
- CC:CSRC计数器
-
- M(markbit): 标记
占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
- M(markbit): 标记
-
- PT(paytype): 有效荷载类型
占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。
- PT(paytype): 有效荷载类型
-
- 序列号(seq_number):占16位
用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。
- 序列号(seq_number):占16位
-
- 时戳(timestamp)
占32位,必须使用90 kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
- 时戳(timestamp)
-
- 同步信源(SSRC)标识符
占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
同步信源是指产生媒体流的信源,它通过RTP报头中的一个32位数字SSRC标识符来标识,而不依赖于网络地址,接收者将根据SSRC标识符来区分不同的信源,进行RTP报文的分组。
- 同步信源(SSRC)标识符
-
- 特约信源(CSRC)标识符
每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。
特约信源是指当混合器接收到一个或多个同步信源的RTP报文后,经过混合处理产生一个新的组合RTP报文,并把混合器作为组合RTP报文的 SSRC,而将原来所有的SSRC都作为CSRC传送给接收者,使接收者知道组成组合报文的各个SSRC。
- 特约信源(CSRC)标识符
注:基本的RTP说明并不定义任何头扩展本身,如果遇到X=1,需要特殊处理
解析举例
80 e0 00 1e 00 00 d2 f0 00 00 00 00 41 9b 6b 49 €?....??....A?kI
e1 0f 26 53 02 1a ff06 59 97 1d d2 2e 8c 50 01 ?.&S....Y?.?.?P.
cc 13 ec 52 77 4e e50e 7b fd 16 11 66 27 7c b4 ?.?RwN?.{?..f'|?
f6 e1 29 d5 d6 a4 ef3e 12 d8 fd 6c 97 51 e7 e9 ??)????>.??l?Q??
cfc7 5e c8 a9 51 f6 82 65 d6 48 5a 86 b0 e0 8c ??^??Q??e?HZ????
取得这样一段码流
已知RTP Header是4个byte,那么我们取前32bit就可以分析得到这段码流的信息
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
|1 0|0|0|0 0 0 0|1|1 1 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 1 1 1 1 0 0 0 0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identitifier |
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (SCRC) identifiers |
| ...... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
类型 | 数据 |
---|---|
V_P_X_CC | 80 |
M_PT | E0 |
SequenceNum | 00 1E |
Timestamp | 00 00 d2 f0 |
SSRC | 00 00 00 00 |
把前两字节换成二进制如下 | |
V | 10 |
P | 0 |
X | 0 |
CC | 0000 |
M | 1 |
PT | 110 0000 |
RTP荷载H264码流
+---------------+
|0 1 2 3 4 5 6 7|
+-+-+-+-+-+-+-+-+
|F|NRI| TYPE |
+---------------+
荷载格式定义三个不同的基本荷载结构,接收者可以通过RTP荷载的第一个字节后5位识别荷载结构。
-
1、单个NAL单元包:荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元类型,即在范围1到23之间
-
2、 聚合包:本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27
-
3、slice单元:用于分片单个NAL单元到多个RTP包。现存两个版本FU-A,FU-B,用NAL单元类型 28,29标识
常用的打包时的分包规则是:如果小于MTU采用单个NAL单元包,如果大于MTU就采用FUs分片方式。
因为常用的打包方式就是单个NAL包和FU-A方式,所以我们只解析这两种。
单个NAL单元包
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| TYPE | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL Unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
定义在此的NAL单元包必须只包含一个。这意味聚合包和分片单元不可以用在单个NAL 单元包中。并且RTP序号必须符合NAL单元的解码顺序。NAL单元的第一字节和RTP荷载头第一个字节重合。如上图。
打包H264码流时,只需在帧前面加上12字节的RTP头即可。
分片单元(FU-A)
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
slice只定义于单个NAL单元不用于任何聚合包。NAL单元的一个slice由整数个连续NAL单元字节组成。每个NAL单元字节必须正好是该NAL单元一个slice的一部分。相同NAL单元的slice必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似的,NAL单元必须按照RTP顺序号的顺序装配。
当一个NAL单元被slice运送在slice单元(FUs)中时,被引用为slice NAL单元。STAPs,MTAPs不可以被分片。 FUs不可以嵌套。 即, 一个FU 不可以包含另一个FU。运送FU的RTP时戳被设置成分片NAL单元的NALU时刻。
上图表示FU-A的RTP荷载格式。
+---------------+
|0 1 2 3 4 5 6 7|
+-+-+-+-+-+-+-+-+
|F|NRI| TYPE |
+---------------+
如上图,FU-A由1字节的分片单元指示
+---------------+
|0 1 2 3 4 5 6 7|
+-+-+-+-+-+-+-+-+
|S|E|R| TYPE |
+---------------+
1字节的分片单元头(如上图),和分片单元荷载组成。
-
S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
-
E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的 FU荷载不是分片NAL单元的最后分片,结束位设置为0。
-
R: 1 bit 保留位必须设置为0,接收者必须忽略该位
打包时,原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位为FU header的后五位。
解析举例
取一段码流如下
80 60 01 0f 00 0e 10 00 00 0000 00 7c 85 88 82 €`..........|???
00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?
89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.
84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.
17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$
前12字节是RTP Header
类型 | 数据 |
---|---|
FU indicator | 7C |
FU Header | 85 |
把前两字节换成二进制如下 | |
F | 10 |
NRI | 11 |
FU Type | 11100 |
S | 1 |
E | 0 |
R | 0 |
NAI Type | 00101 |
打包时,FU indicator的F、NRI是NAL Header中的F、NRI,Type是28;FU Header的S、E、R分别按照分片起始位置设置,Type是NAL Header中的Type。
解包时,取FU indicator的前三位和FU Header的后五位,即0110 0101(0x65)为NAL类型。