音视频RTP数据包封装
对于语音通信而言,语音码率较低,添加适当冗余是对抗网络丢包的常见方式。冗余方式有多种,包括RED
,FEC
等都是冗余的一种,如果冗余份数较多,可以采取交织的方式实现。RFC 3350
是RTP的基础标准协议,RFC 2198
是冗余数据RTP封装的标准协议,RFC 5109
是添加FEC数据的RTP封装标准协议。
RTP格式(RFC 3350)
文档地址:RTP: A Transport Protocol for Real-Time Applications
RTP(Real-time Transport Protocol, 实时传输协议)是互联网上常见的处理媒体数据流的网络协议,包括单播和多播等多种场景下的网络环境中媒体数据的传输。RTP是一种应用层协议,一般使用UDP作为底层协议实现数据传输,但并不强制底层协议的选择。RTP不提供任何机制来保证实时的传输和服务质量保证,而是由底层的服务来完成。也就是说,它不保证可靠传输和按序传输,不假定下层网络是否可靠,不限制按照顺序传送数据包。
RTP一般与RTCP同时出现,端口号相邻。一般而言,RTP负责传输数据,RTCP用于传输控制信息,比如提供数据传输质量的反馈。RTCP为每个RTP源提供一个固定的识别符CNAME
。当SSRC因重启或者冲突发生改变时,可以更加CNAME
跟踪参与者;或者用CNAME
关联一系列相关RTP会话中来自同一个成员的多个数据流,如用来同步音频和视频。注意需要控制RTCP流的快速增长,一般不超过总占用带宽的5%。
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 |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中,主要字段有:
V
,version,2比特,RTP版本标识,定义了两个版本。第一个RTP草案版本使用1标识,在vat
音频工具的最初实现中使用0标识。P
,padding,1比特。如果设定padding,在报文末端会包含一个或多个padding字节,这不属于负载playload。最后一个字节的padding有一个计数器以标识需要忽略多少个padding字节(包括自己)。一些加密算法可能需要固定块长度的padding。X
,extension,1比特。如果 X=1,则在 RTP 报头后将有且仅有一个扩展报头。CC
,CSRC count,4比特。CSRC count标识了定长头字段中包含的CSRC identifier的数量。M
,marker,1比特。由具体协议解释其意义,用来在比特流中标记重要的事件,如音视频帧边界。PT
,payload type,7比特。payload type设置RTP负载的格式,由应用程序解释该字段含义。接收方必须忽略未知的playload type数据包。sequence number
,16比特。序列号,每发送一个RTP数据包,序列号加1,接收端可以据此检测丢包和重建包序列。timestamp
,32比特。时间戳,反映了RTP数据包中第一个八位字节的采样时间。时间戳的初始值应当是随机的,类似序号。时钟频率依赖于负载数据格式,因此时间戳增量依赖于当前数据格式和策略。如果RTP数据包周期性产生,那么将使用采样时钟确定的标称采样时刻,而非读取系统时间。举例而言,对于固定采样率的音频,时间戳时钟可能会在每个采样周期增加1;如果音频应用程序从输入设备读取覆盖160个采样周期的块,则对于每个这样的块,时间戳将增加160,无论该块是在分组中传输还是作为静默丢弃。SSRC
,Synchronization source,32比特。同步源,接收方可以将一个同步源的包放在一起进行重放。SSRC是一个随机值,在特定的RTP会话中是全局唯一的。CSRC
,Contributing source,0~15项,32比特。若一个 RTP 流的源,对由 RTP 混频器生成的组合流起了作用,则它就是一个作用源。
RED格式(RFC 2198)
文档地址:RTP Payload for Redundant Audio Data
RTP本身不包含任何的质量保证,因此面对网络扰动时表现较差,因此在通信过程中增加冗余数据,即使存在丢包,也可以在一定程度上恢复。RFC 2198
主要针对音频数据。
- 每个包必须携带一个主包(主编码数据),以及一个或多个冗余编码数据;
- 冗余信息可以有多种形式,但每一个冗余块必须有一个编码类型标识;
- 可能存在变长编码,因此每一个数据块都存在长度标识;
- 每个编码块都有自己的时间戳,对于冗余块可以参考与主包的时间戳差值。
增加RTP头扩展或采用新的一个或多个负载类型,即使用PT
标识。鉴于使用扩展头会带来很多限制,因此建议采取新增负载类型的方式。在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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F| block PT | timestamp offset | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中:
F
,1比特,标识后面是否还存在其它的块。设置为1表示存在;0表示不存在,该块为最后的头块。block PT
,7比特,该数据块的RTP playload type。timestamp offset
,14比特,相对于RTP header中时间戳的偏移量,使用unsigned表示。block length
,10比特,对应于除了头长度的有效负载长度。
主包头位于整个数据包的最后,PT
和timestamp
和RTP数据包头相同,格式如下:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0| Block PT |
+-+-+-+-+-+-+-+-+
最后一个头之后就是数据块,存储顺序和头的排序顺序相同。数据块之间不需要填充或者使用其它分隔,一般不需要32位对齐。主要是为了在损失一定的解码时间降低宽带负担。如下包括一个DIV4(8kHz)主包和8kHz的LPC冗余编码包:
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=0 |M| PT | sequence number of primary |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp of primary encoding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1| block PT=7 | timestamp offset | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| block PT=5 | |
+-+-+-+-+-+-+-+-+ +
| |
+ LPC encoded redundant data (PT=7) +
| (14 bytes) |
+ +---------------+
| | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ +
| |
+ +
| |
+ +
| DVI4 encoded primary data (PT=5) |
+ (84 bytes, not to scale) +
/ /
+ +
| |
+ +
| |
+ +---------------+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
RED SDP协商
在进行双方的通信协商时,需要通知双方的SDP(Session Description Protocol,会话描述协议)完成信息交互,协议中建议的SDP形式如下:
m=audio 12345 RTP/AVP 121 0 5
a=rtpmap:121 red/8000/1
a=fmtp:121 0/5
其中,121为冗余编码的PT值,0为PCM主编码,5为DVI4第二编码。
一般FEC的RTP格式(RFC 2733)
文档地址:An RTP Payload Format for Generic Forward Error Correction
在网络上,语音质量不佳的主要原因在于高丢包率。也正是基于此,FEC才被考虑解决丢包的问题,为了更好的应用这些纠错码,必须有相关的协议支持。
前置知识
FEC(Forward Error Correction,前向纠错)可以确保信号的长距可靠传输。FEC技术是一种广泛应用于通信系统的编码技术。以典型的分组码为例,在发送端通过将k比特信息作为分组进行编码,加入(n-k)比特的冗余校验信息,组成长度为n比特的码字。码字通过信道到达接收端之后,如果错误在可纠正的范围之内,通过译码就可检查并纠正错误的比特,从而抵抗信道带来的干扰,提高通信的可靠性。校验位长度(n-k)与信息位长度k的比值,称为编码开销。开销越大,FEC的理论极限性能越高,但性能的提高并不是线性增加的,开销越大,增加的性能提升就越小。
RFC 2733
RFC 2733定义了一种传输实时媒体的一般性的RTP负载格式,一般性意味着:
- FEC协议独立于其保护的媒体数据,该媒体数据可以是音频、视频或者其它;
- 具有足够的灵活性以支持不同的FEC机制;
- 自适应设计,使得FEC技术修改但不影响传输的信令;
- 支持许多不同的传输FEC包机制。
基本原理
RFC 2733负载格式支持基于异或校验算法的FEC机制。发送端在媒体流中取出一些包,并对其负载、RTC头中的组件进行异或操作,得到包含FEC信息的RTP包,产生的FEC包可以在接收端恢复与这个FEC包关联的任何一个包。实际使用中,是时延以及恢复能力之间的一种平衡。
为了能正常恢复,发送方还需要在负载格式中携带信息,包括哪些媒体包用来生成FEC包。接收端可以不了解纠错码的细节,使用FEC,发送端具有较大的灵活性并可根据网络环境进行自适应的编码,而接收端仍能正常恢复。FEC包在生成后立即发出,接收端即使不支持FEC也不影响媒体流的正常接收。但是也存在一些FEC纠错码,不传输原始媒体流,只使用FEC流,这样做的缺点在于所有接收者都必须支持FEC。
FEC包可以不与媒体包在同一个RTP流中发送,以一个单独的流进行发送,或者作为以各种冗余编码发送(如RFC 2198)。当作为单独流发送时,FEC包具有独立的序列号空间,但时间戳从对应的媒体包获得,单调递增。FEC包流支持任何固定差值的包头压缩机制,对接收端来说,如果没有丢包,则忽略FEC包,如果出现丢包,则FEC联合收到的关联生成的媒体包组,完全恢复丢失媒体包。
通用FEC方案
定义函数\(f(x,y,...)\)对包\(x,y,...\)等的异或操作,被叫做奇偶校验包。为简单起见,奇偶校验包就是输入的各个输入包按位异或得到的,而使用奇偶校验包对数据包进行恢复是通过产生这些校验包的一组数据包完成,这一组数据包必须是线性无关的。
举例而言,若两个数据包产生一个校验包。原始数据包\(a,b,c,d\),发送端产生数据包如下,时间从左至右:
a b c d <-- media stream
f(a,b) f(c,d) <-- FEC stream
如果数据包\(b\)丢失,则可以通过\(a\)和\(f(a,b)\)恢复。
-
方案一
a b c d e <-- media stream f(a,b) f(b,c) f(c,d) f(d,e) <-- FEC stream
方案一类似于上面的示例。但是在发送数据包\(b\)之前发送\(f(a,b)\)。该方案会增加发送端延迟,但允许对连续两个数据包突发丢失的恢复。
-
方案二
f(a,b) f(a,c) f(a,b,c) f(c,d) f(c,e) f(c,d,e) <-- FEC stream
原始数据包并不是必传的,在方案二中仅仅发送FEC数据包。该方案允许所有单数据包和一些连续数据包的丢失恢复,但开销略低于方案一。
-
方案三
a b c d <-- media stream f(a,b,c) f(a,c,d) f(a,b,d) <-- FEC stream
该方案允许从连续一个、两个或三个数据包丢失中恢复。
通用FEC的RTP格式
如果FEC作为独立流发送,媒体数据包格式与传输不受影响;如果作为冗余编码发送,参考RFC 2198将媒体数据作为主数据包,次编码传输FEC包。
通过在RTP负载中放置FEC头和FEC有效载荷来构造FEC包:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Payload |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+