网络传输

单对单模式主要是怎么通过路由路径优化手段达到两点之间最优,这方面 SKYPE 首先提出基于 P2P 的 Real-time Network 模型。而 单对多模式是一个分发树模型,各个客户端节点需要就近接入离自己最近的服务器,然后在服务器与服务器构建一个实时通信网络。

基础

推流

所谓推流,就是将我们已经编码好的音视频数据发往视频流服务器中。实时音视频系统都是一个客户端到其他一个或者多个客户端的通信行为,这就意味着需要将客户端编码后的音视频数据传输到其他实时音视频系统都是一个客户端到其他一个或者多个客户端的通信行为,这就意味着需要将客户端编码后的音视频数据传输到其他客户端上,一般做法是先将数据实时上传到服务器上,服务器再进行转发到其他客户端,客户端这个上传音视频数据行为称为推流。

我们可以通过 Nginx 的 RTMP 扩展方便地搭建推流服务器:

rtmp {
server {
listen 1935; #监听的端口
chunk_size 4000;
application hls { #rtmp推流请求路径
live on;
hls on;
hls_path /usr/local/var/www/hls;
hls_fragment 5s;
}
}
}

推流会受到客户端网络的影响,例如:wifi 信号衰减、4G 弱网、拥挤的宽带网络等。为了应对这个问题,实时音视频系统会设计一个基于拥塞控制和 QOS 策略的推流模块。

WebRTC

WebRTC 是一个开源项目,旨在使得浏览器能为实时通信(RTC)提供简单的 JavaScript 接口。说的简单明了一点就是让浏览器提供 JS 的即时通信接口。这个接口所创立的信道并不是像 WebSocket 一样,打通一个浏览器与 WebSocket 服务器之间的通信,而是通过一系列的信令,建立一个浏览器与浏览器之间(peer-to-peer)的信道,这个信道可以发送任何数据,而不需要经过服务器。并且 WebRTC 通过实现 MediaStream,通过浏览器调用设备的摄像头、话筒,使得浏览器之间可以传递音频和视频。WebRTC 有三个重要的部分:MediaStream、RTCPeerConnection、RTCDataChannel:

  • MediaStream:通过设备的摄像头及话筒获得视频、音频的同步流

  • PeerConnection: 用于构建点对点之间稳定、高效的流传输的组件

  • DataChannel:能够使得浏览器之间(点对点)简历一个高吞吐量、低延时的信道,用于传输任何数据

实时网络传输优化

TCP 与 UDP

在大规模实时多媒体传输网络中,TCP 和 RTMP 都不占优势。TCP 是个拥塞公平传输的协议,它的拥塞控制都是为了保证网络的公平性而不是快速到达,我们知道,TCP 层只有顺序到对应的报文才会提示应用层读数据,如果中间有报文乱序或者丢包都会在 TCP 做等待,所以 TCP 的发送窗口缓冲和重发机制在网络不稳定的情况下会造成延迟不可控,而且传输链路层级越多延迟会越大。

在实时传输中使用 UDP 更加合理,UDP 避免了 TCP 繁重的三次握手、四次挥手和各种繁杂的传输特性,只需要在 UDP 上做一层简单的链路 QoS 监测和报文重发机制,实时性会比 TCP 好,这一点从 RTP 和 DDCP 协议可以证明这一点,我们正式参考了这两个协议来设计自己的通信协议。

UDP 不可避免地存在抖动、乱序、丢包问题,视频必须按照严格是时间戳来播放,否则的就会出现视频动作加快或者放慢的现象,如果我们按照接收到视频数据就立即播放,那么这种加快和放慢的现象会非常频繁和明显。也就是说网络抖动会严重影响视频播放的质量,一般为了解决这个问题会设计一个视频播放缓冲区,通过缓冲接收到的视频帧,再按视频帧内部的时间戳来播放既可。

UDP 除了小范围的抖动以外,还是出现大范围的乱序现象,就是后发的报文先于先发的报文到达接收方。乱序会造成视频帧顺序错乱,一般解决的这个问题会在视频播放缓冲区里做一个先后排序功能让先发送的报文先进行播放。

