两种协议的故事:比较 WebRTC 与 HLS 的直播流媒体传输

你还记得上一次观看 WWDC 主题演讲是什么时候吗?你是实时观看的吗?你有没有看到一些关于你还没看到的内容的推文,并感到困惑?

答案是,苹果使用 HLS 进行这些活动的直播,就像大多数其他流媒体视频服务一样,包括那些直播足球比赛和总统辩论的服务。与直播电视广播不同,直播电视广播中每个观众在同一时间看到相同的视频,HLS 具有可变延迟。这意味着观看同一视频的每个观众都有略微不同的时间延迟。你可能比其他人早或晚了整整一分钟看到蒂姆·库克发布 Apple Vision Pro。

在这篇文章中,我们将探讨 HLS 是如何工作的,为什么它具有可变延迟,以及它与一种彻底消除可变延迟的新型直播流技术——服务器介导的 WebRTC——有何不同。

直播流媒体技术的内部运作方式

苹果的主题演讲通常以史蒂夫·乔布斯剧院中面向蒂姆·库克的摄像头开场。在幕后,该摄像头不断地将视频数据包流式传输到媒体服务器(有时是本地运行的演播室软件)。

对于 HLS 而言,该媒体服务器收集这些数据包,并通过编码管道运行它们以生成片段;每个片段通常是一个六秒钟的视频文件。然后,每个片段的副本和一个“播放列表”文件被上传到源服务器,并通过全球的文件服务器(即 CDN)下载。播放列表只是一个文本文件,列出了视频文件应按顺序播放的顺序——与 Spotify 播放列表或专辑列表没有太大区别。

在主题演讲期间,视频数据包不断上传,视频文件不断创建,播放列表不断更新,视频文件和播放列表都由全球分布的文件服务器下载。

当你(或我)导航到一个网页观看主题演讲时,HLS 播放器(即客户端)连接到地理位置最近的文件服务器,下载播放列表文件,找到一个最近的条目(表示最近录制的片段)并开始下载和播放它。随着新的视频文件被录制和相应的播放列表更新,这些片段将由你的客户端在后台获取并添加到你的本地缓冲区进行播放。

基于 HLS 的直播流的高级架构

HLS 建立在自 1990 年代以来我们拥有的基于无状态 HTTP 的交付基础设施之上。简而言之,流媒体将视频文件上传到一个共享文件夹,而观众则轮询同一文件夹以获取新文件并在它们出现时下载。当这种交换在没有网络问题的情况下完成时,它就呈现出观看直播流的外观。

WebRTC 依赖于浏览器中最近引入的 UDP 套接字。与 HLS 相比,WebRTC 媒体服务器充当有状态路由器:首先在流媒体和观众之间建立会话或专用网络路径,然后媒体在它们之间流动。如果苹果的主题演讲使用 WebRTC,媒体服务器会将刚从摄像头出来的视频数据包副本直接推送到观众。

用于大规模直播流媒体的基于 WebRTC 的架构

这两种方法的一个关键区别是:对于 HLS,客户端(流媒体和观众)处于控制地位,并且彼此独立操作。对于 WebRTC,虽然媒体服务器(“SFU”)和客户端有一个紧密的协调循环,但服务器在很大程度上处于控制地位。

在下一节中,我们将探讨这种设计差异变得明显的几种方式。

HLS 和 WebRTC 之间的权衡

🗒️
作者注:虽然我们在本节中专门讨论 HLS,但相同的观点也适用于其他基于片段的协议,如 MPEG-DASH。

延迟

开发人员评估的第一件事是协议开销,或者在理想网络条件下的延迟。

由于数据包被编码成文件以及客户端驱动通过 TCP 获取片段,HLS 的延迟高于 WebRTC 也就不足为奇了。HLS 的基线延迟为 5-30 秒。HLS 有一些变体,例如 LL-HLS 和 Periscope 的 LHLS(由我们自己的 Benjamin Pracht 共同撰写),旨在通过更短的片段长度和交付部分片段等技术来减少延迟,但即使有了这些,延迟仍然保持在 2-5 秒之间。

