使用 GStreamer 的 WebRTC Plumbing

GStreamer 是历史最悠久、最成熟的媒体处理库之一。作为 Linux 和 WebKit 中的核心媒体处理元素,它在世纪之交就已推出,因此许多早期的 WebRTC 项目使用它的各种组件也就不足为奇了。如今,GStreamer 通过将各种元素流水线化连接在一起,扩大了帮助开发人员构建 WebRTC 堆栈的选择范围。此外,GStreamer 现在还为端到端 WebRTC 呼叫提供了许多新选项,包括 WHIP 支持。

作者:Matthew Waters,GStramer 咨询公司 Centricular 的顾问。
原文:https://webrtchacks.com/webrtc-plumbing-with-gstreamer/

使用 GStreamer 的 WebRTC Plumbing

GStreamer 简介

有些读者可能知道 GStreamer 并曾使用过它。对于新手来说,GStreamer 是一个基于图形的多媒体框架,其中的组件可以连接在一起执行多媒体任务。GStreamer 广泛应用于各行各业,包括但不限于太空小工具、安全摄像头、医疗设备、电话会议、引力波分析、无线电流媒体、非线性视频编辑等。GStreamer 支持多种不同的网络传输,如 RT(S)P、SRT、RIST、HTTP(S)等,WebRTC 是其中最新的一种。读者如果想了解 GStreamer 的一般介绍,可以参阅 GStreamer 文档

GStreamer 有许多插件和元素,能以许多有趣的方式转换数据。项目生态系统还包括各种用于不同目的的“out-of-tree”插件和元素。其中一些元素与特定的硬件编码器或解码器相关联。有些元素在严格的多媒体相关流程之外的领域提供功能,如引力波分析。这种灵活性允许将所有这些构件(GStreamer 所称的元素)组合到一个管道中,以执行所需的任务。

使用 GStreamer 的 WebRTC Plumbing
高级 GStreamer 架构。来源:gstreamer.freedesktop.org

就 WebRTC 而言,这种灵活性使得 WebRTC 连接的源可以是 GStreamer 已经支持(或可以开发)的任何内容,如本地摄像头、RT(S)P/SRT/RIST/RTMP/HTTP 网络源、录制或读取文件、HLS 流媒体、其他 WebRTC 调用等。反之亦然。接收到的 WebRTC 流可以转发到许多不同的位置。结合对许多硬件编码和解码元素的支持,GStreamer 可以利用其运行硬件的大多数方面。

使用 GStreamer 的 WebRTC Plumbing
将文件源转换为音频和视频汇的 GStreamer pipeline 示例。来源:gstreamer.freedesktop.org

GStreamer 与 WebRTC 的历史

让我们开始探索 GStreamer 的 WebRTC 之旅,简要介绍一下 GStreamer 获得 WebRTC 原生支持之前的情况。WebRTC 早期有几个有趣的项目:OpenWebRTC、Kurento 和 Farstream,一起来了解下。

OpenWebRTC

OpenWebRTC 是爱立信的一个研究项目,旨在为 WebRTC 应用程序提供移动友好的 SDK,当时 libwebrtc 还未在该领域取得一些进展。GStreamer 的 WebRTC 实现中目前可用的部分功能也是 OpenWebRTC 的一部分。如今,OpenWebRTC 已不再维护,而是指向原生 GStreamer WebRTC 支持。

Kurento

Kurento 当时仅限于 MCU 的功能,需要重新编码数据流。它的目标是为 WebRTC SFU/MCU 服务器提供完整的功能,现在仍然如此。因此,它的 WebRTC 实现非常高级,与 Kurento 架构的集成相当紧密。当时,Kurento 的信令基础设施是一个必要组件。如今,Kurento 只处于维护模式,并已被 OpenVidu 取代,我对 OpenVidu 没有任何经验。

webrtchacks 编辑注:

向 OpenVidu 团队进行了核实,他们证实 Kurento 确实依赖 GStreamer 来路由、转码和处理媒体,但该项目没有重大开发计划。OpenVidu 是更高级别的 API,因此支持其他媒体服务器,主要是 mediasoup。

Farstream

Farstream 是另一个使用 GStreamer 的老项目。该项目旨在为音频/视频会议应用提供必要的基础设施。它帮助支持的一些 GStreamer 元素包括原始 RTP 元素和 libnice ICE GStreamer 元素。

