即构自研WebRTC网关服务器架构实践

演讲 / 黄开宁
整理 / 小极狗


作为实时音视频领域最火的开源技术,WebRTC 点对点的架构模式,无法支持大规模并发。如何在架构中引入服务端,一直是开发者关注的热点。5月20日,在WebRTCon大会上,即构科技资深音视频架构师黄开宁带来了他的实践分享。


我们对他的演讲内容做了整理,整理后的文章分为上下两篇,本文为第二篇,欢迎阅读。




前面(第一篇:WebRTC网关服务器搭建:开源技术VS自行研发)我给大家分享了:

第一,为什么需要WebRTC网关。对于即构来说,目前所使用的系统已经较为成熟,也经过了市场的检验,为什么还要在如此成熟的商用系统中加入WebRTC?

第二,WebRTC通信模型对比。分析了目前的WebRTC通信模型以及不同模型的适用场景,以便大家在选型时能结合自己的需求匹配对应的场景。

第三,开源VS自研。分享了目前市面上的一些开源系统及其特点以及即构自研WebRTC网关的目的和出发点。

下面,我将结合即构自研的WebRTC网关服务器方案,分享在开发过程中遇到的难点和解决办法。



Zego-Gateway架构的改进


在加入WebRTC网关之前,即构自研系统架构如下图所示,主要分成两部分,左边是低延时用户,而右边是围观用户。低延时用户主要是通过ZEGO的实时传输网络进行推拉流。



由于RTMP的实时性并不是很好,在浏览器端没有办法通过RTMP进行上行传输达到低延时的特点,所以即构对原有的系统架构进行了升级,在低延时的实时传输网络中加入了WebRTC网关服务器,具体如下图所示。



在加入了WebRTC网关服务器后(图中红线部分所示),即构的系统已经能全面支持网页端视频互动场景,同时实现了APP、微信小程序、WebRTC三端的连麦互通。



Zego-Gateway架构的实践


在开发WebRTC网关的过程中,我们遇到了不少的难题。下面我将结合即构的方案给大家一一分享。



首先我们需要了解网关在架构中的地位。如上图,左边是浏览器端的接入方式,右上角是后台房间、信令等服务,中间就是Gateway。通过网关可以连接即构现有的实时音视频架构,而即构的架构是基于UDP私有协议的,同时搭配CDN分发网络,或者是自主实现的RDN服务器。同时即构的架构体系中还包含了部署在全球范围的实时音视频架构。通过网关与原有的媒体服务器进行连接,可以轻易实现跨国实时音视频通信。目前,即构的架构体系在路由器选择和负载平衡方面都非常完善,只需要在中间的Gateway中进行对接就能获得原本架构中的实时音视频能力。



上图是Gateway的系统架构,与WebRTC的架构图十分类似。这个图包含了Gateway的一些模块,对比上一篇文章中介绍的开源项目,可以发现大多数开源项目只包含最右边的四个模块。如果只是需要实现WebRTC网关,只需要右边的四个模块,就可以搭建一个最简单的服务器架构。但是若想实现功能较为完善的WebRTC网关,MCU的模型、路由带宽预测、JitterBuffer、Codec等都是需要实现的模块。下面主要根据不同的模块,分享即构在自研过程中实现的技术难点和模块间的结合应用。



图中所示的是非常典型的RTMP的转协议流程,左边是浏览器,中间是RTP、RTCP的模块,右边是RTMP的模块。由于RTMP的TCP是有序的,所以在网关的内部,需要引用JitterBuffer,进行丢包和抖动的处理。另外,由于RTP、RTCP和RTMP的时间戳处于不同的体系,不是基于同一个标准的,所以需要实现时间戳的同步。RTP是基于NTP,而且音频和视频会根据各自采样时钟来打时间戳的,而RTMP却是一个绝对时钟,需要通过RTCP的SendReport反馈对音频和视频时间戳进行同步校对。实现同步后,还需要实现转码,一般来说是将OPUS转成AAC。在这个模块中,包含了JitterBuffer、时间戳同步和转码等技术,虽然有些技术看上去很简单,但是实际上要做到很好的实时性,也是比较困难的。



在评价一个系统时,时间是非常重要的考量指标。大家会发现使用WebRTC进行接入测试的时候速度非常慢,这是由于WebRTC用到的ICE流程非常复杂,导致耗时过长。在STUN中,首先需要做IP的映射识别出内网的IP,然后根据需要进行协助打洞。为了减少接入时间,我们首先要去掉STUN。

为什么在有服务器的情况下还需要打洞?从图上可以看到在ICE流程中,首先是左边的浏览器发起了Offer,接下来是信令阶段,主要是SDP Candidate的协商交换,随后会进入连通性检测。在ICE标准中,需要Answer方是要先发起Binding Request请求,假如在Candidate的协商交换时没有经过STUN做IP的映射,那么浏览器端的IP是内网地址。而Gateway在云端,对应的IP是公网的地址。很明显,这种地址的不对称会导致连通失败。这也是为什么需要打洞,或者是中间服务器的原因。

这个时候可以利用TURN的方案,在上一篇文章所介绍的开源系统中,可能有相当一部分的TURN方案都是独立部署的。独立部署的优点是扩展性比较好,缺点是可能会造成资源浪费。针对此即构的方案是在TURN的基础上做了一个整合,直接在Gateway中实现TURN。这样,既节省了资源,在部署上也有保持一定的优势。

通过整合去掉了STUN服务器后,只需要几步简单的连通性检测,不需要再经过中间服务器的转发,所以即构的方案在连通时间上可以达到毫秒级。