WebRTC 对媒体数据包的干扰最小,并以最快的速度在发送方和接收方之间推送它们。结果是端到端延迟的上限为 300 毫秒。在优化的网络上,它甚至可以实现光速传输(地球上最长弧线之间的距离为 66 毫秒)。

适应性

就像道路系统一样,网络条件很少是理想的。因此,我们必须研究这些协议如何处理拥塞和低带宽、丢包和抖动等问题。

处理拥塞或低带宽的一种常见技术是自适应比特率(ABR)流。在直播流的上下文中,ABR 回答了这样一个问题:当观众的网络很差时,我们如何动态地交付低质量视频,而当网络良好时,我们又如何交付高质量视频?

HLS 通过让媒体服务器的编码管道生成相同视频数据的多个版本(即文件),并以不同的比特率进行编码来支持流媒体端的 ABR。每个版本都有自己的播放列表文件,一个新的多变体播放列表充当索引,指向每个特定版本的播放列表。然而,客户端(即 HLS 播放器)负责在轮询间隔期间选择要检索的下一个片段的适当版本。由于片段长度通常为六秒或更长时间,HLS 对网络条件变化的反应可能很慢。如果发生这种情况,播放器继续获取比特率高于观众连接所能承受的版本,流可能会暂停以进行缓冲。

ABR 在 HLS 中的工作原理

如果我们像 LL-HLS 那样缩短片段长度,底层 HTTP 传输仍然会带来挑战。TCP 不会将网络拥塞暴露给应用层,这使得客户端难以准确测量可用带宽。因此,即使我们缩短了轮询间隔,并从理论上为 LL-HLS 提供了更多切换比特率的机会,传输层提供的有限可见性意味着客户端可能仍会长时间保持在次优质量级别。

TCP 也不暴露丢包。一旦开始下载片段,HLS 播放器必须要么等待整个片段传输完成(可能在重传期间缓冲),要么取消获取并切换到较低质量的版本,要么跳到下一个片段。启发式方法因播放器实现而异,但在所有情况下,用于决定策略的数据都与网络级别发生的情况相抽象。

WebRTC 使用一种称为联播的方法来支持 ABR。与 HLS 不同,流媒体本身会发布其视频数据的多个版本,具有不同的比特率。然后,(媒体)服务器负责选择要交付给每个客户端的适当版本的流。由于视频数据由数据包而非片段表示,因此 SFU 可以即时动态切换流版本(联播“层”)。

ABR(使用称为“联播”的技术)在 WebRTC 中的工作原理

WebRTC 的基于数据包的交付和对 UDP 的使用使其对传输层具有更大的可见性和控制力。如果视频数据到达较晚或根本没有到达,服务器可以选择重传或简单地转向较新的内容。WebRTC 还包括一套拥塞控制算法,例如 TWCC,可以实时估计链接的带宽能力。

当客户端网络拥塞时,现代编解码器(例如 AV1 和 VP9)支持可伸缩视频编码(SVC)。这允许 SFU 通过跳过数据包来立即降低分辨率、帧速率或两者,从而降低该客户端的带宽要求。虽然 HLS 支持 HEVC(即 H.265),但目前不支持 SVC 编解码器。

同步

正如我们在本文开头提到的,同步可能是 HLS 和 WebRTC 之间最重要但最容易被忽视的区别。

HLS 的问题在于每个客户端都控制着观看体验。片段选择、缓冲区大小、轮询间隔、层切换、可接受的数据速率——所有这些都由 HLS 播放器控制。这意味着观众之间的偏差是不可避免的;我们永远不会在同一时间观看同一件事。有时我们可能只相隔几秒钟,有时相隔三十秒或一分钟。