让 GStreamer 像 libwebrtc 一样

在这种环境下,我被要求实现一个封装 libwebrtc 功能的 GStreamer 元素,以便在 GStreamer Plumbing中使用。这个方法相当成功,但也有很多局限性。其中最有问题的是:

  • libwebrtc 不能保证提供稳定的 API
  • 几乎不支持应用程序提供的视频解码器和编码器
  • 构建过程非常复杂,需要依赖谷歌专用工具

有鉴于此,我决定研究结合 GStreamer 中已有的相关组件来构建 WebRTC 实现的可能性。

初始实现:webrtcbin

WebRTC 可通过 webrtcbin 插件在 GStreamer 中使用。在构建 webrtcbin 的过程中,我们首先研究了最小实现所需的功能。这项工作的一个强大动力是,用户可以使用自己想要的编码器或解码器,还可以灵活地使用任何类型的源媒体。这意味着 webrtcbin 主要关注的是传输和网络方面的问题,而不是数据编码的细节。很快,我们就列出了以下所需的组件(及其各自的实现):

  • ICE – libnice 用于启动 ICE 连接的 GObject 库
  • RTP – GStreamer 现有的经过实战检验的 RTP 协议栈
  • SRTP – libsrtp2 以及在 OpenWebRTC 中实现的一些相关 GStreamer 元素
  • DTLS – OpenWebRTC 开发人员编写的一些与 OpenSSL 有关的 GStreamer 元素

信令

除上述内容外,我们还必须考虑如何处理信令。我们需要一种方法来处理 webrtcbin 元素之间、webrtcbin 与浏览器之间以及浏览器与 webrtcbin 之间的信令。我们决定模仿 JSEP:Javascript 会话建立协议(现为 RFC 8829)中概述的 Web 应用程序使用的 API。虽然在实现过程中仍有一些缺失,但我们的目标是最终支持 JSEP 的全部内容。

可以在此处查看开放的 “JSEP “问题,也可以在此处查看所有开放的 “WebRTC “问题。

媒体

在媒体方面,webrtcbin 可以摄取这些媒体类型中的一种:

  • 原始音频/视频帧
  • 编码音频/视频 – OPUS/H.264/VP8/AV1 等
  • RTP 有效载荷数据包(GStreamer 术语中的 “payloaded”)

由于要求用户使用他们选择的任何编码器,webrtcbin 将最低的合理数据格式作为输入—— 单个 RTP 有效负载数据包。webrtcbin 可以处理包括 RTP 会话、DTLS 连接、SRTP 启动/解密和 ICE 连接在内的一切事务。

由于使用了 GStreamer 现有的 RTP 协议栈,一些 RTP 功能开箱即受支持。RTCP 和 AVPF RTCP 配置文件等功能从第一天起就已实现。对 PLI、FIR 和 NACK 的支持也从第一天开始就已实现。

经过一段时间的开发,webrtcbin 的初始版本于 2017 年 10 月在年度 GStreamer 大会上向公众发布。公开发布的视频录像可从 https://gstconf.ubicast.tv/videos/gstreamer-webrtc/ 获取。2018 年初,webrtcbin 被合并,并将成为 GStreamer 1.14 版本的一部分。

使用 GStreamer 的 WebRTC Plumbing

缩小功能差距

webrtcbin 发布之初,与 libwebrtc 的功能相比,GStreamer 对 WebRTC 的实现非常有限。为了缩小功能差距,我们重新实现了 libwebrtc 中的大部分功能。为了加快进度,我们使用了 OpenWebRTC 项目中的许多元素。

有些功能在最初实施后很快就实现了,因为它们已经有了一些现有的 GStreamer 元素。前向纠错(FEC)在视频方面使用 ULPFEC,在音频方面使用 RED,就像 libwebrtc 所使用的那样。libwebrtc 的 ULPFEC 版本与RFC 版本略有不同,但差别很小,但却很重要。重传 (RTX) ( RFC 4588 ) 基于其他使用 RTP 的场景中一些先前存在的 GStreamer 元素。其他一些可快速添加的功能还包括 TURN 服务器支持、添加一些属性以遵循当时不断变化的 WebRTC 规范。

