自研直播客户端架构
我们先来了解一下自研直播客户端的架构,如图 2.1 所示。这是一个最简单的音视频直播客户端架构,通过这张架构图,你大体可以知道自研系统包括了哪些模块。
由图 2.1 可以知道,一个最简单的直播客户端至少应该包括音视频采集模块、音视频编码模块、网络传输模块、音视频解码模块和音视频渲染模块五大部分。

-
音视频采集模块。该模块调用系统的 API,从麦克风和摄像读取设备采集音视频数据。音频采集的是 PCM 数据,视频采集的是 YUV 数据。
-
音视频编码模块。该模块负责将音视频设备上采集的原始数据(PCM、YUV)进行压缩编码。
-
网络传输模块。该模块负责将编码后的数据生成 RTP 包,并通过网络传输给对端;同时,在对端接收 RTP 数据。
-
音视频解码模块。该模块对网络传输模块接收到的压缩数据进行解码,还原为原始数据(PCM、YUV)。
-
音视频渲染模块。该模块拿到解码后的数据后,将音频输出到扬声器,将视频渲染到显示器。
通过前面的介绍,相信读者一定觉得自研一个直播客户端好像也不是特别难的事情。但实际上,上面介绍的音视频直播客户端架构是极简化的,甚至都不能称之为直播客户端架构,这里只是给出了一个简化的客户端架构示意图,要将它变为真实的、可编码的架构还需要做不少细化的工作。
拆分音视频模块
接下来,我们就对上面的直播客户端架构图进行逐步细化。细化的第一步就是拆分音视频模块。因为在实际开发中,音频与视频的处理是完全独立的,它们有各自的处理方式。如音频有独立的采集设备(声卡)、独立的播放设备(扬声器)、访问音频设备的系统 API、多种音频编解码器(如 Opus
、AAC
、iLBC
)等;同样地,视频也有自己的采集设备(摄像头)、渲染设备(显示器)、各种视频编解码器(如 H264
、VP8
)等。细化后的直播客户端架构如图 2.2 所示。
从图 2.2 中可以看到,细化后的架构中,音频的采集模块与视频的采集模块是分开的,而音频编解码模块与视频的编解码模块也是分开的。也就是说,音频采用了一条处理流程,视频则采用了另外一条处理流程,它们之间并不相交。在音视频处理中,我们一般称每一路音频或每一路视频为一条轨。

-
PCM 是一种将模拟信号(如声音、图像)转换为数字信号的编码方式
-
YUV 将颜色信息分离为亮度(
Y
)和色度(U/V
),通过色度采样压缩减少数据量
除此之外,我们还可以知道,自研音视频直播客户端要实现的模块远不止 5 个,至少应该包括音频采集、视频采集、音频编码/音频解码、视频编码/视频解码、网络传输、音频播放以及视频渲染这 7 个模块。
跨平台
实现音视频直播客户端除了要实现上面介绍的 7 个模块外,还要考虑跨平台的问题,只有在各个平台上都能实现音视频的互联互通,才能称得上是一个合格的音视频直播客户端。所以它至少应该支持 Windows
、Mac
、Android
以及 iOS
四个终端,当然如果还能够支持 Linux
端和浏览器就更好了。
要知道的是,如果不借助 WebRTC
,想在浏览器上实现音视频实时互通,难度是非常大的,这是自研系统的一大缺陷。除此之外,其他几个终端的实现倒是相对较容易的事。
增加跨平台后,音视频直播客户端的架构较之前复杂多了,如图 2.3 所示。从这张图中可以看到,要实现跨平台,难度最大也是最首要的是访问硬件设备的模块,如音频采集模块、音频播放模块、视频采集模块以及视频播放模块等,它们在架构中的变化是最大的。

