QoS即服务质量,我们都知道WebRTC在实时通讯领域的地位,其中大部分得益于它内部集成的各种QoS策略,QoS策略包含在整个音视频pipeline中,比如在发送侧的编码动态调整、网络的均匀出包避免网络阻塞、网络抖动实时探测来反馈调整编码、丢包重传、接收端的buffer与卡顿的灵活调整、音频包播放补偿等等等等。
这里面的每项QoS策略单拿出来都可写很多篇非常专业的文章,这里我站在一个Web前端开发的角度,来梳理一下这些内容,尽量阐述清楚原理。这里我也参考了非常多的优秀文章,我会把链接放到每块内容中间,方便你查阅。
一、RTP/RTCP
RTP和RTCP是两种具体应用层的网络协议,RTP(the real-time transport protocol)用于发送音视频数据,RTCP(the RTP control protocol)用于反馈调节发送码率、控制重传等。如果想了解WebRTC中的QoS是如何实现的,需要掌握RTP与RTCP的格式,这两协议都见于rfc 3550
-
RTP简介
RTP协议最主要的功能就是传递音视频的payload,也可以携带各种扩展信息来实现网络质量的反馈。
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 |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
协议本身只挑几个重点:
-
SSRC可以标识一个track(SSRC在sdp协商可以看到)
-
sequence number是包的序号,根据这个序号,接收端就可以知道是否有丢包
-
M位通常是一个视频帧的最后一个包(一个视频帧要拆成好几个RTP包)
-
X是标识是否有扩展头,RTP有很多非常重要的功能都是写在扩展头里的,扩展头的信息是跟在CSRC(如有)后面的,RTC扩展头也分为单字节扩展头、双字节扩展头,甚至可以同时支持单、双扩展头(对应sdp描述里的a=extmap-allow-mixed),具体扩展头讲解可以看WebRTC研究:RTP报头扩展,最后说一下,扩展头的ID也是在sdp里协商的
-
RTCP简介
RTCP的通用头部格式如下:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P| xx | PT | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中PT指示出了RTCP包的类型,每种类型的实际负载是不同的,也各有作用,以下举出几种:
-
SR: Sender report,发送报告,值为200
-
RR: Receiver report,接收报告,值为201
-
SDES: 发送源的信息描述,值为202
-
XR: RTCP的扩展,值为207,在rfc3611中定义
-
RTPFB: RTCP的扩展(FB即feedback),值为205,在rfc4585中定义
xx在PT不同时有不同的含义,比如在SR和RR里表示report block的个数,在PT=205里是定义的子类型
看下SR和RR的包格式:SR和RR包结构比较类似,SR仅比RR多了一个sender info,结构类似方便发送端的RR报文可以通过SR发送,这里的包结构可以先跳过,下面讲到指标计算的时候可以回头看下
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P| RC | PT=SR=200 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of sender |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender | NTP timestamp, most significant word |
info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, least significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sender's packet count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sender's octet count |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_1 (SSRC of first source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1 | fraction lost | cumulative number of packets lost |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| extended highest sequence number received |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| interarrival jitter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| last SR (LSR) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last SR (DLSR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report | SSRC_2 (SSRC of second source) |
block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2 : ... :
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| profile-specific extensions |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
二、常见指标的计算
丢包、RTT、jitter是和网络息息相关的指标,WebRTC也会通过这些指标来动态调整,
-
RTT计算
1.1 在发送端通过SR&RR计算
以RFC 3550 6.4.1的例子来看下RTT是如何计算的
[10 Nov 1995 11:33:25.125 UTC] [10 Nov 1995 11:33:36.5 UTC]
n SR(n) A=b710:8000 (46864.500 s)
---------------------------------------------------------------->
v ^
ntp_sec =0xb44db705 v ^ dlsr=0x0005:4000 ( 5.250s)
ntp_frac=0x20000000 v ^ lsr =0xb705:2000 (46853.125s)
(3024992005.125 s) v ^
r v ^ RR(n)
---------------------------------------------------------------->
|<-DLSR->|
(5.250 s)
A 0xb710:8000 (46864.500 s)
DLSR -0x0005:4000 ( 5.250 s)
LSR -0xb705:2000 (46853.125 s)
-------------------------------
delay 0x0006:2000 ( 6.125 s)
先说明下图中的内容:
- 上下两条横轴为时间,上轴为发送端,下轴为接收端
- 发送端发了一个SR带了ntp时间戳,真实时间是3024992005.125 s -> SR包里的sender info信息
- DLSR=5.250 s是在收到上个SR包后,发RR前处理/等待的时间
- 接收端回复了一个RR,带了DLSR和LSR,LSR就是上一个SR包的ntp时间戳 -> RR包里的LSR和DLSR
仔细看下这个LSR是怎么得出来的,SR里的ntp是64位,前32位是整数,后32位表示小数,所以ntp_sec =0xb44db705和ntp_frac=0x20000000拼起来的时间就是3024992005.125 s,LSR取的是中间的32位,46853.125就是这么得来的
ntp_sec =0xb44db705 -> 0xb705=46853
ntp_frac=0x20000000 -> 0x2000=0.125 -> 表示小数
那么有了以上几个数值,我们就可以计算出RTT,当前时间(A 46864.500 s)-初始时间(通过LSR带回来 46853.125 s)-在接收端的耗时(DLSR 5.250 s),得RTT是6.125s
这块逻辑可以在source.chromium.org/chromium/ch…
1.2 在订阅端通过XR计算
上面计算RTT的过程我们看到是发送端使用SR和RR来配合计算的,那对于订阅端来说,他一直在发RR,没有办法通过上述过程来统计RTT,这时就引入了一个XR来解决问题,XR被定义在rfc3611中,XR是DLRR/RRTR等的统称,3611扩展的XR也并不只是用来计算RTT
使用XR计算RTT的原理和上面SR&RR一样,看下RRTR和DLRR包就会马上明白,RRTR带过去ntp,然后DLRR带回来ntp时间和在发送端的等待时间(注意此时变为发送端了)
RRTR
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=4 | reserved | block length = 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, most significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, least significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
DLRR
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=5 | reserved | block length |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC_1 (SSRC of first receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
| last RR (LRR) | 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last RR (DLRR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC_2 (SSRC of second receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
: ... : 2
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
2. ### jitter抖动计算
jitter是在接收端计算的,然后通过RR包的interarrival jitter发送,jitter的计算,RFC3550直接提供了一个公式:
D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si)
J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16
-
D(i, j) 直接定义了一个函数,Rj/Ri是接收时间,Sj/Si是发送时间,大概就是比较2个包接收时间差
-
J(i)是抖动数据,i-1是上一个包,套用上面的函数计算而得的值。需要注意的i-1,是上一个到达的包,并且要求seq连接的上一个
-
丢包计算
丢包数据是在接收端计算的,并在RR中有体现:
-
fraction lost:8位,表示的是丢包率 = (fraction_lost / 256) * 100%,是从发送上一个SR/RR后的丢包率,是一个周期值
-
cumulative number of packets lost:24bit,历史丢包总数,是一个累加值
丢包数的统计,和RTP协议的sequence number有关,seq是16bit,初始值是随机生成的值,发送的数据多了之后,seq达到极大值后又会回到从0开始,理论上收到包总数就是之前循环了几轮,加上本轮的seq最大值,当然还是减去那个随机的初始值。
extended_max = s->cycles + s->max_seq;
expected = extended_max - s->base_seq + 1;
那丢包的数量就按期望收到的包,减去实际收到的包即可
lost = expected - s->received;
三、音频
-
NACK(Negative ACKnowledgement)
我们都知道TCP协议是通过发送-应答(ACK)机制来确认发送包是否有收到,NACK工作原理正好与ACK相反,是没有收到包的时候给上行端发送一个NACK,让上行端重新发送。
浏览器默认没开音频NACK,也不会产生音频的RTX,需要手动开启,并且会使用原SSRC进行重传,需要低RTT时效果好。
NACK的协议包是由RTCP的PT=205实现的,这个RTCP的扩展在RFC4585中定义,其RTCP头为:
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| FMT | PT=205 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of packet sender |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of media source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: Feedback Control Information (FCI) :
: :
其中FMT可以表示下面FCI的类型,FMT=1为NACK,下面看下NACK的结构
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PID | BLP |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
结构非常简单,PID即为packet id,也就是包序号,表示第一个开始丢包的序号,BLP为16位,表示从PID开始,紧接着16个包的丢失情况,其中每一位表示一个包。一个NACK最多可以表示17个(PID + BLP16个)连续包丢失的情况,如果有更多包连续丢失,就要靠其他NACK包表示了。
在浏览器里的音频是默认关闭nack的,要打开音频nack的方式是修改sdp,手动添加nack的内容
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 nack
2. ### FEC(Forward Error Correction)
FEC是前向纠错码,就是在后面的包里携带之前包的冗余信息,如果之前的包丢失了,可以使用后面包的冗余信息随时恢复。FEC在实现又分为带内(inband)和带外,inband是说编码器内容实现的时候就会把冗余信息打进去,代表就是opus编码。带外FEC就和编码算法无关,可以理解为使用之前包编码后的数据,再进行FEC算法打到当前包内。
-
inband-fec(带内fec)
opus编码里的rfc里可以看到对于inband-fec的描述:前包音频信息是使用低编码模式。
概括:1. 只冗余前一个包的内容,恢复能力有限 2. 对30%以内的丢包效果好 3. 冗余信息会占用编码码率,如果同样码率开inband-fec音质会比不开的要差
sdp协商:
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
2. #### RED(带外fec REDundant coding)
RFC2198为RTP提供了一种冗余包的封装格式,它允许一个RTP包里携带多帧音频数据,优点是这些数据可以共用一个RTP头,RED的RTP头部与正常RTP头部还是有些区别,详细可以看下RFC
Chrome浏览器在M96正式开启了实现RFC2198的RED(而且还是highlights展示的):groups.google.com/g/discuss-w…
Highlights
Support for RFC 2198 redundancy
Audio redundancy for OPUS using RED is now available. See the PSA for details
并且在这个Demo上可以体验:webrtc.github.io/samples/src…
剖析下代码,在浏览器开启RED可以使用transceiver上的setCodecPreferences重排一下编码的顺序,如果不支持setCodecPreferences API就手动改下sdp。
最后看下效果,红线是码率、绿线是包头的码率,蓝线是目标码率
-
rs-fec(带外)
rs-fec对算力的要求比较高(使用Reed-Solomon算法,相对于一般的异或计算来说需要更高的算力),并且浏览器目前也不支持,了解可以看下技术解码 | RSFEC原理分析
-
NetEQ
NetEQ是WebRTC自带的一个核心的音频处理模块,其功能非常强大,实现也非常复杂,这里只提一下其中2个在前端可以监控到的指标
-
jitterBuffer
jitterBuffer是一个缓冲区,它位于接收到RTP后,解码之前,设置缓冲区可以解决因为网络抖动导致rtp/udp未按序达到排序,也可以对抗网络丢包,给重传包一点时间到达。
通过webrtc-internals工具可以清晰地看到这个值,需要注意的是这个值JS是需要经过计算得到,jitterBufferDelay是一个递增的值,然后有一个jitterBufferEmittedCount,二者相除就可以得到这段时间的jitterBuffer,单位是ms。
jitterBuffer在JS里是可以设置的,我们通过RTCRtpReceiver的playoutHint这个值可以设置额外增加的jitterBuffer(注意是在原有基础上额外增加的值),我们测试上面的Demo:
pc2.getReceivers()[0].playoutDelayHint = 1; // 注意单位是s
设置完成后,jitterBuffer升到1s,注意因为我的测试环境没有音频buffer,这个设置的值仍然是额外增加的时间,该值应用是一些对实时要求不高的场景,增加jitterBuffer来避免卡顿
除了jitterBuffer,我们看上图jb的右侧还有一个 jitterBufferTarget ,这个是在Chrome M124新增的特性,可以直接设置jitterBuffer的目标值,解决了原来不能自定义jb的痛点,但是一旦设置了如果在网络抖动的情况下可能会加大卡顿,下面测试下:
pc2.getReceivers()[0].jitterBufferTarget = 400; // 注意单位是ms
可以看到jb会平滑落在400
最后我们来看一下什么因素会影响jb,抖动会直接拉大jb,抖动表示当前网络的稳定状态,也有可能在rtt很小的情况下jitter变化强烈,进而增大了jb,除此之外在实践过程中发现web的页面卡顿也会拉大jb。
-
PLC(Packet Loss Concealment)
PLC为了对抗丢包的手段,其作用是在实际发生丢包后,根据上下文推测丢包的内容进行补充,但实际恢复能力有限,该指标可以通过pc.getStats中的concealedSamples体现
四、视频
-
Nack/RTX
音频流对于延迟很敏感,而且占用带宽不多,所以用 FEC 更好。WebRTC 默认并不为 audio 开启 RTX 视频流对于延迟没那么敏感,而且占用带宽很多,所以用 RTX 更好。
Webrtc 默认开启 RTX (重传),它一般采用不同的 SSRC 进行传输,即原始的 RTP 包和重传的 RTP 包的 SSRC 是不同的,这样不会干扰原始 RTP 包的度量。
-
FEC
-
RED
同上文音频-RED
-
ulp-fec/flex-fec
ULP Fec与 Flex FEC 概述_WebRTC_webrtc developer_InfoQ写作社区
-
Simulcast/SVC
Simulcast和SVC(Scalable Video Coding,可伸缩编码)是一种在发送端发送多个分辨率编码,订阅端按需订阅的方案,两种方案略有不同
Simulcast原理是在发送端有一个分辨率输入,然后编码多次,这样是比较浪费性能的,如果没有流被订阅还会浪费带宽。
SVC则是经过一次编码,将视频分为多空域和多时域,比如视频原30fps,在网络差的的情况下订阅接收到15fps也能播放视频,网络好的情况可以接收30fps,这样会比15fps更加流畅,这个是时域
空域则是将视频分为不同的层,接收到核心层(小分辨率)就可以播放视频,网络情况比较好接收到上层,就可以播放大分辨率视频,体验会更好
其他SVC介绍可以看这里
-
jitterBuffer
功能和用法可以参考上面音频jitterBuffer的内容,在浏览器上能控制的项不多
-
IDR、FIR、PLI
这三个概念都是和视频关键帧立刻刷新有关,说到之前先要回顾一下视频的编码,视频为了在网络中传输会把视频一帧帧的画面压缩,为了压缩到极致,会编码成I帧(关键帧,独立记录了一帧的画面)和P帧、B帧(参考帧),所谓有参考帧就是需要从I帧开始解码后,参考前一帧(或者前后帧)增量记录了变化的内容,如果没有I帧是无法单独渲染的。在WebRTC编码里只有I帧和P帧,因为P帧只是单纯参考前一帧,而B帧是需要前后参考,算法比较复杂,压缩率也会更高,WebRTC目前并没有采用,不过听说听说有人计划给WebRTC提标准去推动这个事情。
IDR(Instantaneous Decoding Refresh)在RTC领域十分重要,比如一个通话,一方开始编码了一个I帧,后面都是P帧,网络正常的情况下,双方可以一直愉快地视频下去,但出现网络波动丢帧了,或者加入一位新同学,这个时候就需要发送端重新编码一个I帧发送过去。
那WebRTC里实现IDR的方式有2种:
-
FIR(Full Intra Request)
-
PLI(Picture Loss Indication)
他们的在sdp里的描述会写在编码里的feedback里面,类似这样
a=rtcp-fb:<payload type> ccm fir
a=rtcp-fb:<payload type> ccm pli
那在实际效果上也是非常类似的(以我不专业的角度来看,二者效果是一样的),只是从语义上有所区别,比如FIR是一个主动请求,比如SFU/MCU会议里加入了一位新同学,这个时候SFU给发送端发一个FIR,而PLI是在接受端丢包严重组不成帧的情况下,给发送端发送的。
-
带宽评估及码率控制
带宽评估是WebRTC里最最最核心的内容,因为现实网络情况很复杂,你把控不住,所以需要一个评估模型来预测当前的网络情况,根据准确的网络评估才能进一步去控制码率来保障流畅地通话(写到这里的时候莫名想到三体里的各种预测恒纪元和乱纪元的模型)。
根据出现顺序评估模型有2种,分别是
- REMB(Receiver Estimated Maximum Bitrate):接收端估计的最大比特率,接收端根据自身接收到的数据,估算出所能承受的最大比特率,并将该信息反馈给发送端
- GCC(Google Congestion Control):WebRTC中广泛使用的拥塞控制算法。通过发送探测包、测量RTT和丢包率,动态调整发送速率,这个是目前主流使用的算法
他们在sdp里的描述如下:
a=rtcp-fb:<payload type> goog-remb
a=rtcp-fb:<payload type> transport-cc
最后还需要通过Pacing算法来控制数据包发送的速率,避免瞬时吞吐量过大造成的网络拥塞。
-
清晰度优先/流畅度优先
在网络情况实在不好的情况下,从算法方面的优化可能失效,在此情况下,使用者就要选择是清晰度优先,或者流畅度优先来维持基本的通话。
在浏览器中我们可以通过 MediaStreamTrack 实例上的contentHint属性来操作,比如可设定的值有text或者motion,分别就代表了清晰度优先和流畅度优先,还有一些其他的选项可以看MDN,兼容性也还可以接受。
教育场景在共享桌面会对清晰度有较高的要求,因此在实践中可以给共享屏幕设置上text,给人像设置成motion或者默认值。
X. RFC汇总
| RFC | 标题 | 描述(仅本文提到的内容) |
|---|---|---|
| 3550 | RTP: A Transport Protocol for Real-Time Applications | RTP及RTCP协议,及扩展格式定义 |
| 4585 | Extended RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/AVPF) | NACK |
| 3611 | RTP Control Protocol Extended Reports (RTCP XR) | XR(DLRR/RRTR) |
| 6716 | Definition of the Opus Audio Codec | Opus Codec |
| 2198 | RTP Payload for Redundant Audio Data | 音频RED |
| 5104 | Full Intra Request (FIR) | FIR |
| 4585 | RTP Profile for Audio and Video Conferences with Minimal Control | 包含了PLI |
写在最后
虽然内容比较浅显,但对于我来说还是查了大量的资料写成的,时间跨度上也长达半年(狗头),之前想写一个系列文章来记录一下自己在WebRTC相关工作的收获,也方便想从事该方向的前端同学入门的指引,想着如果以后不做WebRTC也可以留个痕迹。没想到先帝创业未半而中道崩殂,在写这篇文章的时候换了一个方向,只能是抽空写写,拖拖到今天才基本写完,内容深度也比我规划时要欠缺,只能下次一定。
规划的系列文章的话,已经写完:
- 一文看懂WebRTC建连过程 juejin.cn/post/732308…
- 一文看懂在浏览器里采集音视频(mediaDevices开启你本地视频之旅行)juejin.cn/post/692456…
- 一文看懂WebRTC Chrome调试工具 (WebRTC Internals工具在项目中的实践)juejin.cn/post/702290…
后面还规划有2-3篇,完成时间更是遥遥无期。
如果你一个Web前端,想要在WebRTC领域深耕,那只了解API使用是远远不够的,需要深入原理,深入RFC,深入源码。