在构建真正共享的用户体验时,这根本行不通。例如,朋友们一起观看和讨论同一个直播活动是很常见的,结果却发现有人在其他人看到之前无意中分享了剧透。

左侧是 WWDC 2023,右侧是 LiveKit 团队实时在 Slack 上讨论

通过 SFU 控制每个观众看到的内容和时间,以及典型的客户端缓冲区大小(100 毫秒或更少,旨在平滑抖动)的结合,基于 WebRTC 的直播流的观众实际上在同一时间看到了相同的内容。

是否需要同步最终取决于你正在构建的应用程序。对于依赖观众互动的用例(例如直播购物或观看派对)至关重要。对于“单向”广播来说不太重要,但即使如此,你的观众也可能在你的应用程序中(例如通过实时聊天)或在其他第三方应用程序中一起讨论你的内容。因此,让每个人都在同一页(数据包?😉)上至少会带来更好的用户体验,最好会增加参与度或病毒式传播。

规模

CDN 基础设施已经经过几十年的优化,可以处理数百万个并发文件请求——从图像到 JavaScript 应有尽有。鉴于 HLS 依赖 CDN 进行交付,基于 HLS 的直播流可以处理数百万并发观众。然而,值得注意的是,HLS 协议本身并没有内置固有的扩展优势。

虽然 WebRTC 使用数据包而不是文件,但我们仍然可以采用类似 CDN 的扩展技术。例如,数据包数据可以在 SFU 和数据中心之间复制和中继。基于 WebRTC 的直播流的观众连接到地理位置最近的媒体服务器并从中流式传输数据包。这种基于 CDN 原则建模的架构也可以处理数百万并发观众。

用于横向扩展 WebRTC 媒体交付的架构

功能

HLS 和 WebRTC 之间存在明显的权衡,这决定了你可以使用每种技术做什么。

观众互动
HLS 是一种单向协议;流媒体的视频被捕获并写入托管在 CDN 上的文件。对于观众来说,这些文件是只读的,这意味着他们无法与流媒体互动或交流。Twitch 之类的应用程序最终使用 WebRTC 或 WebSockets 构建单独的聊天功能。

WebRTC 专为全双工通信而设计。在直播流中,任何观众都可以通过发布他们的摄像头、麦克风或共享他们的屏幕,立即成为另一个流媒体。该协议还支持数据通道,用于在会话中的用户之间发送任何类型的数据,并经常用于构建实时聊天功能。

流媒体设置
要使用 HLS 进行广播,你通常需要运行演播室软件将摄像头像素转换为 RTMP 流,以及媒体服务器将视频转码和分段为文件。这个管道有很多活动部件,虽然一些桌面和移动应用程序使其变得更容易,但它不如使用 WebRTC 上线那么简单。WebRTC 在几乎所有现代浏览器中都可用,上线只需要几行 JavaScript 代码

VOD
由于 HLS 将视频片段写入托管在 CDN 上的文件,因此它免费提供 VOD(重播和快退功能)。HLS 播放器可以从播放列表文件中的第一个(重播)或任何其他片段(快退)开始播放。

要使用 WebRTC 实现类似的功能,你需要同时运行一个类似于 HLS 的过程。这将直播观众引导到你的 WebRTC 应用程序,而 VOD 观众则被引导到其他地方。去年,我们推出了 Egress,使运行基于 WebRTC 的直播流并同时录制和/或将其流式传输到 Twitch、YouTube/Facebook Live 等第三方服务变得容易。你可以在此处阅读有关发布的信息。

成本

通过互联网传输一定量的数据需要花费一定的金钱。

视频帧包含大量数据,虽然可以使用不同的编解码器在传输前更有效地压缩视频,但 HLS 和 WebRTC 仅定义如何传输该视频。相对于实际的视频帧,这两种协议都不会为我们需要传输的总有效载荷增加可观的数据量。因此,从根本上讲,无论你使用 HLS 还是 WebRTC,成本应该大致相同。如果你要自行托管 HLS 视频文件或管理自己的 WebRTC 服务器,你的成本将主要包括底层云提供商的带宽费用。

