//
编者按:视频会议已成为日常办公不可或缺的一部分,为远程交流的人们提供了许多便利。本次RTSCon 2022会议,由RTS社区和LiveVideoStack音视频技术社区联合出品,很荣幸地邀请到了来自vivo的架构师李莹莹老师,为我们介绍了FreeSWITCH架构、vivo自研的视频会议架构、以及在实践过程中应用到的一些方案等。
文/李莹莹
整理/LiveVideoStack
大家好,我是来自vivo AI研究院的李莹莹,本次我的分享主题是FreeSWITCH在视频会议中的实践。首先介绍下FreeSWITCH。
01 FreeSWITCH简介
FreeSWITCH是一个软件的电信交换平台。感兴趣的同学们可以看一下杜金房老师写的这本《FreeSWITCH 权威指南》。经过两年多的实践,我们认为FreeSWITCH的是一个功能全面、性能高效、容易扩展、文档完整的项目,很适合年轻的团队来入门。
1.1 FreeSWITCH框架
FreeSWITCH的框架很像是一个披萨,它有一个核心层,还有多个外围模块。核心层里面定义了一些关键数据结构、状态机和框架流程,同时它还提供了一个公共应用程序接口,供外围接口去调用。它是一个抽象并稳定的框架。它的外围接口是根据核心框架里面定义的抽象接口来实现具体的业务逻辑,就像一个钩子一样嵌入到整个框架中。上图列出了几个主要的模块(接口),例如终点模块,一般终端都会接入这个模块。我们主要会用到应用程序里面的会议模块。除此之外像应用程序、编解码器、事件套接字等有很多可实现的接口。
1.2 FreeSWITCH会议模块
下面介绍一下FreeSWITCH会议模块。我们整个的会议系统基本上是围绕FreeSWITCH的会议模块来做的。上图所示的是FreeSWITCH会议模块实现的一些融屏方案。比如它可以支持3x3的视频会议布局,还有突出显示演讲者的布局视图等等。上图右侧列举了一些会议中的常用操作,如创建会议、查询会议、邀请入会、提出会议、成员禁言、成员禁听,以及安全相关的会议密码、会议锁定和会议录音等这些都是FreeSWITCH所支持的,可以满足视频会议中的基本操作,所以我们也称它是一个完备的MCU视频会议解决方案。
02 vivo自研视频会议架构
我们vivo自研的视频会议架构主要是围绕FreeSWITCH和它的视频会议模块来建设的。在终端层我们基于WebRTC开发了一套跨平台的SDK,支持Windows、Android、iOS等平台。在云端对外接口方面,我们提供了三类协议的端口,包括http、SIP和媒体等协议接口。http接口主要处理的是与业务逻辑相关的工作,如用户的账号管理、会议创建、会议预定、会议要求、日程安排等功能,它对应到我们自研的会议管理服务。SIP接口主要是在加入会议时,实现会议远端的位置信息以及媒体信息的交换。媒体接口包括传输的RTP、RTCP、Stun等媒体相关协议,实现会话中音视频数据在会话两端的实时传输。
FreeSWITCH是一个有状态服务,为了实现FreeSWITCH的集群化部署,我们自研了一套方案,在FreeSWITCH前面架设了自研的SIP代理服务和媒体代理服务,这两个服务都是无状态的,主要是做会话路由和媒体路由。经过这样的处理,我们实现了低延时、高清、流畅、易扩展、高并发、稳定的一套会议系统。
03 媒体端口复用方案
3.1 方案1:公网信令端口和媒体端口分片
接下来介绍下一些我们的优化方案,首先是媒体端口复用方案。什么是媒体端口复用?就是这里云端对外只提供一个媒体传输的端口,所有终端都是通过这个端口跟云端进行媒体交换,包括发送数据、接收数据。
再来介绍下我们在没有端口复用方案之前是怎么做的。我们知道媒体数据基本上都通过UDP来传输的,而UDP是无链接的,所以很多媒体服务器都是通过端口来区分不同的会话,而不是通过链接,FreeSWITCH也不例外,这个认知是很重要的。所以在这个基础上我们既要做到对外只提供一个公网,又要满足FreeSWITCH这个需求。我们之前做了一个媒体端口的分片,只提供了一个公网IP。我们知道每个公网IP最多对应了65535个端口,但我们又要做一个FS集群,该怎么办呢?我们想到的第一个方案是将端口进行分片,例如10000-19999分给FS1,20000-29999分给FS2,这样就能够满足FreeSWITCH根据不同端口来区分不同的会话。它的交互流程是终端先拨号加入一个会议,请求到会议管理服务,然后会议管理服务根据会议号,将会议分配到一个FS服务器。当FS服务器接收到这个通知,根据它能够用到的公网端口,给它分配到一个公网端口,再通过管理服务告诉终端用户,后面FS用这个公网端口来进行媒体数据的接收和发送,这是原始的一方案。这个方案很明显的一个缺点是我们一个业务就占用了一个完整的公网IP,这很浪费公网资源。另外一个缺点是一个公网IP最多有65535个端口,对用户并发数是有限制的,超过这个数的话,我们的系统就不行了。第三个缺点是公网端口与FS映射关系是固定的,很难去扩充,比如我们加一个FS或减一个FS要做很多的配置。第四个是公网端口映射关系,要映射几万条这种配置对变更来说也是很困难的。
3.2 方案2:用信令和媒体代理管理会话路由
基于这些痛点,我们其实要解决的是对外端口的数量限制以及FreeSWITCH要求用端口来区分不同会话等主要矛盾。经过我们的实践,我们可以做到公网只提供一个端口并且满足FS的要求。我们通过自研的无状态的SIP代理和无状态的媒体代理服务来完美地解决了这个矛盾。这两个服务的主要核心思想是建立终端网络地址IP和端口与FS的IP和端口做一一映射关系,然后终端把数据发送到媒体端口。比如有一个9527的媒体代理,我们知道它的原IP和端口之后,就把它发送到对应的FreeSWITCH上面去。这样的关系总体上是一个比较简单的方案,这个映射关系就像是路由表。
04 MCU&SFU终端并存方案
4.1 MCU和SFU简介
再来和大家介绍下MCU&SFU终端并存方案。之前这套方案主要是我们与思科对接而做的。思科用的是一个传统硬件的MCU方案,所以我们开始也是想用MCU架构。但是后面我们想用软件自研的终端,也要对接SFU这些终端,所以我们需要把MCU和SFU都去实现。如上图左侧所示,这是一个MCU的示意图,有四个人将他们的视频上传到云端,云端经过混流成为一个视频再发送给所有人。这就是MCU,它需要做转码。上图右侧是一个SFU的示意图,两个人将自己的视频上传到云端,没有转码,只是把他们两个人的视频再分别发给对方。
4.2 MCU和SFU对比
我们再来对比下MCU和SFU的不同。首先本质上的不同,MCU需要服务端混流转码,而SFU是直接转发。对用户体验来说,所有用户在MCU下看到的是一路相同的视频,用户体验可能不是很好。但SFU是不同用户可以自由选择观看多路不同的视频。在架构实现上,MCU的终端是一路流,实现起来比较简单,但服务端需要混流,相对复杂一些。SFU在这点刚好相反。在功耗方面,MCU在终端上只有一路流,它只解码一路,功耗相对是比较低的。而服务端它需要接收所有的流并且需要转码混流,在这里它的功耗比较高。SFU在功耗方面也是相反情况。从流量方面来说,MCU只消耗一路流,对终端来说它的流量消耗比较低。而SFU它可以下发多路视频流,它的流量来说是相对高的。在适用性方面,MCU由于做了服务端转码,它可以适应不同编解码终端设备的差异化。而SFU因为它只做了转发,所以它要求发送端和接收端需要具有相同编解码能力。在实时性方面,由于MCU需要做转码所以它的延时要高一些。
4.3 FreeSWITCH的MCU方案
我们再来看下FreeSWITCH的MCU方案。如上图所示,这里有四个用户,前面两位用户分别上传了自己的视频到云端,然后FreeSWITCH对他们的视频流进行解码,放在一个画布上。然后第三和第四位用户也同样放到另一个画布上,这是上行部分。在下行部分中,第三位用户要看第一个画布,那么FS上就会生成一个编码器,把第一个画布的流编码后发给第三位用户。第四位用户要看第二个画布,就会用另外一个编码器去编码第二个画布的流然后发送给第四位用户。当第一位用户也想去看第二个画布的时候,这时还会有一个编码器去编码,再发送给第一位用户。我们对此总结:1.每个上行视频都有自己的解码器,这里有四位用户就会有四个解码器。2.可同时有多个混流画布,这对我们后面的方案来说是一个基础。3.每个上行视频混流在指定的画布,如第一位用户上传的视频要么混流在第一个画布,要么混流在第二个画布,它不能同时混流在多个画布里面。4.每个下行视频都有自己的编码器,像上图有三位用户要观看视频,那么就会有三个编码器。5.每个终端只能看一个画布,这是传统的说法。如果一些终端支持像BFCP这样的双流协议,其实是可以看两个画布的。6.不同的终端可以看不同的画布。这些是FreeSWITCH的MCU原始方案。
4.4 魔改FreeSWITCH为MCU&SFU并存方案
在这个基础上,要实现SFU方案,我们对此做了许多改动。首先,我们有两类终端,MCU终端和SFU终端。然后我们把画布归为两类,一个是可以混流,把多路流放在一个画布上面,我们称之为混流画布,第二个是只能放一个视频的称之为单流画布。当MCU终端将视频上传,经过解码之后,我们会把它的流放在单流画布上,也会放在混流画布上,SFU终端同理,这是上行方面。在下行部分,MCU终端用户想看MCU混流画布,就会有一个编码器来编码混流画布并发送给MCU终端。对于SFU终端用户,他可以看多个画布,当他想看混流画布时,可以直接复用前面的编码器,这也是我们对它的一个改进。在MCU方案的基础上,我们来总结一下它的改动点:1.每个上行视频都有自己的解码器,这一点没有变。2.可同时有多个混流画布和多个单流画布,这是我们对画布进行的一个归类。3.每个上行视频在指定的混流画布和单流画布,即上行视频可以放在混流画布里也可以放在单流画布里面。4.每个画布有自己的编码器。5.SFU终端可以同时看多个画布,他可以自由去选择他想看的画布。6.不同的终端可以看不同的画布,这个是之前的规则。
4.5 MCU&SFU并存的难题
在我们推出了MCU&SFU并存方案之后,也遇到了一些难题。当我们把MCU和SFU并存之后,等于把它们的缺点也都吸纳了。首先就是在架构实现上,MCU的服务端复杂,SFU的终端复杂。在功耗方面,MCU的服务端功耗高,SFU的终端功耗高。在流量方面,支持了SFU后,终端流量也高了。在实时性方面,由于MCU需要转码,那么延时也高了。这些对于当时的我们来说是一个比较难的问题。
05 应用层动态码率
5.1 编码简介
对此,我们在应用层方面想了很多优化方案。这里分享有关动态码率相关的内容。首先来介绍一些基本概念,第一个是编解码相关的。在编码方面中,主要有编码算法,常见的有H.264、H.265、VP8、VP9,还有比较高效的AV1等等。还有分辨率,像我们买手机、显示器,很关注这个指标,有超清的4K、2K,高清的1080P、720P,低清的640P、360P等。另外还有帧率,就是每秒钟渲染图画的个数。对于一些变化比较慢的场景,比如共享文档,可能它需要的帧率就比较低,大概十帧左右就可以了。但面对一些游戏场景,它的操作是很频繁的,想要做到画面丝滑的效果就需要很高的帧率,比如需要90帧、120帧等。还有两个比较关键的概念,首先码率就是每秒传输的数据量,它是一个客观且可以度量的数据,它与分辨率、帧率和压缩率相关,是一个乘积关系。还有一个就是清晰度的概念,在实时音视频中,像高清视频主要讲的就是清晰度,但很多人经常会把清晰度和分辨率两者混淆,它们二者虽然有关系,但并不是完全等同。分辨率我们很容易理解,它与显示器的参数相关,与图片相关,它是客观存在且可度量的数据。清晰度除了与分辨率相关,也和显示器的PPI相关,PPI就是显示器的每英寸所能容纳像素点的个数,举个例子,假设有一张1080P的图片,放在1080P的显示器上,这样显示的可能很高清也很完美,但是如果这张图显示在4K的显示器上,这时就需要四个点来显示一个像素,就可能需要去放大,这样就会早成一定的损失,清晰度就会降低。同理如果这张1080P的图片显示在低分辨率的显示器上,如720P,就需要去压缩,这样又会损失一些数据,清晰度也会下降。再者是传输过程中的压缩率,比如一张原图它是高清的,但对它压缩的越多,损失的数据信息也就越多,这样清晰度也会降低。同时还有一个跟生物相关的特性就是人眼分辨率,每个人的视觉分辨率是不同的。所以综合来看,清晰度是一个感性的且很难度量的概念。
5.2 画布多编码器复用方案
接下来介绍下我们在画布多编码器复用方面的方案。上面提到我们之前在做FreeSWITCH的MCU方案中,每位用户观看视频都会生成一个编码器,这样做的好处是它可以灵活适配不同需求的终端用户。比如第一个用户用的是H.265的设备,那我们就用一个H.265的编码器去进行编码然后发送给他。第二个用户用的是4K的设备,那么就用一个4K的编码来编码发送给他。以此类推,它可以适配不同终端用户的需求。但这个方案的缺点在于随着终端用户越来越多,导致编码器也越来越多,个数不可控,对服务端的压力很大,性能也不可控。后面我们讲到SFU并存方案里面,我们把一个画布只匹配一个编码器,它的优点是一个编码器的可控性强,性能也是最好的。但它的缺点也很明显,就是它不能满足不同需求的终端用户。比如你的编码器就只能支持H.264,那么H.265的终端用户就接入不了,看不到任何东西。那么在这两个方案的基础上,我们取长补短折中了一个方案就是一个画布用多个编码器,编码器是可以复用的。比如一个H.264 1080P编码器,可能大部分的设备都能用,其他设备接入进来直接用就可以了,就无需再重新分配一个编码器。如果有一个特殊的设备接入,我们就按需分配,生成一个特殊的编码器提供给用户,这样我们就能够达到在终端方面的灵活适配,在服务端上的性能可控。
5.3 按场景配置多编码器
那我们有了这些编码器,如何去使用这些多编码器?首先,我们按场景划分。我们在会议中经常会用到共享功能,这时有些人会在办公室使用,还有些人是在自己的办公桌环境使用,针对这些不同的场景,我们对画布也进行了一些划分,分为共享画布、会议室画布和普通参会人画布。其中共享画布的特点是大部分时间是在分享文档,画面变化少,看的人多,它的要求是要高清和稳定。对于这个要求,我们对编码器设定了一些规则。首先设定了高清编码器,1080P,低帧率,固定GOP来保证它的稳定性。针对小屏用户,比如它把视频放置在小框播放,针对这种场景我们设定了小框编码器,给出180P分辨率来适应用户的屏幕。另外设定了一个过渡编码器,主要针对需要切换观看共享画布的用户,当他需要观看时会有关键帧请求,我们需要有动态GOP来及时响应。针对会议室画布,它的特点是会议室人多,经常会有老板在场,经常说话,观看人数相对多,这时它的要求是清晰度在标清即可,重要的是保证它的流畅性。在规则方面基本上和共享画布相同,唯一不同的是在高清编码器方面我们会将帧率提高来保证它的流畅性。普通参会人画布的特点是很少有人会开启摄像头,不常说话,以听众角色为主,极少被人观看。它的要求就会比较低,只要画面较清晰、较流畅即可。经过对这些场景的划分和对规则的编排,我们在该清晰时做到清晰,该流畅时做到流畅,该实时的时候做到实时,保证整个系统的稳定性和省流。
5.4 多编码器切换
再来讲解一下多编码器的切换情况。我们对刚进入观看画布的用户提供过渡编码器,使他能够及时响应关键帧请求,可以及时看到画布。当用户网络稳定后就接入到高清编码器,当网络不好的时候再切回到过渡编码器。当用户在小框状态时就为他启用小框编码器,当网络抖动时再为他切换到过渡编码器。高清编码器和小框编码器之间根据全屏和小框状态来进行对应的切换。经过这些切换,我们能够让90%的用户使用高清编码器,9%的用户使用小框编码器,只有1%的用户使用过渡编码器。
5.5 颜色分布直方图
在颜色分布直方图方面,我们进行了一个对比。如上方左侧图所示,是一张文档图像,这里面有大量的白色,以及少量的紫色和蓝色,通过进行颜色分析之后得出颜色分布直方图,可以看出这张文档图像的特点是颜色种类少,分布集中,在统计学上来说它的方差比较小。再来看上方右侧这张视频图像,它的色彩很丰富,元素也很丰富,有花、草、天空和湖泊等。我们对这张图进行颜色统计分析得到一张这样的颜色分布直方图,显示颜色种类很多,分布是分散的,从统计学来讲方差比较大,这个数据还是比较好区分的。我们利用颜色分布直方图的目的其实是为了在共享时来区分播放的文档还是视频。我们虽然大部分时间是在共享文档,但也会有共享播放视频的情况,所以我们需要把这两种场景区分开,就可以使用颜色分布直方图这种算法来简单快捷的区分这两种场景。在播放文档的时候可以选择比较低的帧率,达到低码率。当播放视频时,我们就需要提升帧率,让用户观看起来更流畅。
06 网络拥塞控制
6.1 集成WebRTC拥塞控制
在网络拥塞控制方面我们也做了一些工作,我们把WebRTC上与拥塞控制相关的库抽离出来,并集成到FreeSWITCH上。整个媒体流转的情况如上图所示,服务端把媒体发送到终端,终端经过统计再把这些信息数据返回给服务端,然后服务端经过延时、丢包这些数据及复杂算法的统计,经过码率估算这个模块,估算出这条链路的带宽上限是多少,再经过带宽分配模块,就是把总码率分配到不同媒体流的链路上,相当于一条马路分成多个车道这种形式。带宽分配后应用到编码器上,编码后再发送到平滑发送模块,平滑发送能够有节奏地向外发送数据,保证网络稳定。通过这样的实时正向反馈,可以做到链路上的稳定,对用户来说可以获得较好的观看体验。
6.2 根据码率分配切换编码器
前面提到了我们有多编码器也有码率分配,那么我们如何把多编码器和码率分配结合起来呢?对此,我们将编码器按码率进行了分配。我们将4mps以上分为高清编码器,500kps-4mps分为标清编码器,500kps以下分为低清编码器。通过带宽分配后我们得到了一个码率值,那么码率在哪个区间就用对应的编码器,然后用这个编码器来处理数据并发送给终端。
6.3 端到端帧延时统计
另外,我们还做了端到端帧延时统计。在服务器转码的场景下,端到端的统计还是比较难的,因为转码过程中它把帧的数据打乱了,也打乱了传输这条线。因为我们有这方面的需求,所以做了一个方案。这个方案解决的主要矛盾点是把端到端的时间戳对齐到FreeSWITCH上,然后终端在登录的时候同步FreeSWITCH时间戳,通过将扩充的RTCP这些报文把自己当前的时间戳发送给FreeSWITCH,然后FS再返回的时候将收到的时间戳以及自己当前的时间戳一起返回给终端,那么终端拿到这些数据时就可以计算出rtt值,以及跟FS时间戳的差值,也能拿到FS的截止时间戳。到后面发送数据时,用到的就是FS的时间戳而不是自己的时间戳。当发送端将时间戳带到FS后,它需要去转码,但是转码时不会把这个时间戳丢掉,而是会把时间戳加到要发送的帧上面,然后带给接收端。当接收端接收到信息后,它根据自己接收到的时间就可以计算出差值时间,也就是延时时间。通过这个方案我们统计到系统延时基本上在200ms,这个延时是包括从发送端到接收端再到转码,一直到合流、混流等整个过程。
6.4 FEC探索
除此之外,我们还对FEC进行了探索。这里我们主要是把WebRTC上的模块集成进来。我们开始实现的是Ulp FEC,主要是把冗余包和媒体包都放在一个SSR流里面,这样一来它有很多不友好的东西。比如它对NACK的支持就不是很好。同时WebRTC所支撑的算法对非VPX编码也不是很友好。后来我们又实现了Flex FEC,将媒体包和冗余包放在两个不同的媒体流里面,这样就可以很容易支持NACK。然后我们看FEC和NACK哪个恢复地快就用哪个数据。当前我们用的是这套FEC,但是我们对它的期望和效果还是有一定的差距,我们未来还会对此进行一些探索。
07 一点思考
这里是我的一点思考。因为我们从事AI相关的工作,了解AI和RTC之间有一种天然的场景结合,比如我们实现了AI降噪使人声凸显,人在说话时把周围的噪音,如敲桌子的声音、关门的声音等类似这种噪音给屏蔽掉,从而凸显说话人的声音。另外我们还做了空间音频,让用户能够有一种身临其境的感觉。在端侧方面,我们在手机上面应用了AI超分,比如在会议场景中,因为它的帧率比较低,它的画面元素相对比较简单,做超分相对来说比较容易。在拥塞控制方面,我们也正在探索。我们知道WebRTC主要基于的是规则性的策略,能够适配大部分的场景,但对于一些特殊的场景它处理的就不是特别的好。因此我们考虑应用AI的一些思想,把一些特殊场景补足。此外还有AI编解码等方面也在我们的探索中。
以上就是我本次分享的全部内容,谢谢大家!
▼识别二维码或猛戳下图订阅课程▼