以音频采集为例,在不同的平台上,采集音频数据时使用的系统 API 是不一样的。PC 端使用的是 CoreAudio
;Mac
端使用的系统 API 也称为 CoreAudio
,不过具体的函数名是不同的;Android
端使用的是 AudioRecord
;iOS
端使用的是 AudioUnit
;Linux
端使用的是 PulseAudio
。
总之,每个终端都有各自采集音视频数据的 API。由于不同的系统其 API 设计的架构不同,所以在使用这些 API 时,调用的方式和使用的逻辑也千差万别。因此,在开发这部分模块时,其工作量是巨大的。
插件化管理
对于音视频直播客户端来说,我们不但希望它可以处理音频数据、视频数据,而且还希望它可以分享屏幕、播放多媒体文件、共享白板……此外,即使是处理音视频,我们也希望它可以支持多种编解码格式,如音频除了可以支持 Opus
、AAC
外,还可以支持 G.711/G.722
、iLBC
、Speex
等,视频除了可以支持 H264
外,还可以支持 H265
、VP8
、VP9
、AVI
等,这样它才能应用得更广泛。
实际上,这些音视频编解码器都有各自的优缺点,也有各自适用的范围。比如 G.711/G.722
主要用于电话系统,音视频直播客户端要想与电话系统对接,就要支持这种编解码格式;Opus
主要用于实时通话;AAC 主要用于音乐类的应用,如钢琴教学等。我们希望直播客户端能够支持尽可能多的编解码器,这样的直播客户端才足够强大。
如何才能做到这一点呢?最好的设计方案就是实现插件化管理。当需要支持某个功能时,直接编写一个插件放上去即可;当不需要的时候,可以随时将插件拿下来。这样的设计方案灵活、安全、可靠。
为了让直播客户端支持插件化管理,我们对之前的架构图又做了调整,如图 2.4 所示。从图中可以看到,为了支持插件化管理,我们将原来架构图中的音视频编解码模块换成音视频编解码插件管理模块,而各种音视频编解码器(Opus
、AAC
、iLBC
……)都可以作为一个插件注册到其中。当想使用某种类型的编码器时,可以通过参数进行设定,这样从音视频采集模块采集到的数据就会被送往对应的编码器进行编码;当接收到 RTP 格式的音视频数据时,又可以根据 RTP 头中的 PayloadType
来区分数据,将数据交由对应的解码器进行解码。经这样处理后,音视频直播客户端的功能就更强大了,应用范围也更广了。

这里以音频编解码器为例,简要介绍一下直播客户端增加插件管理前后的区别。客户端在增加插件管理之前,只能使用一种音频编解码器,如 Opus
。因此,在一场直播活动中,所有参与直播的终端都只能使用同一种音频的编解码器(Opus
)。这样看起来好像也不会产生什么问题。不过,假如此时我们想将一路电话语音接入这场直播中(电话语音使用的编解码器为 G.711/G.722
),那它就无能为力了。而有了插件管理模块情况就不同了,各终端可以根据接收到的音频数据类型调用不同的音频解码器进行解码,从而实现不同编解码器在同一场直播中互通的场景,这就是插件化管理给我们带来的好处。
其他
除了上面介绍的几点外,要实现一个功能强大、性能优越、应用广泛的音视频直播客户端还有很多工作要做。通常读者比较关心以下问题:
-
音视频不同步。音视频数据经网络传输后,由于网络抖动和延迟等问题,很可能造成音视频不同步。对此,可在音视频直播客户端增加音视频同步模块以保障音视频的同步。
-
回音。指的是与其他人进行实时互动时可以听到自己的回声。在实时音视频通信中,不光有回音问题,还有噪声、声音过小等问题,我们将它们统称为
3A
问题。这些问题都是非常棘手的。目前开源的项目中,只有WebRTC
和Speex
有开源的回音消除算法,而且WebRTC
的回音消除算法是非常先进的。 -
音视频的实时性。要进行实时通信,网络质量尤为关键。但网络的物理层是很难保障网络服务质量的,必须在软件层加以控制才行。虽然常用的
TCP
有一套完整的保障网络质量的方案,但它在实时性方面表现不佳。换句话说,TCP
是以牺牲实时性来保障网络服务质量的,而实时性又是音视频实时通信的命脉,这就导致TCP
不能作为音视频实时传输的最佳选择了。因此,为了保证实时性,一般情况下实时直播应该首选UDP
。但这样一来,我们就必须自己编写网络控制算法以保证网络质量。
此外,还有网络拥塞、丢包、延时、抖动、混音等问题。
通过上面的描述,读者应该清楚要自己研发一套音视频直播客户端到底有多难了。