下面简要介绍其他一些核心功能:

  • 数据通道:集成了 OpenWebRTC 的 SCTP 元素,并实施了 DCEP 协商(RFC 8832:数据通道建立协议)。
  • RTP 捆绑:GStreamer 的 RTP 实现支持接收捆绑的 RTP 流。为 webrtcbin 添加了配置,以便在单个套接字上对多个 RTP 流进行发送和接收分组。
  • 流重新协商:2019 年添加了对添加/删除 RTP 流的初步支持,但有一些限制,例如,未移除或重用transceiver/m-line时“已删除”的流将被标记为非活动状态。
  • DSCP:添加了差异化服务代码点支持,可为支持 DSCP 的中间网络设备标记不同优先级的流,从而提高网络拥塞期间的性能。
  • 端口选择:公开 libnice 的最小和最大 RTP 端口范围,以控制发送/接收数据的本地端口分配,为网络防火墙后的应用程序提供帮助。
  • 传输范围拥塞控制(TWCC):对发出的数据包实施 RTP 包头扩展,对收到的数据包实施 TWCC 反馈,以监控带宽使用情况。配置编码器的责任在于应用程序或 webrtcsink 等父级元素。
  • 同步广播(仅发送或仅接收):添加了 RTP 级联播支持,可发送/接收同一数据流的不同质量。这涉及在 RTP 付载过程中添加 RTP 标头扩展(MID、RID)。
  • 兼容性:努力支持具有不同 SDP 格式的远程对等设备,包括不同的数据通道格式、非捆绑对等设备、ICE-lite 实现、SDP 中的 ICE 候选设备,以及浏览器对等设备的 MSID 支持,以允许对流进行分组。
  • 统计信息:与 Web 的 PeerConnection API 类似的接口,用于连接统计信息,但不适用于 webrtcbin 的编码/解码和打包。我们不断提高与 webrtcbin 范围相关的报告统计数据的质量和数量。

其他集成

虽然 webrtcbin 功能强大,支持多种应用场景,但在编写成熟的应用程序时,实施难度相当大。例如,拥塞控制功能必须针对应用程序使用的特定编码器来实现。RTX/FEC 设置在某些浏览器中也存在兼容性问题,TWCC 在不同浏览器中的表现也可能不同。所有这些都意味着每个 webrtcbin 应用程序都需要额外的工作。

为了降低实现成本,GStreamer 为一些有限的情况添加了辅助元素——webrtcsinkwebrtcsrc

webrtcsink

webrtcsink 是在 webrtcbin 基础上构建的第一个辅助 GStreamer 元素。webrtcsink 被设计为 “batteries included “的仅用于发送 WebRTC 的元素。它实现了几项附加功能:

  • 通过与对等方协商,为一组特定的编解码器选择相关编码器
  • 使用 TWCC 信息和 GCC(谷歌拥塞控制)算法的重新实现进行拥塞控制(rtpgccbwe 元素)
  • 自定义信令实现,便于在命令行上使用。

该数据流的多个消费者可以对源数据流进行各自独立的编码,并可根据其特定的网络条件对数据流进行定制。例如,假设发送对等点的网络不是问题,网络差的对等点不会影响网络好的对等点的质量。

webrtcsink 还支持接收预编码流(H.264、H.265、VP8 等)。不过,它不能为您执行相关的拥塞控制。要使拥塞控制在预编码流中发挥作用,应用程序必须参与进来,对编码器的比特率进行相应的配置。

我们还在考虑实施比特率阶梯支持的一些初步想法。当一个数据流需要以不同的分辨率和比特率进行固定次数的编码,以限制特定数据流的编码次数时,就需要这种支持。

默认的编解码器包括 VP8、VP9、H.264 和 H265。应用程序也可以限制编解码器的选择。

此外,请注意 AV1 支持最近已被合并到上游合并请求中。

最后,还有一个自定义信令器实现,用于处理将 webrtcbin 生成的 SDP 和 ICE 候选文件传输给对等设备。如果应用程序需要与不同的信令基础架构通信,可以重写该信令器。要与不同的 WebRTC 相关服务集成,通常需要为 webrtcsink 或 webrtcsrc 编写信令实现。上游还有一些外部服务的信令实现,如 AWS KVS、LiveKit、WHIP 等(下文将详细介绍)。

在命令行中运行此功能可以非常简单:

gst-launch-1.0 -v \
  videotestsrc ! video/x-raw,width=640,height=480,framerate=30/1 ! \
  webrtcsink stun-server=stun://stun.l.google.com:19302 signaller::uri=ws://<your-signaling-server>:8443
