阅读 89

WebRTC服务器设计小结

引言

上一篇我们分析了如何设计一套RTC PaaS服务?,从整体的角度讲述了设计一套完整的RTC PaaS服务需要的功能和服务。这一篇我们来侃一侃对服务器设计细节的思考。

首先还是重申一下我们面临的问题和挑战:

  • 网络延时(Latency/Delay)

  • 网络丢包(Packect Loss)

  • 网络乱序(Out-of-order delivery)

  • 网络抖动(Jitter/Delay variations)

  • 网络拥塞(Bandwidth Overuse/network congestion)

  • 服务的稳定性/高可用(service reliability/High Availability)

  • 服务的高并发(High Concurrency)

  • 服务的高性能(High Performance)

抛开后三项通用服务器的设计目标,前面几项一直是设计优秀的流媒体服务器,保证良好的音视频质量所必须兼顾的因素。下面很多设计的出发点和切入都与之息息相关。

调度服务器

我们以Improving Scale and Media Quality with Cascading SFUs (Boris Grozev)这篇文章作为切入点来讲一讲为什么要有调度服务器?以及调度服务器的主要职能和背后的思考。

正如文章中所讲,RTC整个系统的设计不仅要支持多个媒体服务器的灵活扩展,还要为每个入会用户提供最优的网络链路和媒体延时。因为实时通信应用对诸如带宽、延时、抖动和丢包率等网络状况非常敏感,更低的码率决定了更糟糕的视频质量,更长的网络延时意味着更长的音视频端到端延时,而网络丢包则往往会导致音频的断断续续和视频卡顿。这就是调度服务器存在的价值,为会议中的用户选择最优的网络链路,用于媒体传输。

首先,调度服务器需要管理所有的媒体服务器,需要知道媒体服务器的外网IP、服务端口、内网IP、地理位置、资源最大载荷、实时资源负载等信息,以便调度能够根据用户的信息分配最优的媒体服务器。注册+定时心跳很容易实现上述的需求,同时也比较好地支持媒体服务器(分布式SFU)的水平扩展。那么如何为用户分配最优的媒体服务器呢?

如果仅仅是按照“同一个会议的所有用户分配到同一台媒体服务器上”的策略,显然在跨国跨地域跨运营商的场景下无法实现网络链路和媒体延时的最优解。如下图所示,为星状拓扑,所有终端连接到同一台中心媒体服务器上,彼此推拉流,这种策略和中心服务器转发的视频会议架构对于用户的体验是很糟糕的,因为:

  • 用户远距离接入,尤其是跨国、跨地区时,传输链路质量没有保障。

  • 国内用户跨运营商之间接入,网络抖动大,丢包率高,影响会议质量。

  • 因CPU、内存和带宽等资源有限,单点媒体服务器容量和负载受限。

那么如何解决上述的问题?构建一个具备智能调度的实时传输网络呢?

一般情形下,整体原则如下:

  • 分区域/分运营商部署媒体服务器,用户通过接入服务实现就近接入,保障了最后一公里的质量。

  • 灵活/按需部署路由节点,通过路由分配媒体服务,能够根据实时网络质量选择最优的传输路径。

  • 需要保障传输网络内部的数据传输质量,如媒体服务器之间选择同VPC局域网或者不同VPC专线互联,云厂商保证其链路稳定性和可靠性。

实际拓扑结构如下图所示:在媒体节点都选择三线机房BGP的前提下,常用的调度策略如下:

  • 同一个房间/会议同一地域优先分配到同一个媒体节点,避免服务器级联

  • 按照地域优先的原则,同一区划的用户优先就近分配。

  • 按照负载优先的原则,要选择符合条件中负载最小的。

一般按照用户的分布去选点和部署,然后制定一定的区划策略(需要实际的数据收集来支撑)。比如在中国部署四个点:华北、华南、华东、西南,亚洲除中国以外都分配到新加坡的节点,欧洲都分配到法兰克福的节点等。

对于没有节点覆盖的地域用户,理论上在大池子里分配负载最低的节点。

其次,调度服务器需要支持最短路径智能路由选择和辅助实现媒体服务器级联。因为上面按照用户信息就近分配最优的节点,解决了最后一公里的问题,而媒体节点之间的路径不仅受节点健康状况和实际网络链路的影响,如服务crash、机房瘫痪、光纤挖断等,还受物理上网络拓扑的限制。如果节点的部署选择了阿里的云企业网,则不需考虑动态路由选择问题,交由云厂商去保证即可。

对于媒体服务器级联的实现,可以参考如何使用开源SFU构建RTC云服务中的思路,另外mediasoup的级联实现可参考Broadcasting of stream from One to Many using multiple Mediasoup Server instances。

媒体服务器SFU

对于SFU来说,基本功能是媒体包的转发和QOS保障,重点还是如何在复杂的公网环境下,利用UDP实时传输音视频数据,为用户提供低延时、清晰、流畅的音视频互动体验。

