文 | 红点联合创始人 王宇航 原文地址:http://t.cn/R4LVZ2B
我今天分享的主题,是以实时连接场景为目标的一些技术架构探索。主要是关于红点在产品研发过程中,我们的技术选择,架构变化,还有这个过程中,我们的一些考虑。
有很多科幻的作品,描述人类突破自然界对时间、空间的客观限制,去做一些事情。互联网在很多方面已经对我们的这种能力有了一个很强的补充,比如我们每个人都可以用微信,可以实时的与不同地域的人们对话。但是我们觉得这个能力还可以进一步提高,尤其在多人的场景。
我们希望在互联网上能够进一步提高信息的传输效率,进一步降低信息的传输延迟,去让一些线上的多人场景,有更好的体验。这是我们技术迭代的初衷,所以在后面的内容里大家可以看到,我们很多的技术选择跟这一点有很大关系。
红点的第一个版本功能比较简单。我们当时要做一款手机端可以录音,网页端可以播放的直播产品。手机端只支持 iOS 就可以了,但是要能够全平台播放。对这个版本迭代的要求是能够快速上线,提供服务。
这个功能需求中,首要的问题是调研各个平台的 web 页面支持哪些音频直播格式。大家在图上可以看到调研结果,但是这个不是我们当时就使用的方案,这是我们花了一段时间通过线上实际运行的情况,总结的一个方案。
这里我们只列了两个格式,HLS,这是苹果主力推广的手机网页直播格式 Http Live Streaming,这个格式实际上是一个多媒体的播放列表,这个列表要求最少有三个文件,每个文件是一个独立的多媒体文件,通过在播放端循环更新文件列表,依次将最新的多媒体文件插入本地播放列表,顺序播放,来实现直播的效果,这个格式做直播的延迟在 8 秒以上会比较稳定,也就是每个文件的时长应该在 2 秒以上;另一个格式是 Http mp3 流,这个流是一个直播的流,不是我们平时经常见到的 mp3 点播的格式。
Pc 平台因为有 flash 对富媒体的支持,我们产品要求的多媒体形式都有成熟解决方案,比如基于 tcp 的 rtmp 协议,这是 adobe 公司推出的流媒体协议,等等。iOS 平台上,苹果大力推广 HLS 作为他的标准格式,所以在 iOS 平台上也没有太多问题。
但是在安卓平台上情况并不理想。可能开发过需要运行在安卓平台上的 native 应用或者 H5 页面的朋友可能会了解,由于安卓厂商种类繁多,各自又存在脱离安卓标准修改 rom 的情况,安卓平台对多媒体的兼容性有很大问题。我们对 HLS 调研的情况最夸张的时候,复杂度是浏览器种类 设备种类 安卓系统版本 *安卓 Rom 种类。我们自己做的小样本调研结果是,大约一半的安卓浏览器,包括应用内的 webview,无法正确播放 HLS 格式的直播。但是对于 Http mp3 流这种音频直播格式,效果就好很多,支持的比例在 90% 以上。
我们最初的全平台使用的是 HLS 方案,然后逐步过渡到,PC 网页和 iOS 使用 HLS 方案,安卓使用 http mp3 流的方案。
这是我们第一版产品的架构。客户端使用 tcp 协议上传 mp3 流,这里对照可选的还有 http 和 udp 两种,不过这两种的研发成本都较 tcp 高一些。我们这个版本的后台服务是一个单机的服务,支持接收 mp3 流的上传和 http mp3 流分发,同时能够本地落盘生成 HLS 切片和文件列表。HLS 通过 nginx 反向代理,对外提供分发服务。
接着我们对这个版本做了一次功能迭代,这次功能迭代我们主要增加了音频直播的历史回听。所以我们增加了新的后台服务,用于将直播结束后生成的历史多媒体文件上传到 UPYUN 上面。这个功能的主体部分我们是依靠 UPYUN 来完成的,这节省了我们大量的成本、时间和精力。大家都知道创业团队很多时候就是在跟时间和成本赛跑,所以能够直接使用成熟的第三方服务,是非常有帮助的。
然后我们产品功能做了一次大的更新。我们需要实现多人语音功能,支持 iOS 和安卓两个平台的录音和播放。这里的多人语音是一个语音会议的能力,比如像 yy 语音,qtalk 这样的,能够多人实时会话的产品功能。
这个功能引入了这几个技术点,大家可以看到。首先是混音,混音就是将多路声音混为一路声音。这是我们播放能力带来的衍生需求。在 flash 里面,播放多路声音是没问题的,就是同时下载多路流,然后播放就行了,但是在手机 H5 页面里,没有这个能力。手机 H5 页面只允许同时播放一路声音,这就要求我们必须在后台实现多路转一路这个功能,然后才能支持手机 H5 的播放。
然后是音频格式。Mp3 格式并不适用于低延迟场景,mp3 的编码延迟在 200ms 左右,这在音频编码中是特别高的。并且 mp3 这个格式本身是上下文相关的,也就是说一段声音的编解码依赖上一段或者下一段,这个特点也不适用于音频会议这种功能需求。所以我们需要选择一个新的音频格式。
下一个是网络协议,我第一版使用 tcp 的传输格式,但是基于 tcp 的协议有一个很严重的问题,就是无法改变拥塞控制策略。Tcp 在遇到有丢包的情况时,会有非常严重的惩罚,影响传输效率,这也是语音通话不能容忍的,需要使用基于 udp 的协议来传输音频数据。
还有一个我没有列在上面的,是 AEC,也就是回声消除。什么是回声消除呢,这个场景特别好理解。就是我们打电话的时候,如果我们打开免提,如果电话的回声消除做的不好,就会出现非常刺耳的尖叫声音,这个声音叫啸叫。这是由于设备本身的录音中包含了这个设备自己播放的声音,如果不能在录音中把自己播放的这部分去掉,就会出现循环,也就没法使用了。这个点我们在产品体验上规避了一下,我们要求安卓用户必须带耳机才可以使用多人语音功能,iOS 因为苹果提供系统支持,效果非常好,所以在 iOS 上没有这个问题。
右边是一些相关的项目和方案。
FFmpeg 是业界最流行的多媒体处理框架和多媒体处理工具集。它有一套成熟的多媒体处理架构,包括从采集到编解码,格式转换等一系列处理需求。它还整合了大部分音视频格式的封装与解析工具,音视频编解码器,公共的工具函数,还有视频后期的效果处理等功能服务。支持命令行使用,也支持作为函数库使用。
WebRTC 实现了基于网页的视频会议,标准是 WHATWG 协议,目的是通过浏览器提供简单的 javascript 就可以达到实时通讯能力。它的音视频处理部分源自于 google 收购的一家 ip 解决方案服务商 Global IP Solutions,这个引擎是这个公司的核心技术之一。它包括音视频的采集、编解码、网络传输、显示等功能,并且还支持跨平台:windows,linux,mac,android 都可以使用。
其中有两个模块对语音会话有显著作用,NetEQ 和 aecm。NetEQ 是自适应抖动控制算法以及语音包丢失隐藏算法。使其能够快速且高解析度地适应动态的网络环境,确保音质优美且缓冲延迟最小。NetEQ 对声音的优化一般是通过减慢部分音频的播放速率,或者加快部分音频的播放速率,以及使用音频编解码自身的特性恢复丢包,等几个策略综合调节实际的播放效果。
Aecm 是 aec for mobile 的意思,是专门为移动端优化的回声消除算法。这个模块本身的功能没问题,但是在安卓上,由于设备种类的问题,每个设备仍然需要自行适配这个模块的一些参数。其中一个最重要的参数是播放到采集的延迟,这个延迟是指从调用 API 播放原始声音数据,到这段声音数据被手机采集,通过手机的录制回调返回给程序,期间的间隔时长。这个时间参数对回声消除的效果影响是最大的。
Rtp、rtcp 是 rfc 标准的音视频传输协议。其中 rtp 是针对互联网上多媒体数据流的一个传输协议,rtcp 是负责管理传输质量在当前应用进程之间交换控制信息的协议。一般实际使用需要两种协议共同配合。Rtp 可以是基于 udp 的也可以是基于 tcp 的。Webrtc 的网络传输就是基于 rtp、rtcp 的。
Live555 是 c++ 实现的,支持 rtp、rtcp、rtsp、sip 的开源服务器。
我们自己重点对比了自研的方案和基于 webrtc 二次开发的方案。我们自己对自研工作的评估是这样的,我们需要实现的协议最小功能集合包括两个点,一是协议要支持应用层的会话管理,二是协议要支持传输数据的排序。支持这两个点的功能,就可以初步实现多人语音了,不过效果还有很大提升空间。这个方案的实现成本可以接受,但是在未来面对协议扩展等问题时,存在一定的风险。
Webrtc 是成熟框架,有 google 支持,并且是跨平台项目。但是 Webrtc 是客户端项目,没有配套的成熟服务器,只有用于 p2p 的配对开源项目。同时,webrtc 并非只实现了 rtp、rtcp 协议的基本格式,它里面增加了一些扩展字段,用于支持前向纠错和流控,也就是拥塞控制。这就需要我们自己对照他的协议,实现一个配套的服务,并且要给出分布式方案。所以虽然 webrtc 有完整的多媒体处理流程,但是整体的使用成本还是很高,所以我们最后选择了自研的方案。
这两幅图是几种音频格式特性的对比,两幅图来自 opus 的官网,左图纵轴表示压缩的效果,横轴表示支持的采样率,右图的纵轴是编解码的延迟,编解码延迟是指输入一定时长的音频数据到输出压缩后对应的音频帧的时间间隔,横轴是码率。Opus 压缩后的实际效果是否真的在数值上这么出色,我们没做详细的测评,但是我们对比了 ilbc 和 opus 对音乐压缩的效果,opus 在人声以外的场景仍然非常出色,并且编解码延迟也的确是如图中表示的这样,所以我们多人语音采用的音频格式是 opus 编码。
这里左图是多人语音上传分发功能的多媒体服务节点结构。每个节点由三个模块组成。Room 模块实现房间对多媒体数据的广播;同时会将本地用户上传的数据转交给 Master 模块;Master 会将本地的上传数据同步给其他节点的 Slave 模块;Slave 模块是与 Master 配对的接收数据同步的模块。这个节点结构是同构的,每个节点的程序本身都一样,在部署的时候只有配置不同。
这要求整个服务内部节点之间必须是全连通的组织方式,每两个节点之间都需要有一个数据同步的链路。这种架构的好处是研发、运维成本很低,可以很容易的实现一定程度的可靠性和可用性。对于宕机节点,可以直接将这个点从服务列表中摘除,受影响的用户会自动由于本地网络失败,选择其他节点的服务。这个架构的问题在于,无法跨机房部署。由于是全连通的组织形式,如果跨机房,会导致机房间存在大量可能影响服务质量的数据链路,难以做网络优化。
这是多人语音功能的架构。其中 codec 服务是 ffmpeg 的网络封装,方便横向扩展。Http mp3 流和 HLS 切片的输入是混音服务的输出。对 web 全平台通过 CDN 分发。
这个版本上线以后,接着我们对多媒体服务节点做了一次重构。从全连通的组织形式改成树形组织形式。每个服务节点由两个模块组成,Room 模块实现对房间用户的广播,Client 模块是 Room 配对的客户端,用于实现节点间的数据节点。每个节点有一个唯一的 ID,通过 ID 判断数据的同步是否会产生数据回路。当需要跨机房部署的时候,只需要多个机房能够共同通过同一个根节点进行数据同步即可完成整个功能。节点间我们增加 etcd 提供服务协调能力,etcd 是 golang 版本的类 zookeeper 服务。
多人语音之后我们增加了视频功能。视频功能的要求也是一个会议的场景,需要延迟尽可能低。我们为协议增加了重传能力,前向纠错能力和选择重传算法。增加了私有协议转 RTMP 流的服务,和一套音视频同步的机制。转成 RTMP 标准输出以后,通过 CDN 支持 web 的播放。下面我们详细的看一下每一个技术点和架构选择。
H264 格式的视频有三种帧,I 帧 P 帧和 B 帧,其中 I 帧可以独立解码显示,但是 P 帧和 B 帧都直接或者间接依赖最近的 I 帧。所以如果 I 帧存在数据丢失,会造成大片持久的马赛克,这个在视频体验上是不能容忍的,所以重传在这里是必要的。FEC,也就是前向纠错,是对重传的一种补充,通过增加数据传输过程中的冗余比例,实现丢包恢复。
我们采用的是多项式方程的方案,这个方案可以自由调节冗余的比例和尺度。Webrtc 中采用的冗余算法是异或的方案,异或的方案只能容忍一个数据包的丢失,无法处理连续丢失。选择重传算法要求数据重传要有时间限制和长度限制,这个算法本身是提高重传效率,提高网络利用率。
音视频同步这部分我们采用以音频为主时间线的方案。是因为人耳对声音更敏感,而对画面容忍度较高。
混流这部分我们实际采用的是客户端混流的方案。这里涉及到音视频复用,就是在服务器将音视频做好时间同步,再通过一个数据链路发送到客户端,或者在客户端做音视频复用,两个方案。由于服务器的复用处理会引入额外延迟,我们最终选择音频与视频,采用平行的系统提供上传与分发服务。这也带来了一个产品上额外的好处,就是当画面卡顿时,可以很容易的选择只播放声音,停止播放视频画面。
以上就是我们在迭代产品过程中的技术架构变迁。
本文整理自 红点直播联合创始人 王宇航 于 11 月 28 日在“UPYUN 架构与运维大会·北京站”上的主题演讲。
查看&下载本次大会全部讲师的演讲 SLIDES 及现场视频,请访问:http://opentalk.upyun.com/show/issue/29