webrtc twcc接收端处理在Nginx RTC SFU 服务端的实现
twcc简介TWCC全称是Transport wide Congestion Control,是webrtc的最新的拥塞控制算法。其原理是在接收端保存数据包状态,然后构造RTCP包反馈给发送端,反馈信息包括包到达时间、丢包状态等;在发送端进行带宽估计,进行拥塞控制。发送方带宽估计有什么好处?谷歌解释的理论是,通过这种方式,所有的决策逻辑都在一个地方(发送方),因此可以轻松测试新算法,因为你不依赖两
twcc简介
TWCC全称是Transport wide Congestion Control,是webrtc的最新的拥塞控制算法。其原理是在接收端保存数据包状态,然后构造RTCP包反馈给发送端,反馈信息包括包到达时间、丢包状态等;在发送端进行带宽估计,进行拥塞控制。
发送方带宽估计有什么好处?谷歌解释的理论是,通过这种方式,所有的决策逻辑都在一个地方(发送方),因此可以轻松测试新算法,因为你不依赖两个端点。老实说,考虑到浏览器自动更新,我不认为这一点有什么大的优势,但它肯定更干净,即使它在带宽使用方面更昂贵。另一个优点是,发送者知道他正在发送的流量类型,并且可以在发送普通视频时使用不同的算法,例如在进行屏幕广播时。我们受到影响了吗?如果您正在构建的媒体服务器需要对任何内容进行带宽估计(例如,在使用simulcast时决定转发的质量),您将需要在某个时候升级您的实现。好消息是Chrome必须支持旧机制(REMB)一段时间,至少在Firefox支持之前是这样。但是REMB可能不会得到更多的改进,而且它现在更有可能出现bug,所以推迟更改可能不是一个好主意。Chrome 55的默认设置是发送端带宽估计,但这项工作仍在进行中,我们预计会有很多变化。官方标准化正在RMCAT集团的IETF中进行,但Chrome中可用的大部分实现都是谷歌自己版本的算法和反馈协议的进行中规范。(古斯塔沃·加西亚)
传输序列号
在传输层上添加RTP报头扩展,同通过同一连接发送的所有数据包使用相同的计数器。这样做有两个好处:
1、更适合拥塞控制,因为拥塞控制器不在媒体流上运行,而是在数据包流上运行。
2、允许更早的数据包丢失检测(和恢复),因为当接收到来自流B的数据包时,可以检测到流a中的丢失,因此我们不必等到接收到流a的下一个数据包。
RTP报头扩展格式
RTP报头扩展,在发送的所有数据包上附加一个16位序列号。对于通过同一套接字发送的每个数据包,该序列号递增1。
如何使用该扩展,需要在SDP交互中添加信息。
SDP Request :a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions
TWCC
为了给发送端最大的自由度,需要提供每个数据包的信息给发送方。实现这一点最简单的方式上让接收方回复一条消息,信息中包含数据包的到达时间戳和每个数据包是否到达的标识。
建议为接收到的每一帧发送反馈消息,但在上行链路带宽较低的情况下,可以减少发送它们的频率,例如,每RTT发送一次,以减少开销。通过这种方式,接收方只需简单记录数据包到达时间戳(A)。发送端保存传输中的数据包信息,在收到反馈后,它会查找相应数据包的在线时间戳(S)。发送方通过这两个时间戳可以计算相应的数据指标:
数据包间延迟变化:d(i) = A(i) - S(i) - (A(i-1) - S(i-1))
估计排队延迟:q(i) = A(i) - S(i) -min{j=i-1..i-w}(A(j) - S(j))
由于发送方收到了每个数据包的反馈,与以接收方决定的恒定速率发送数据包相比,它可以更好的苹果发送突发数据包的成本。
发送方估计的两个缺点是:
无法区分下行链路上丢失反馈和伤心链路丢失数据包。
增加反向反馈率。
从拥塞控制的角度来看,丢失的反馈消息是通过忽略在丢失的反馈消息中报告为丢失或接收的数据包来处理的。此行为类似于处理丢失的RTCP接收器报告的方式。
建议为接收到的每一帧发送反馈消息,但在上行链路带宽较低的情况下,可以减少发送它们的频率,例如,每RTT发送一次,以减少开销。
TWCC报文格式
version:RTP版本号。当前版本号是2.
padding:1位。填充位表示数据包末尾包含额外的填充八位字节,这些八位字节不属于控制信息的一部分,但包含在长度字段中。
feedback message type:5位,表示是FB消息,值必须是15.
payload type:8位,这是将数据包标识为RTCP FB消息的RTCP数据包类型。该值必须为RTPFB=205。
SSRC of packet sender:32位。数据包的发送方的同步源标识符。
SSRC of media source:32位。与此反馈信息相关的媒体源的同步源标识符。
base sequence number:16位。基本序列号,此反馈中第一个数据包的传输范围序列号。并非每次反馈都会增加这个数字;在重新排序的情况下,它可能会减少。
packet status count:16位。此反馈包含状态的数据包数,从基本序列号标识的数据包开始。
reference time:24位有符号整数,表示反馈数据包发送方选择的某个基准时间的绝对参考时间。该值应以64ms的倍数进行解释。该数据包中的第一个recv增量与参考时间有关。由于参考时间总是使用相同的时基,因此即使某些反馈数据包丢失,也可以计算反馈之间的增量。
feedback packet count:8位。每发送一个反馈数据包,计数器递增一。用于检测反馈数据包丢失。
packet chunk:16位, 数据包状态块的数组。这些数据包指示以基本序列号标识的数据包开始的多个数据包的状态。
recv delta:8位,对于每个“数据包已接收”状态,在数据包状态块中,将跟随一个接收增量块。
数据包状态标记
使用2位符号描述数据包的状态:
00 数据包未收到
01 数据包收到,较小的间隔。
10 数据包收到,大间隔或为负数。
11 保留。
状态为“未收到”的数据包不一定被解释为丢包。他们可能还没到。对于在+/-8191.75ms内以增量接收到的每个数据包,将接收增量块附加到反馈消息中。
注意:如果基本序列号减少,则会创建一个与之前的反馈消息重叠的窗口,之前报告为已接收的任何数据包的状态必须标记为“数据包未接收”,因此该符号不包含增量。
数据包状态块
丢失RLE报告块。以下是两种不同的块:
Run length chunk:行程长度编码数据块
Status vector chunk:状态矢量编码数据块
Run length chunk
行程长度块以0位开始,后跟数据包状态符号和该符号的行程长度。
chunk type (T):块类型,1位,值为0,表示是行程长度块。
packet status symbol (S):包状态标识,2位,该符号在本行程中重复出现。
run length(L):行程长度,13位无符号整数,表示行程长度。即有多少连续包为相同状态。
包状态:00,表示包未收到,行程长度11011101,为221。整体表示为有连续221个包未收到。
表示连续24个包已收到,并且无接受增量。
Status vector chunk
状态向量块以1位开始,将其标识为向量块,然后是符号大小位,符号位决定后面是7个或14个包的状态。
chunk type (T):1位。值1表示其是状态矢量编码数据块。
symbol size(S):1位。为0表示当前编码数据块的每一位代表“包收到=1”和“包未收到=0”两种状态。意味着总共可以表示14个包状态。为1意味着两位表示一个包状态,可以总共表示7个包的状态。
sysbol list:14位数据包状态符号列表。
chunk type (T)=1,表示当前是状态矢量编码数据块。symbol size(S)=0表示其后每一位表示一个包状态。sysbol list:表示14个包状态,6个包未收到,8个包收到。
chunk type (T)=1,表示当前是状态矢量编码数据块。symbol size(S)=1表示其后每两位表示一个包状态。sysbol list:表示7个包状态。
00--“包未收到”,“11”--包收到,w/o timestamp,3个“01”--包收到,较小时间间隔,2个“00”--包未收到。
Receive Delta
Delta表示为250us的倍数。
1、如果“packet received,small delta”添加到packet chunk中,则8位无符号的recv delta会添加到receive delta列中。由于receive delta以250us的倍数,所以8位无符号整数的receive delta表示范围是[0, 63.75]ms。
2、如果“packet received,large or negative delta”添加到packet chunk中,则16位有符号的recv delta会添加到receive delta列中。表示[-8192.0,8191.75]ms范围内的增量。
3、如果增量甚至超过了更大的限制,则必须使用新的反馈消息,其中24位基本接收增量可以覆盖非常大的范围。
请注意,第一个接收增量与基本接收增量指示的参考时间有关。
接收增量上限为63.75 ms的较小值意味着,这仅在每秒1000/25.5~=16个数据包及以上的情况下可行。数据包大小为1200字节/数据包,相当于约150 kbit/s的比特率。
0.25毫秒的分辨率意味着每秒最多可以代表4000个数据包。对于1200字节/包的有效负载,这相当于38.4 Mbit/s的有效负载带宽。
packet chunk以及receive delta的使用是为了尽可能减小RTCP包大小。packet chunk用到了不同编码方式,对于收到的RTP包才添加到达时间信息,而且是通过时间间隔的方式记录到达时间。
具体实现
利用Nginx创建RTC module作为SFU服务器,并在其中实现了TWCC的接收端处理。
1、对每一个publish流,创建twcc数据成员。
2、解析接受到的RTP数据包,将RTP扩展序列号和接收到当前数据包的时间戳保存到twcc中。采用Nginx中的红黑树保存数据包信息。
3、对每一个publish流,启动Nginx timer定时发送twcc的RTCP反馈包。
4、遍历twcc成员中的红黑树,可以获取到当前已经接受到的包信息,如果RTP包的扩展序列号不连续,说明存在未收到的包。通过这些信息构建packet chunk和delta chunk。
更多推荐
所有评论(0)