作为一种更成熟(即商品化)的技术——大多数大型云提供商本身都提供集成的 CDN 服务——HLS 通常是直播流媒体最便宜的选择。作为一种最初为视频会议设计的新技术,WebRTC 价格昂贵,缺乏竞争导致大多数 WebRTC 提供商收取同样的高价。

我们(LiveKit)相信 WebRTC 是直播流媒体的未来。本着这种精神,我们放弃了行业标准的按分钟计费模式,并选择根据带宽使用量计费,类似于云提供商。这使我们的利益与你的利益保持一致:当我们找到更有效运营网络的方法时,这些节省下来的费用就会转嫁给你。

例如,下一代编解码器(例如 VP9 和 AV1)可以分别平均减少 15% 和 40% 的带宽使用量。当我们使用 AV1 传输你的应用程序数据时,我们向云提供商支付的费用减少了 40%,而你向我们支付的费用减少了 40%。这是我们与流行的基于 HLS 的直播流媒体解决方案的快速比较

服务 LiveKit Cloudflare Amazon IVS
类型 WebRTC CDN HLS HLS
单位成本 $0.12/GB* $1/1000 分钟观看时间 $0.075/小时
总成本 $912 $600 $750
编解码器 LiveKit H.264 LiveKit VP9 LiveKit AV1
单位成本 $0.12/GB $0.12/GB $0.12/GB
带宽使用量 7.65 TB 5.35 TB 4.59 TB
总成本 $912 $642 $551
🗒️
这些计算是假设 10,000 名观众观看流媒体 1 小时,平均 1.7 Mbps (720p)。*在最高端,在这个假设的流媒体期间,总共将传输 7.65TB 的下游数据。假设每天发生一次此类事件,我们使用 LiveKit Cloud 将自动应用的适当容量层价格

审查你的选择

毫无疑问,HLS 是经过实战检验的向大量受众直播流媒体的技术。它可以扩展到数百万观众,并且具有成本效益。但是当谈到构建真正共享的实时体验时,HLS 并不是正确的工具。

“共享”可能意味着观众可以与流媒体动态互动,例如在直播购物、播客或游戏应用程序中。它也可能只是与世界各地的朋友一起观看电影或体育赛事。

WebRTC 的超低延迟、全双工通信和跨观众同步使其成为构建此类体验的理想选择。历史上阻碍其用于此目的的两个问题是缺乏规模和高成本。

最近出现的 WebRTC CDN 通过将传统 CDN 的扩展技术应用于基于边缘的数据包服务器,解决了可扩展性挑战。随着 LiveKit Cloud 转向基于带宽的定价模型,我们将 WebRTC 用于大规模直播流媒体的成本与 HLS 保持一致。下一代 SVC 编解码器的使用将使成本大大降低。

HLS LL-HLS WebRTC 服务器 WebRTC CDN
延迟 10 秒以上 2-3 秒 300 毫秒 300 毫秒
规模 数百万观众 数百万观众 < 1k 观众 数百万观众
成本 $0.06 / 小时 $0.08 / 小时 $0.24 / 小时 $0.05-$0.13 / 小时(取决于容量和比特率)
自适应比特率 (ABR)
可伸缩视频编码 (SVC)
观众同步
互动性
多个发布者

WebRTC 现已具备 HLS 的所有优势,你可以构建几年前不可能实现的体验。如果你正在构建新的东西,希望为现有的直播流媒体应用程序添加多人游戏功能,或者重新评估当前基于 HLS 的设置,请不要忽视 WebRTC。


我们喜欢与大家讨论想法和代码。如果你感兴趣,请来我们的 Slack 社区打个招呼 👋。