本文是从Webrtc实际应用的方面介绍如何快速理解Webrtc的源码。Webrtc是用于web的实时通信框架,也可以直接使用Google开源的Webrtc实现来开发非Web的APP。要理解如何使用webrtc的源码,最好是先懂得Webrtc通信的流程,请查看 用Webrtc实现免费的1对1高清实时视音频会议系统 就可以了解Webrtc的接口使用流程,了解这个流程后就可以快速理解Google开源的那部分Webrtc源码了,并为自己使用。
需要掌握Webrtc的js部分的 PeerConnection,js部分的接口及方法如RTCPeerConnection,setLocalDescription、setRemoteDescription、createOffer、createAnswer 等,这些是理解源码的关键。
一、Webrtc源码概述
WebRTC 源码作为 Chromium 的一部分,更新速度非常快,这得益于 Google 对音视频通信的大力推广。WebRTC 源码的结构也变化很快,主要体现在以下几方面:
- 比较源码版本 m50 和 m66,在编译工具上发生了变化。老版本是通过 GYP 来生成 ninja 文件,新版本是通过 gn 生成 ninja 文件。
- 在目录结构上发生了变化,老版本中支持的一些功能在新版本中删除了。
- 随着 c++11,c++14,c++17 新标准的推出,WebRTC 在新版本中逐渐支持了新标准中的语法功能,使得代码变得更简洁、易维护。
目前手上有一份而 windows 版本的 m66 版本代码和一份 linux 版本的 m73 代码,这两个版本在目录结构上差异不大,在实现上有一些差异。比如,m73 版本为 BaseChannel 类抽象了一个 ChannelInterface 接口类。由于 WebRTC 代码下载不是很方便,所以本文后续会交替用 m66 和 m73 版本。
作为技术人员的我们,一是要理解 WebRTC 实现的原理,这样可以应对不断地变化。二是要跟踪最新的实现,将最新技术不断的引入到我们的产品中。
二、Webrtc源码的层级图
因为Webrtc源码比较庞大,以下列出了比较关键的模块:
如果按照通常层次化的思维来组织,从下到上,大概分以下几个层次:
- OS 跨平台适配、硬件设备访问、第三方库 Wrapper 层
包括网络层、操作系统 API 的跨平台封装,音频设备、视频设备封装,音频、视频 codec,DTLS 的第三方实现等。 - 网络传输层
这里包括 candidate 收集,stun/turn 协议的实现,dtls、rtp 网络连接的建立,sctp 连接的建立等。 - 通道层
主要包含传输通道也就是 BaseChannel 层 和 媒体通道 也就是 MediaChannel 层。BaseChannel 是和 PeerConnection、Transport 层对接。MediaChannel 实现其实在音视频引擎里面,是 BaseChannel 和引擎的桥梁。 - RTP_RTCP 主要是流控
- Audio Engine、Video Engine 是音视频引擎层,音视频处理。
- 音视频编解码器,这也是 WebRTC 自己的一个抽象,真正的编解码库还是依赖第三方库。
- PeerConnection、MediaStream 主要是对 jsep 协议的实现。
- PeerConnectionInterface 是一个对外抽象接口类设计。
三、WEBRTC源码目录结构简要说明 Webrtc代码的目录结构说明:
api | 提供了对外的接口,音视频引擎层和 Module 直接的接口。 |
audio | 音频流的一部分抽象,属于引擎的一部分逻辑。 |
base | 属于 Chromium 项目的一部分,貌似 WebRTC 中用的并不多。 |
build | 编译脚本。这里需要注意的是,不同平台的代码在下载的时候,获取的工具集是不一样的。 |
build_overrides | 编译工具 |
buildtools | 编译工具链 |
call | 主要是媒体流的接口抽象。为媒体引擎和 codec 层提供桥接。这里说的媒体流是 RTP 流。pc 层也抽象了媒体流,那是编码前、或者解码后。 |
common_audio | 音频算法实现,比如 fft。 |
common_video | 视频算法实现,比如 h264 协议格式。 |
data | 测试数据 |
examples | WebRTC 使用的例子。提供了 peerconnection_client、peerconnection_server、stun、turn 的 demo。 |
infra | |
logging | WebRTC 的 log 库。 |
media | 媒体引擎层,包括音频、视频引擎实现。 |
modules | WebRTC 把一些逻辑比较独立的抽象为 Module,利于扩展维护。 |
ortc | 媒体描述协议,类似 sdp 协议。 |
out | build 输出目录,这是 webrtc 官方编译指导中示范目录。 |
p2p | 主要是实现 candidate 收集,NAT 穿越。 |
pc | 实现 jsep 协议。 |
resources | 测试数据 |
rtc_base | 包括 Socket、线程、锁等 OS 基础功能实现。 |
rtc_tools | 网络监测工具、音视频分析工具。很多工具都是脚本实现。 |
sdk | 主要是移动端相关实现。 |
stats | WebRTC 统计模块实现。 |
style-guide | 编码规范说明 |
system_wrappers | OS 相关功能的封装,比如 cpu、clock 等。 |
test | 单元测试代码实现,用 gmock |
testing | gmock、gtest等源码,属于整个 Chromium 项目。 |
third_party | 第三方库依赖。比如,boringssl,abseil-cpp,libvpx等 |
tools | 公共工具集,整个 Chromium 项目依赖的。 |
tools_webrtc | WebRTC 用到的工具集。比如代码检查 valgrind 的使用。 |
video | 视频 RTP 流的抽象接口,属于视频引擎的一部分。 |
help | |
上面我们队源码中的目录做了一个概要介绍,每一个目录又包含了很多功能。如果要是把每个目录都展开讲,那么 WebRTC 的完整架构介绍也就完成了,这显然是不可能的,后续章节会一一展开,详细探讨。本节,我们重点介绍一下 pc 和 module 目录。
PeerConnection
PeerConnection 的主要实现逻辑就是在 WebRTC 源码的 pc 目录下。
一切都从 PeerConnectionFactory 和 PeerConnection 开始,对外提供 PeerConnectionFactoryInterface 和 PeerConnectionInterface 两个接口类。Factory 类,顾名思义就是创建 PeerConnection 的,下来我们只讨论 PeerConnection。
也许你已经非常熟悉 WebRTC 的 Javascript 接口。比如,RTCPeerConnection,setLocalDescription、setRemoteDescription、createOffer、createAnswer 等,没错这些。Javascript 接口的 Native 实现就是在 PeerConnection 中完成的,它也有对应的一套接口。Javascript 这套接口实现规范是JSEP。 可以说是把这套规范的模型都给实现了。
WebRTC 终端之间的通信协议是 ICE 协议,书包格式采用 SDP 协议。PeerConnection 实现了 SessionDescription 的逻辑。
PeerConnection 抽象了 RtpTransceiver,RtpSender、RtpReceiver 模型,对应了 sdp 中描述的媒体的实现。
Module
WebRTC 将逻辑功能独立、内聚性、复用性强的部分单独抽象为模块。模块在 WebRTC 源码的 modules 目录下,主要是音视频设备、codec、流控等,这里不一一列举了。
Module 抽象了一个接口,源码实现在 modules/include/module.h 中,代码如下:
namespace webrtc{
class Module {public:virtual int64_t TimeUntilNextProcess() = 0; virtual void Process() = 0; virtual void ProcessThreadAttached(ProcessThread* process_thread) {}protected:virtual ~Module() {}
};
} // namespace webrtc
一共三个函数,相对简单,主要是为那些需要定时处理一些任务的模块提供一个统一的抽象。对于集成了 webrtc::Module 类的模块,使用的时候,需要调用 ProcessThread 的 RegisterModule 和 DeRegisterModule 方法向模块执行线程注册和反注册模块。我相信大家都知道,控制、调度逻辑是最复杂易错的,实现起来也是最枯燥乏味,设计不好,很容易重复实现很多代码。所以 WebRTC 索性都封装起来,实现者只需要被动的实现功能逻辑即可。
关于函数的具体功能,头文件的注释写的非常详细,可以参考。
后记
本文,作者根据自己的理解,总结了 WebRTC 重点逻辑的基本框架,并且做了简要的说明。紧接着,对 WebRTC m66 版本的源码目录做了一个简要说明,并且针对 pc 目录 和 modules 目录做了进一步说明。因为源码非常庞大,一篇或者几篇是没法分析彻底,需要一点一点的学习。