UDP 在传输过程还会出现丢包,丢失的原因有多种,例如:网络出口不足、中间网络路由拥堵、socket 收发缓冲区太小、硬件问题、传输损耗问题等等。在基于 UDP 视频传输过程中,丢包是非常频繁发生的事情,丢包会造成视频解码器丢帧,从而引起视频播放卡顿。这也是大部分视频直播用 TCP 和 RTMP 的原因,因为 TCP 底层有自己的重传机制,可以保证在网络正常的情况下视频在传输过程不丢。基于 UDP 丢包补偿方式一般有以下几种:

  • 报文冗余,报文冗余很好理解,就是一个报文在发送的时候发送 2 次或者多次。这个做的好处是简单而且延迟小,坏处就是需要额外 N 倍(N 取决于发送的次数)的带宽。

  • FEC, Forward Error Correction,即向前纠错算法,常用的算法有纠删码技术(EC),在分布式存储系统中比较常见。最简单的就是 A B 两个报文进行 XOR(与或操作)得到 C,同时把这三个报文发往接收端,如果接收端只收到 AC,通过 A 和 C 的 XOR 操作就可以得到 B 操作。

  • 丢包重传,丢包重传有两种方式,一种是 push 方式,一种是 pull 方式。Push 方式是发送方没有收到接收方的收包确认进行周期性重传,TCP 用的是 push 方式。pull 方式是接收方发现报文丢失后发送一个重传请求给发送方,让发送方重传丢失的报文。丢包重传是按需重传,比较适合视频传输的应用场景,不会增加太对额外的带宽,但一旦丢包会引来至少一个 RTT 的延迟。

拥塞控制

要评估一个网络通信质量的好坏和延迟一个重要的因素就是 Round-Trip Time(网络往返延迟),也就是 RTT。评估两端之间的 RTT 方法很简单,大致如下:

  • 发送端方一个带本地时间戳 T1 的 ping 报文到接收端;

  • 接收端收到 ping 报文,以 ping 中的时间戳 T1 构建一个携带 T1 的 pong 报文发往发送端;

  • 发送端接收到接收端发了的 pong 时,获取本地的时间戳 T2,用 T2 – T1 就是本次评测的 RTT。

因为客户端有可能在弱网环境下进行推流,音视频数据如果某一时刻发多了,就会引起网络拥塞或者延迟,如果发少了,可能视频的清晰不好。在实时音视频传输过程会设计一个自动适应本地网络变化的拥塞控制算法,像 QUIC 中的 BBR、webRTC 中 GCC 和通用的 RUDP。思路是通过 UDP 协议反馈的丢包和网络延迟(RTT)来计算当前网络的变化和最大瞬时吞吐量,根据这几个值调整上层的视频编码器的码率、视频分辨率等,从而达到适应当前网络状态的目的。

QoS 策略

客户端推流除了需要考虑网络上传能力以外,还需要考虑客户端的计算能力。如果在 5 年前的安卓机上去编码一个分辨率为 640P 的高清视频流,那这个过程必然会产生延迟甚至无法工作。为此需要针对各个终端的计算能力设计一个 QoS 策略,不同计算能力的终端采用不同的视频编码器、分辨率、音频处理算法等,这个 QoS 策略会配合拥塞控制做一个状态不可逆的查找过程,直到找到最合适的 QoS 策略位置

媒体处理技术

回声消除

在实时音视频系统中,回声消除是一个难点,尽管 webRTC 提供了开源的回声消除模块,但在移动端和一些特殊的场景表现不佳。专业的实时音视频系统会进行回声消除的优化。回声消除的原理描述很简单,就是将扬声器播放的声音波形和麦克风录制的波形进行抵消,达到消除回声的作用。因为回声的回录时间不确定,所以很难确定什么时间点进行对应声音数据的抵消。在专业的回声消除模块里面通常会设计一个逼近函数,通过不断对输出和输入声音波形进行在线学习逼近,确定回声消除的时间差值点。