大的方向上,QOS可以从两个角度来思考和发散:

  • 信道编码的角度,如常见的ARQ/Nack丢包重传,FEC,Congestion Control(REMB/TransportCC),Jitter Buffer,IDR Request等。

  • 信源编码的角度,如可分层编码SVC,大小流Simulast,Long-Term Reference (LTR) frames等。

其中常用的QOS手段介绍可参考如下资源:

从入门到进阶|如何基于WebRTC搭建一个视频会议

聊聊WebRTC网关服务器4:QoS方案分析

webrtc视频QOS方法汇总

webrtc音频QOS方法汇总

WebRTC Qos优化杂记

在这里,不想过多地罗列名词解释和定义,感兴趣的小伙伴可参考上述资源自行查阅它们的不同和适用场景。

而对于很多QOS保障的设计手段和思想都是来自于TCP和Voip(Voice over IP)的传承,所以对于一些关键词和概念的理解可查阅借鉴对应领域的资源。

比如对于拥塞控制理论的学习,离不开对TCP 拥塞控制理论和算法的研究,可参考资源:

TCP拥塞控制图解(不包括RTO,因为它太简单了)

来自Google的TCP BBR拥塞控制算法解析

30张图解: TCP 重传、滑动窗口、流量控制、拥塞控制发愁

万字长文|全(小区局域)网最强TCP/IP拥塞控制总结...

万字详文:TCP 拥塞控制详解

TCP 的那些事儿(上)

TCP 的那些事儿(下)

WebRTC GCC拥塞控制算法详解

WebRTC基于GCC的拥塞控制(上) - 算法分析

WebRTC基于GCC的拥塞控制(下) - 实现分析

WebRTC基于TransportCC和Trendline Filter的发送端码率估计(Sendside-BWE)

实时视频传输中的BBR拥塞控

What is RMCAT congestion control, and how will it affect WebRTC?

实时视频流的拥塞控制-NADA,GCC,SCReAM

Bug: webrtc:9883 Removes unused BBR congestion controller

前一段刚接触Long-Term Reference (LTR) frames,感觉是一个好东西,虽然还没有机会实践,不过已知的好处:

避免频繁请求关键帧,减小网络冲击,有助于弱网对抗和缓解网络拥塞。

所以还是要安利给大家,详细的介绍可参考:

从入门到进阶|如何基于WebRTC搭建一个视频会议

视频编码长参考帧(LTR)

Improve Encoding Efficiency and Video Quality with Adaptive LTR

Cisco ClearPath Whitepaper

下面谈谈我的几点思考:

  1. 端到端/全链路丢包率 vs 上下行分段丢包率

原生的WebRTC是基于P2P场景设计的,QOS的策略也是端到端之间生效的,接收端发现丢包后,才会向发送端发送Nack请求重传,这种如果全链路的路径(RTT)过长,影响数据重传和恢复的效率。

如上图所示,在SFU架构下,重传请求不再是全链路反馈,而是在客户端和服务器之间分段进行。一方面,服务器具备NACK请求的能力,一旦感知到上行链路的丢包,及时向发送端请求重传。另一方面,服务器能够及时响应接收端的NACK请求。丢包重传的效率提升,有助于减少端到端延时,改善音视频体验。

从实现细节上,一方面可以选择不变Seq,接收到发送端的RTP包,修改ssrc,就直接转发给接收端,这样存在一个严重的问题,链路的下游感知到的丢包都是个累积丢包,即某个用户下行丢包了,有可能是真是用户与其就近接入的SFU节点之间下行网络很差,也有可能是链路的上游引入的丢包,如推流端的上行丢包或者媒体服务器和媒体服务器级联转发丢包。这样如果推流端上行很差,即使下游感知到了丢包,并发送Nack请求重传,也是要而不得的,这种无效的重传请求是非常低效的。 这种情况下接收端计算获得的丢包率是端到端/全链路的丢包率。

上面的设计显然是糟糕的,改进的手段就是经过SFU媒体节点转发时要区分original Seq和重新打上的新的Seq,original Seq可以用于计算全链路端到端的丢包率,而每一段重新打上的Seq用于计算该段下行的丢包率,这样只有在各自链路的每一段下行丢包,才会触发丢包重传请求,从而提升数据重传和恢复的效率。

  1. 端到端/全链路RTT vs 上下行分段RTT

首先我们参考Voice over IP End-to-End Delay Measurements的论述先大致展示一下端到端/全链路的延时有哪些:

当我们评估实时通信系统的性能和质量的时候,端到端延时是一个很重要的数据和参考标准。

对于客户端来说,由于需要决定渲染前的等待延时,全链路端到端的延时统计显的尤为重要。而对于SFU Nack重传控制也需要准确的RTT值,不过这个RTT值是上下行分段RTT值,对于分段RTT的计算可以采用RTCP-XR的方案。

  1. SFU需要对RTP进行reorder吗?需要Jitter Buffer吗?

