要了解 libWebRTC 存在的原因及其如此重要的原因,我们应该从 2011 年谷歌首次宣布一个闪亮的网络浏览器新开源项目时的草根开始。
该项目已经转移到一个全新的网站webrtc.org,现在可以在https://webrtc.googlesource.com/src/找到源代码。此存储库包含大多数现代浏览器(Chrome、Firefox、Opera 等)使用的本机 WebRTC 库。
当然,由于兼容性原因和不同的发布周期,浏览器供应商很少使用完全相同的库和版本。例如,这是Firefox 在 Github 上对 libWebRT C 的克隆,并对项目进行了自己的修改。
实际的 WebRTC 标准
libWebRTC 很神奇,所有的一切,但维护多个平台、浏览器等之间的互操作性的真正使命,是由两个项目实现的:
- IETF : IETF 是一个组织,维护具有各种实时通信标准的文档集合。这些文档不涵盖实际的 WebRTC API,而是涵盖 WebRTC 客户端用于与其他人通信的不同编解码器和协议。例如,ICE、STUN 和 RTP。
- W3:W3 维护您在浏览器中常见的官方 WebRTC API 规范 (
RTCPeerConnection
)。所有浏览器都应遵守此规范,以允许 JavaScript 客户端交叉兼容。然而,在实际实践中,您会发现 Chrome(或“Chromium”)是迄今为止最接近该规范的实现,而其他项目目前实现了该标准的一个子集。
当我们将 WebRTC 作为一个整体来提及时,实际上我们通常指的是这两个规范而不仅仅是 chrome 的 WebRTC 实现。
现在我们已经了解了 WebRTC 空间,让我们更深入地研究 libWebRTC!
libWebRTC
LibWebRTC 是 WebRTC API 的 C++/C 本机实现,它与 Windows、MacOS 和 Linux 兼容。在移动端,它还分别为 Android 和 iOS 提供 Java 和 Objective-C 绑定。
是的,该项目是开源的,但谷歌和其他浏览器(Firefox、Opera)是代码库自成立以来的主要贡献者和推动力。尽管 libWebRTC 最流行的用例是 Web 浏览器,但没有什么能阻止您将其用作本机库来与其他 WebRTC 对等方进行通信。
你应该使用 libWebRTC 吗?
除非你特别热衷于处理 C/C++ 以及随之而来的数百万个其他痛点……你可能会发现使用其他一些实现我们在下面介绍的 WebRTC 规范的开源项目会更容易。
“Mediasoup”项目为 WebRTC API 提供了一个高级 JavaScript/TypeScript 接口。该项目的核心逻辑是用 C++/Rust 实现的。
值得一提的一个值得注意的项目是Pion/webrtc项目,它具有 WebRTC API 的 Golang 实现。当然,我们应该提到 rust 端口WebRTC.rs。让我们也让所有的锈菌们开心吧!
如果“易用性”不是特别重要,并且您正在寻找跨平台支持,那么 libWebRTC 绝对是制作与 Chrome、Firefox 和其他浏览器的浏览器客户端兼容的本机 WebRTC 客户端的最安全选择。
开始使用 libWebRTC
现在我们已经讨论了 WebRTC 空间,让我们来看看如何开始使用 libWebRTC 代码库。
克隆 repo
您可以直接使用git clone <https://webrtc.googlesource.com/src
> 源代码,但建议您安装Google 的 Depot Tools,其中包含一组用于处理该项目的工具。
从克隆软件仓库工具开始:
git clone <https://chromium.googlesource.com/chromium/tools/depot_tools.git>
存储库的根文件夹中有我们稍后会用到的可执行工具,因此将它添加到您的PATH
变量中会很方便:
export PATH=/path/to/depot_tools:$PATH
接下来,我们可以libWebRTC
通过运行获取源代码:
mkdir webrtc-checkout
cd webrtc-checkout
fetch --nohooks webrtc
gclient sync
现在,该src
文件夹应该包含 libWebRTCmain
分支的全部源代码!
构建源
Chromium、libwebRTC 和其他 Google 项目使用ninja
构建系统。
但是在我们可以使用ninja
构建项目之前,我们必须使用另一个工具,gn
.
gnBUILD.gn
使用文件中的配置选项为多个操作系统、可执行文件、共享库等生成忍者构建文件。
该webrtc.GNI
文件包含一些布尔标志,用于控制构建项目的哪些组件,如果您愿意,可以乱搞这些组件。现在,我们将只使用默认值。
要生成 ninja 的构建文件,请运行
gn gen out/Default
默认情况下,这将为 DEBUG 构建生成文件,其中包含我们稍后在 repo 上调试示例时使用的额外数据。
最后,要使用 ninja 开始构建,请运行:
ninja -C out/Default
LibWebrtc 的线程模型
处理任何代码库的第一步是找到代码的入口点。由于 libWebRTC 是一个库,因此很难将其缩小为一个入口点。但是大多数代码库将首先使用代码库中的CreatePeerConnectionFactory 函数,它可以让您轻松创建 WebRTC PeerConnection。此函数的前三个参数对于理解整个 webRTC 代码库至关重要。这三个线程对象是rtc::Thread 类的实例,并在整个代码库中全局使用,以在不同的线程上分配不同的任务,以帮助避免处理任务造成瓶颈网络调用和外部 API:
- network_thread:该线程负责编写流经对等连接的实际媒体数据包,并执行最少的处理任务。
- worker_thread:三个线程中资源最密集的线程,该线程处理接收/发送给对等方的各个视频/音频流。
- signaling_thread:这个线程是peer_connection类的所有外部API函数运行的地方。所有外部回调也在这个线程上运行,因此对于库的用户来说,不要阻塞内部回调是很重要的,因为这会阻塞整个信号线程。
在整个代码库中,libWebRTC 使用RTC_DCHECK_RUN_ON宏来断言函数调用正在正确的线程上运行。这对于阅读代码库的人了解每个函数在哪个线程上运行也非常有帮助。
LibWebRTC 的peer_connection
例子
亲自动手使用 libWebRTC 的最佳方法是使用项目目录中peer_connection
提供的示例。src/examples/peerconnection
如果您按照前面“构建源代码”部分中介绍的步骤进行操作,则应该已经根据webrtc.gni
文件中的默认规则构建了该示例。
示例架构
该示例概述了如何设置简单的对等连接以将视频流从一个客户端发送到另一个客户端,并在 GTK 窗口顶部的接收端显示它:
正如我们在上图中看到的,这个例子中有两个组件:客户端和服务器。在这篇博文中,我们将专注于客户端代码,因为服务器逻辑与 LibWebRTC 本身无关,只是一个在客户端之间路由流量的简单套接字服务器。客户端应用的架构如下:
运行代码
在本指南的其余部分,我们假设您使用的是 Linux 机器。要运行该示例,首先,我们需要启动服务器进程。我们可以运行我们之前构建的二进制文件:
./src/out/Default/peerconnection_server
这应该在端口 8888 上启动套接字服务器:
现在,启动另外两个终端窗口并在每个终端窗口上运行客户端应用程序。将这些窗口附加到gdb
. 这将帮助我们在更改代码库时调试错误。
gdb ./src/out/Default/peerconnection_client
r
如果客户端正确启动,您应该会看到两个 GTK 窗口弹出窗口。
在这两个窗口上单击“连接”,现在您应该会在这两个窗口上看到已连接的对等点列表。
现在,单击任一窗口上的对等点。这应该设置一个 WebRTC 连接,将视频从那个窗口流式传输到另一个窗口!但在撰写本文时,这似乎失败并出现以下错误:😞
让我们尝试调试为什么会出现此错误。
调试/修复客户端二进制崩溃 #1(线程管理中的竞争条件):
当应用程序崩溃时,我们可以bt
在 GDB 窗口上运行以查看调用堆栈,并看到调用了以下函数:
- PhysicalSocketServer::Wait() 在客户端的主线程中调用,它会阻塞直到我们收到套接字消息。
- 单击界面上的对等点时,会收到触发OnMessageFromPeer的套接字消息。
- 这触发了一个新的 Peer 连接的创建,它似乎再次递归地调用 PhysicalSocketServer::Wait() ……
在调试代码时,我们发现发生这种情况是因为我们之前介绍的线程模型中的警告。我们调用 CreatePeerConnectionFactory 的线程实际上是我们客户端应用程序的主线程。工厂的构造函数有一个检查,确保所有函数调用都在信号线程上运行。但是,由于我们从不同的线程调用构造函数,它试图在信号线程上排队调用并阻塞主线程,直到前者的函数调用完成。
这导致 Wait() 在我们的主应用程序线程上递归调用,而 rtc::Thread 类不允许以这种方式调用 Wait() 两次:
我们可以通过简单地在信号线程上排队函数调用而不是在应用程序的主线程上同步调用它来防止这个问题:
这是github gist上带有修复的补丁,您可以使用它在本地计算机上应用修复。现在,当我们重建文件并运行代码时,视频流会正确显示!
这工作了一小段时间,但过了一会儿,视频流似乎卡在了接收端的客户端应用程序上,现在它因不同的分段错误而崩溃:
让我们来看看为什么会这样。
调试/修复客户端二进制崩溃 #2(视频渲染器中的缓冲区溢出):
查看调用堆栈,我们注意到崩溃发生在 main_wnd.cc 文件中的 Redraw() 函数中:
通过分析OnRedraw(),我们发现该函数负责将对端的视频从remote_renderer传输到draw_buffer。在此过程中,代码似乎通过复制同一行上的相邻像素将源视频的宽度缩放2 倍。
由于 memcpy 在一段时间后导致段错误,源视频的大小可能会在一段时间后增加到更大的大小,并且由于我们只初始化了一次 draw_buffer,我们最终溢出了它的边界,因为它仍然尺寸较小。我们可以通过在代码中添加一些日志行来确认这一点,以记录remote_renderer视频的宽度和高度:
RTC_LOG(LS_INFO) << "Video size: " << size;
我们可以通过在每次 remote_renderer 的视频大小发生变化时重新初始化绘制缓冲区来修复此错误,如本补丁中所述:
现在,视频流应该可以无限期地正确呈现了!
作者:Rishit Bansal
原文链接:https://dyte.io/blog/understanding-libwebrtc/
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/5940.html