深入探讨:VideoNG

508 阅读8分钟

深入探讨:VideoNG

原文链接:https : //developer.chrome.com/blog/videong/

我是 Dale Curtis,Chromium 媒体播放的工程负责人。我的团队负责面向 Web 的视频播放 API,如MSEWebCodecs,以及涉及多路分离、解码和渲染音频和视频的平台特定内部结构。

在本文中,我将带您了解 Chromium 的视频渲染架构。虽然有关可扩展性的一些细节可能是特定于 Chromium 的,但这里讨论的大多数概念和设计都适用于其他渲染引擎,甚至本机播放应用程序。

多年来,Chromium 的播放架构发生了重大变化。虽然我们不是从本系列第一篇文章中描述的成功金字塔的想法开始,但我们最终遵循了类似的步骤:可靠性、性能,然后是可扩展性。

开始,视频渲染非常简单——只是一个 for 循环,选择将哪些软件解码的视频帧发送到合成器。多年来,这已经足够可靠,但随着 Web 复杂性的增加,对更高性能和效率的需求导致架构发生变化。许多改进需要特定于操作系统的原语;因此,我们的架构也必须变得更具可扩展性,以覆盖所有 Chromium 平台。

img

视频渲染可以分为两个步骤:选择要传递的内容和有效地传递该信息。为提高可读性,在深入探讨 Chromium 如何选择交付内容之前,我将介绍高效交付。

#一些术语和布局

由于本文侧重于渲染,因此我将仅简要介绍管道的解复用和解码方面。

解复用是媒体管道将字节流转换为单独编码的音频和视频数据包的过程。

解码是将这些数据包转换为原始音频和视频帧的过程。在媒体播放的上下文中,渲染是选择及时呈现那些解码的音频和视频帧。

img

在我们具有安全意识的现代世界中,解码和解复用需要相当小心。二进制解析器是丰富的目标环境,媒体播放充满了二进制解析。因此,媒体解析器中的安全问题非常普遍。

Chromium进行深度防御,以降低用户面临安全问题的风险。实际上,这意味着解复用和软件解码总是发生在低特权进程中,而硬件解码发生在具有与系统 GPU 对话的足够特权的进程中。

img

Chromium 的跨进程通信机制称为Mojo。虽然我们不会在本文中详细介绍 Mojo,但作为进程之间的抽象层,它是 Chromium 可扩展媒体管道的基石。在我们浏览播放管道时意识到这一点很重要,因为它通知了交互接收、解复用、解码和最终显示媒体的跨进程组件的复杂编排。

#这么多位

了解当今的视频渲染管道需要了解视频的特殊性:带宽。每秒 60 帧的 3840x2160 (4K) 分辨率播放使用 9-12 吉比特/秒的内存带宽。虽然现代系统的峰值带宽可能达到每秒数百吉比特,但视频播放仍然占很大一部分。如果不小心,总带宽很容易因 GPU 和 CPU 内存之间的复制或跳闸而倍增。

任何考虑到效率的现代视频播放引擎的目标都是最小化解码器和最终渲染步骤之间的带宽。出于这个原因,视频渲染在很大程度上与 Chromium 的主要渲染管道分离。具体来说,从我们的主要渲染管线的角度来看,视频只是一个不透明的固定大小的洞。Chromium 使用称为表面的概念实现了这一点——每个视频直接与Viz对话。

img

由于移动计算的普及,功耗和效率已成为当前这一代人的重要关注点。这样做的结果是,解码和渲染在硬件级别比以往任何时候都更加耦合——导致视频看起来就像一个不透明的洞,甚至对操作系统本身也是如此!平台级解码器通常只提供不透明的缓冲区,Chromium 以覆盖的形式传递到平台级合成系统。

img

每个平台都有自己的覆盖形式,它们的平台解码 API 可以与之协同工作。Windows 有Direct CompositionMedia Foundation Transforms,macOS 有CoreAnimation LayersVideoToolbox,Android 有SurfaceViewMediaCodec,Linux 有VASurfacesVA-API。Chromium 对这些概念的抽象分别由OverlayProcessormojo::VideoDecoder接口处理。