这个问题只要想清楚Jitter Buffer的作用就可以了。顾名思义,抖动缓冲,通过在接收端维护一个数据缓冲区,来对抗一定程度的网络抖动,丢包和乱序,本质还是为了解码的丝滑,为了减少网络乱序和抖动对解码的影响。

SFU媒体节点只对音视频媒体包进行转发,不进行重新编解码,所以没必要进行缓存、重排,更不需要设计Jitter Buffer,因为一方面缓存会引入不必要的延时;另一方面用户侧在拉流播放解码渲染之前会对网络接收的媒体包进行缓存和重排,进而组帧,此时Jitter Buffer的设计需要考虑接收延时和卡顿之间的平衡。

其中客户端侧视频Jitter Buffer的控制逻辑可参考WebRTC视频JitterBuffer详解,音频请参考WebRTC的NetEQ。

webRTC中音频相关的netEQ(一):概述

webRTC中音频相关的netEQ(二):数据结构

webRTC中音频相关的netEQ(三):存取包和延时计算

webRTC中音频相关的netEQ(四):控制命令决策

webRTC中音频相关的netEQ(五):DSP处理

浅谈 WebRTC NetEQ

同样的道理,也就解释了为什么设计一个MCU混流服务器时必须考虑Jitter Buffer了。

  1. 1对多的场景,某个用户下行网络很差,丢包很高,如何避免影响所有人?

在1对多的视频会议场景下,各个接收端的带宽能力和设备性能不尽相同,如果因为某个接收端用户的下行网络状况很差,丢包很高,亦或是设备性能很差,强制要求对端发送端上行降低分辨率和码率,而影响到会议中其他用户的音视频体验,这种显然是不可取的。

解决这个问题,首先是进行分段的拥塞控制策略,理想情况下,发送端会根据上行的带宽估计值控制源端编码和发送码率,SFU则会利用下行的带宽估计值,来控制下发给各接收端的最高码率。

然而,现实问题是,当SFU只有一路视频可以转发时,如何根据各链路的带宽情况进行下发控制,有点巧妇难为无米之炊的感觉。这里要借助于两种源端编码策略 - Simulcast和SVC。

Simulcast:大小流的方式,指的是同时编码/发送多路视频流,比如常规发送一路720p,外加一路180p的流,这样在SFU下发给接收端的时候,可以根据下行带宽的限制,选择下发不同分辨率的流,照顾到每个端的体验。

应用Simulcast的系统示意:

SVC:可伸缩编码,使用基于层次的方法,提供时间或空间可伸缩编码组合。在RTC的应用中,通常会选用时域SVC,通过改变帧率来实现伸缩性。SFU可以根据下行的实际带宽,从同一路SVC视频流中解析出不同的时域分层,分别传输给各个接收端,同样可以实现差异化的视频流转发。注:该节论述大量引用从入门到进阶|如何基于WebRTC搭建一个视频会议中的论述和图片,如有侵权请联系删除。

  1. 实时通信带宽预测和拥塞控制算法是否需要兼顾公平性?一味地抢带宽是道德的吗?

这一块目前还没有想的特别清楚,先把问题抛出来。因为研究过TCP拥塞控制的都知道,在设计拥塞控制算法的时候要兼顾公平性的原则,而作为需要保证音视频质量的应用来说,是要保守、退让来兼顾公平性?还是多发多抢?目前心中还没有答案,但是感觉一味地抢带宽是不道德、不和谐、不美好的。

自研 VS 开源项目改造

目前比较热门的开源媒体服务器有:

开源项目编程语言网络IO库License
JanusC语言glibcGPL v3
MediaSoupC++/Node.jslibuvISC
LicodeC++/Node.jsboost AsioMIT
Open WebRTC Toolkit Media ServerC++/Node.jsboost AsioApache-2.0
Jitsi VideobridgeJava
Apache-2.0
IONGo
MIT
KurentoC++
Apache-2.0

网上已经有很多他们之间的比较,这里不作为今天的重点。我想讨论的是在前期选型的时候,该选择自研还是参考开源项目改造扩展?是选择C语言?C++?还是Go语言呢?

如果有着丰富的视频会议经验和一个牛逼的团队,自研肯定是无可厚非的,因为自己设计的系统当然有着100%的控制权,能够参考以往的经验选择最适合、最简洁的方案。而能否自研成功,当然是需要天时、地利、人和。 对于大部分小公司来说,改造一个优秀的开源项目,肯定是最佳的选择。

在这里,建议大家选择一个优秀的网络IO库或者框架和高效便捷的编程语言,无疑目前Go是比较完美的选择,你值得拥有。不过GC对于流媒体实时性的影响需要我们注意和研究。

欢迎大家留言交流,欢迎大家关注我个人公众号!你们的鼓励是我前进的动力


作者:angrychuan
链接:https://juejin.cn/post/6901682950673760269


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