有关连通性的另一个方面是端口复用。在使用WebRTC的过程中,会接收到弹框提示相关的权限申请,但是作为播放端可能并没有音视频的采集设备,在这种情况下,弹框的出现会影响用户的体验感。在每次调用getUserMedia的时候都会有弹框出现,如果不调用getUserMedia,则createOffer会失败,所以即构的方案将Offer换到Gateway方发起,这个时候浏览器作为Answer方就不会出现弹框了。除了弹框的问题外,由于网关的端口是随机分配的,所以可能会出现防火墙对某些端口受限,这种情况下可能会无法建立连接。因此通常情况下需要将对端的随机端口换成公共端口,例如80或者8080,这样有利于提高连通性。



在分布式的系统中,由于节点众多,所以可能会出现Nack风暴。

Nack风暴是如何引发的?假设现在Publish方需要将序列号为7、8、9的三个包传输到Gateway1,网关只是做简单的转发。在传输过程中,发生了8号序列包丢失的状况,这个时候网关会实时将7、9号序列包转发给所有用户,同时判定8号序列包丢失并给Publish方发送丢包重传的Nack请求。而下方的用户在收到7、9号序列包时,同样会发现8号序列包丢失并发送Nack请求。假设接入用户有10万,在序列包丢失时会瞬间产生10万个重传请求,这个就是Nack风暴。

对于Nack风暴,即构有几个设想方案。

第一在服务器间增加扩展协议,因为在RTP中没有相关的协议,如果与Gateway1连接的服务器是自建的,可以在RTP中扩展头处理。第二可以考虑上行端不用Nack机制而用FEC。但是由于FEC本身冗余较大且复杂度高,所以不太建议采用这种方式。第三就是WebRTC只用在上行端,下行端采用RTMP或者其他替代方案,这个方案需要基于对下行的实时性要求不高的情况下才能使用。而在线娃娃机就是典型的不需要上行的例子,但是对于摇杆操作的实时性还是非常高的,所以还是需要使用RTC的方案。这种情况下,即构的方案是将Gateway下一个节点转换成基于私有协议的服务器。由于Gateway跟UDP服务器是通过私有协议进行传输的,所以Gateway会在包里增加一些相关信息,UDP服务器会根据信息判断包的完整性。当Gateway收到了重传的包后,自然会传给UDP服务器,当然UDP服务器也有一个超时机制,通过评估判定超时后也会发起重传请求。最后是隔离和负载,前面通过接入UDP服务器起到隔离的作用,接下来通过接入Gateway集群实现负载平衡。



第一种是联播方式。假设上行是4M的码流,下行端用户的网络状况不一样,对高于4M带宽的用户来说没有影响,但是对带宽低于4M的用户来说,由于带宽不足会导致无法观看。在WebRTC的RTP体系中包含专有的联播方案,会在评估了用户节点的能力之后,根据节点选择对应码率的视频流。假设两个用户节点中,最高分别支持2M和0.6M的宽带能力,这种情况下,联播方案会在上行中分为两路编码,一个是2M或者是2.5M的编码,另一个是0.6或者是更小的编码。这两路视频编码会同时上传,根据用户的带宽能力选择对应的码流进行传输。但是,这种方案对于带宽能力良好的用户来说并不公平,因为在足够的带宽条件下却无法获取更好的画质。这种方案在WebRTC及Gateway中已经实现,尽管这种方案有一定的缺陷,但是根据不同的场景,也是一个值得考虑的方案。



另外一种方案是分层编码。大家应该知道在视频中,帧和帧之间是有一定的关联性,根据帧率的不同,关联的帧数目也不太一样。假设目前有三种帧率,分别是30fps、15fps和7.5fps,对应用蓝色、红色和绿色进行标记。在15fps的帧率下,在两个标记帧中间也就是没有被红色标记的帧是不需要的,换句话说,在网络情况不好的情况下,只转发红色标记的帧就能进行解码,同时流畅性也很好。因为降低了帧率,对应的码率就也可以随之降低。在不同的带宽情况下,同时保证流畅性的前提下,可以选择不同帧率的码流进行转发,充分利用带宽的能力获取对应是高清视频体验。而绿色标记的部分是我们在评估了所有下行方的能力情况后,选择了QCIF作为转发视频的分辨率。QCIF具体是一个177*144像素的画面,在视频会议中会经常用到的一个标准视频采集分辨率。尽管画面比较差,但是这个分辨率下的视频非常流畅。

为了支持直播中大量的分发性需求,即构的方案采用的是H.264编解码,而不是WebRTC中的VP8、VP9。因为如果使用VP8或者VP9的编解码格式,就必须在服务器中进行转码,这样会造成延时。另外,H.264是支持SVC,类似分层编码的能力,基于这些,即构选择了H.264编码格式。而在RTP协议里面,包括头和扩展协议,暂时没有帧之间关联性的描述,或者可以说WebRTC没有实现。由于分层编码是基于帧的关联性,所以说如果没有层关系的描述,就没有办法区分帧之间的关系。

而VP9是支持分层编码的,可以在网页端或者是WebRTC场景下进行编解码,因为编码信息中带有PictureID信息,可以直接分析出帧之间的关联性。但是VP9的编码开销非常大,如果需要支持分层编码的话开销会更大,特别在移动端中性能还是比较差。分层编码不仅是编码端和解码端的结合,更重要的是要和服务器结合,在服务器中根据不同用户的带宽情况选择不同的码流进行转发,这样可以显著降低带宽的需求。

以上为我的分享内容,谢谢大家!

编辑于 2018-07-24