使用 GStreamer 的 WebRTC Plumbing

插件的 README 是提供可用功能概述的最佳资源。

webrtcsrc

webrtcsrc 支持与 webrtcsink 相同的自定义信令,一个 webrtcsink 可以为多个 webrtcsrc 消费者提供服务。

webrtcsrc 支持将接收到的数据流内部解码为原始音频/视频,但也可以按原样输出编码(VP8,VP9,H.264等)数据而无需任何解码。

也可以选择启用自定义数据通道协议,以便将接收器上的导航事件转换为发送器。webrtcsink 支持相同的数据通道协议。

在命令行中,如下:

gst-launch-1.0 -v \
  webrtcsrc stun-server=stun://stun.l.google.com:19302 signaler::uri=ws://<your-signaling-server>:8443 ! \
  queue ! videoconvert ! autovideosink

其他 WebRTC 集成

为简化 WebRTC pipeline 开发,GStreamer 为一些 WebRTC 服务提供了信令集成:

  • AWS Kinesis Video Streams:我们的首个外部信令实施针对 AWS 的 Kinesis Video Streams,它支持从 GStreamer 向 AWS 传输流媒体的 webrtcsink 功能。
  • LiveKit:LiveKit 的信令器支持 webrtcsink 和 webrtcsrc 角色,并已提出合并请求草案,以便与 LiveKit SFU 实现同步广播。
  • Janus VideoRoom:Janus VideoRoom 的信令器支持 webrtcsink 功能,并有一个上游合并请求草案,以纳入 webrtcsrc 支持。
  • WHIP:新的通用 WHIP 信令规范的信令发生器已可用,允许客户端和服务器使用 whipserversrc 和 whipclientsink。
  • WHEP:两份关于 WHEP 信令的合并请求草案涵盖了客户端和服务器实现。该主题在 GStreamer 内部仍在进行中。

未来

以下是我们目前正在开展工作的一些主要领域。

Rust

内存安全编程语言是一种新趋势,尤其是在处理可能不受信任的数据时。通过网络接收的 RTP 数据包就是可能不受信任的数据的一个例子。畸形数据包不应导致错误的应用程序行为。因此,我们一直在努力用 Rust(一种内存安全语言)重新实现 GStreamer RTP 的部分基础架构。我们已经在各种元素中实现了多个 RTP 有效载荷器和卸载器。我们还用 Rust 重写了 RTP 会话管理。我们还没有做到这一点,但 Rust 最终可能会取代现有的基于 C 语言的实现。

librice

另一个可能涉及不信任数据的途径是 ICE。我们也在努力用 Rust 编写 ICE 状态机的 sans-IO 实现(可从 https://github.com/ystreet/librice 获取)。sans-IO 是一种设计模式,所有外部状态都作为输入提供给运行中的状态机。这为应用程序接口的用户提供了灵活性和可重用性。对于那些不想使用 sans-IO API 的用户,我们会在上面提供一个辅助的 rust async API。

使用 sans-IO 设计模式的主要原因是,为 webrtcbin 提供 ICE 连接的 libnice 目前使用 GLib 和 GIO 进行联网,而 GLib 和 GIO 的性能并不尽如人意。在 libnice 或 GIO 中添加对一些更新、性能更好的 IO API 的支持是一项复杂的工作。sans-IO 设计允许应用程序灵活地提供自己的(高性能)IO 或使用 librice 的异步实现。

此外,sans-IO librice 还有一些(对 WebRTC 并无用处)兼容模式,适用于需要非 RFC 兼容的 ICE 实现的老化环境。这就导致了一些实施的复杂性,而仅使用 RFC 的 ICE 实施则不需要这些复杂性。这阻碍了 libnice 新功能的添加。

WebKitGTK/WebKitWPE

作为 Web 框架/浏览器,人们希望通过一些 WebKit 端口来支持 WebRTC。这些 WebKit 端口已经使用 GStreamer 支持 MSE、<video>标签等的媒体播放。因此,将 GStreamer 用于 WebRTC 播放也是合理的。WebRTC 支持曾基于 libwebrtc。但是,对于嵌入式使用案例,libwebrtc 并不容易集成。目前的默认实现基于 webrtcbin。

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/50700.html

(0)

相关推荐

发表回复

登录后才能评论