在某些情况下,这些缓冲区可以映射到系统内存中,因此它们甚至不需要是不透明的,并且在访问之前不会消耗任何带宽——Chromium将这些称为 GpuMemoryBuffers。在 Windows 上,这些由DXGI 缓冲区、macOS IOSurfaces、Android AHardwareBuffers和 Linux DMA 缓冲区支持。虽然视频播放通常不需要此访问权限,但这些缓冲区对于视频捕获非常重要,可确保捕获设备和最终编码器之间的带宽最小。

img

由于 GPU 通常负责解码和显示,因此使用这些(通常也是)不透明缓冲区可确保高带宽视频数据实际上永远不会离开 GPU。正如我们之前所讨论的,将数据保存在 GPU 上对于提高效率非常重要。特别是在高分辨率和帧速率下。

我们越能利用覆盖和 GPU 缓冲区等操作系统原语,花费在不必要的视频字节混洗上的带宽就越少。将所有内容从解码一直到渲染都集中在一个地方可以带来令人难以置信的能效。例如,当 Chromium在 macOS 上启用覆盖时,全屏视频播放期间的功耗减半!在WindowsAndroidChromeOS等其他平台上,即使在非全屏情况下,我们也可以使用叠加层,几乎在任何地方都可以节省高达 50% 的费用。

#渲染

既然我们已经介绍了最佳交付机制,我们可以讨论 Chromium 如何选择交付内容。Chromium 的播放堆栈使用基于“拉”的架构,这意味着堆栈中的每个组件都按层次顺序从其下方的组件请求其输入。堆栈的顶部是音频和视频帧的渲染,下一个是解码,然后是解复用,最后是 I/O。每个渲染的音频帧都会提前一个时钟,当与演示间隔结合时,该时钟用于选择要渲染的视频帧。

在每个呈现间隔(显示器的每次刷新),视频渲染器都被要求通过附加到前面提到的SurfaceLayerCompositorFrameSink提供视频帧。对于帧率小于显示率的内容,这意味着同一帧显示不止一次,而如果帧率大于显示率,则某些帧永远不会显示。

以令观众满意的方式同步音频和视频还有很多事情要做。有关如何在 Chromium 中实现最佳视频平滑度的详细讨论,请参阅Project Butter。它解释了如何将视频渲染分解为代表每帧应显示多少次的理想序列。例如:“每显示间隔 1 帧([1],60 Hz 为 60 fps)”、“每 2 个间隔1 帧([2],60 Hz 为 30 fps)”,或者更复杂的模式,如 [2:3 :2:3:2](60 Hz 时为 25 fps)涵盖多个不同的帧和显示间隔。视频渲染器越接近这种理想模式,用户就越有可能认为播放是流畅的。

img

虽然大多数 Chromium 平台逐帧渲染,但并非所有平台都这样做。我们的可扩展架构也允许批量渲染。批处理渲染是一种效率技术,其中 OS 级合成器被提前告知多个帧,并在应用程序提供的时间安排上处理释放它们。

#未来是现在?

我们专注于 Chromium 如何利用操作系统原语来提供一流的播放体验。但是那些想要超越基本视频播放的网站呢?我们能否为他们提供与 Chromium 本身用于引入下一代 Web 内容相同的强大原语?

我们认为答案是肯定的!如今,可扩展性是我们如何看待 Web 平台的核心。我们一直在与其他浏览器和开发人员合作开发新技术,例如WebGPUWebCodecs,以便 Web 开发人员在与操作系统对话时可以使用与 Chromium 完全相同的原语。WebGPU 带来了对GPU 缓冲区的支持,而 WebCodecs 带来了与上述覆盖和 GPU 缓冲区系统兼容的平台解码和编码原语。

img

#流结束

谢谢阅读!我希望您已经对现代播放系统有了更好的了解,以及 Chromium 如何为每天数亿小时的观看时间提供动力。如果您正在寻找有关编解码器和现代网络视频的进一步阅读,我推荐Sid Bala 的H.264 is magic、Erica Beaves 的现代视频播放器的工作原理,以及Cyril Concolato 使用获奖技术包装获奖节目

Una Kravets 的一幅插图(漂亮的!)。