来源
:W3C

主讲人
:Paul Adenot

内容整理
:尹文沛

主讲人介绍了在 WebCodecs 中 Memory access patterns 这项技术的当前状况和未来发展。

前言

今天,我想谈谈最近一个叫做 WebCodecs API 的API。特别是当在 web 平台上与其他 API 一起使用此 API 时的内存访问模式。我们将讨论访问视频帧时的一些原始性能数据,WebCodes 目前为最小化内存、访问开销所做的工作,以及 API 目前存在的一些问题,其中有一个解决方案,但尚未实现。更重要的是,我们将在未来解决两个更难的问题,这样使用 WebCodec 将具有与本地应用相同的性能。
在开始之前,我想说的是,任何简单的场景,比如解码和渲染视频和音频都是花了大功夫来优化的,我们将讨论高级用例,比如编译到WebAssembly 的大型本机应用程序,它们利用 WebCodes 来加速编码和解码操作,或者其他程序正在大量使用 WebCodes,并希望最大限度地提升效率。
但首先让我重复一句在自由软件多媒体开发界经常听到的老话:“memcpy就是谋杀”。在这个开玩笑的句子背后有一个重要的事实:为了最大限度地提高性能,需要尽可能少的内存拷贝。应用程序的工作集需要尽可能小,以便适配 CPU 缓存。将内存提取到缓存中的速度很慢,缓存很小,因此该类操作越少越好。

文件尺寸问题

但是首先让我们考虑文件大小的问题。YUV420全高清视频帧的标准动态范围约为 4MB。YUV420 4K标准动态范围的视频帧约为 16MB。P010全高清高动态范围(10位或12位)的视频帧约为32MB。
图1 常见 YUV 帧的大小
下面是我收集的一些性能数字,在我的高端 Linux 工作站上编写一个小 C++ 程序,复制一个这样的大小的帧。当缓存处于热状态时,YUV420(全高清,标准动态范围)中的视频帧需要大约1.5ms才能复制,这意味着源已经在缓存中。如果没有,则为4.5毫秒。需要从内存中提取源代码。两个帧很容易放入这个 CPU 的缓存中。
图2 执行帧复制操作的时间
YUV 420 4K标准动态范围的视频帧在 CPU 缓存中需要 6.6ms 和  17ms。这只是因为我运行的 CPU 有很多缓存,而且才复制了一帧而已。
P010、全高清、高动态范围(10位或12位)的视频帧,如果缓存是热启动的,则需要 15 毫秒,如果不是热启动的,则需要 33 毫秒。我运行这个程序的 CPU 有 20MB 的三级缓存,所以只适合部分帧。
考虑到 30Hz 的单帧预算是 33ms,60Hz 的单帧预算是 16.6ms,我们很快就会发现这里的数字令人不舒服,最小化内存拷贝将非常重要。我要重复一遍,网络代码中的标准场景,比如回放,不会复制。我这里讲的是高级用例,比如处理。

GPU到CPU的读回和上传

此外,GPU 到 CPU的拷贝(回读)和 CPU 到 GPU 的拷贝(纹理上传)也相当昂贵。如果可能的话,最好仔细考虑什么时候以及如何去做,尽量减少转移。WebCodecs 经过精心设计,可以轻松地将视频帧留在 GPU 上,并使所有副本清晰可见。

为什么需要拷贝操作

但有时真的有必要进行拷贝操作。以下是三种不可避免的情况:
  1. 使用常规JavaScript 或 WASM 代码对 GPU 上的视频帧进行自定义处理,在这种情况下,我们需要读回。
  2. 通过 WASM 对 CPU 上的视频帧进行自定义处理,然后需要将数据复制到 WASM 堆中.
  3. 使用其他需要复制的 web API。例如,使用 AudioWorklet 播放音频数据需要复制到 AudioWorklet 输出缓冲区。

WebCodes 尽可能地减少拷贝操作

WebCodes 的设计考虑到了拷贝最小化。脚本无法直接看到内存,需要调用一个名为 copyTo 的函数来获取可以直接操作的 ArrayBuffer。在某些情况下,这个 copyTo 方法还可以处理转换。在视频帧或音频数据对象上调用 clone 时,底层资源将被第二次引用,而不是复制,因此单个帧可以在不同的上下文中有效地使用。进行深度复制仍然是可能的。
现在,让我们来看看今天存在于网络视频中的一些 copy 操作。首先,当前复制解码器压缩输入。这并不是什么问题,因为输入比输出小得多,可以被优化。更重要的是,如果内存是常规内存而不是 GPU ,则无法拥有视频帧或音频数据背后的内存访问权限。最后,API 进行了大量分配和释放,不必要地破坏了CPU缓存。

两个例子

图3 必要的拷贝操作1
图4 必要的拷贝操作2
下面是两个简单的设计方案,可以解决列表中最后两个问题。
首先,一个名为 detach 的方法可以在一次调用中返回一个ArrayBuffer 并关闭视频帧,在可能的情况下跳过一个副本,例如,当它尚未关闭时,这是相当常见的。类似地,我们可以在AudioData 上添加此方法。
接下来,我们可以通过在解码方法中使用一个缓冲区来限制本机分配和播放压力,在解码方法中,解码数据将被写入缓冲区,并在输出回调中返回输入缓冲区以重用。这对于音频来说非常重要,因为缓冲区更容易放入 CPU 缓存。
现在,让我们来谈谈当今网络平台面临的一些更难的问题。首先,当要复制的数据足够大(例如视频帧)时,从 WASM 堆复制和复制到 WASM 堆的必要性就会出现问题。解码到 WASM 堆将是一个受欢迎的特性。
然后是通过 SharedArrayBuffer 将视图传递给 API 的问题。SharedArrayBuffer 通常是限制拷贝的一个很好的解决方案,但通常不清楚这些 API 对内存做了什么,以及它们是否处理对已传递给它们的内存区域的并发写入。在 SharedArrayBuffer 上设置只读内存范围可能是一个解决方案,但这是一个复杂的问题。
附上演讲视频:

在 web 上构建音频应用程序

来源
:W3C

主讲人
:Hongchan Choi

内容整理
:尹文沛

主讲人介绍了在网络上构建音频应用程序的一些想法和考虑,展示一些关于网络媒体制作的一些讨论。
首先抛出一个问题:如果你今天要创建一个网络音频应用程序,你需要考虑哪些事情
显然,您首先需要了解的是 Web 音频 API,但今天我不打算在这里讨论如何使用它。
它已经存在了十多年,我们有很多代码示例和教程。相反,我想讨论它的体系结构和性能特征。

音频 API 的体系结构和性能特征

首先,Web Audio API 是一个基于图形的音频编程环境。有几个音频节点可以相互连接以创建图形。
其次,图形渲染器由专用的高优先级线程运行,该线程通常是实时线程。
这种设计是不可避免的,因为 Web 音频 API 是 Web 平台的一部分。
图5
直接在应用程序的主线程上处理音频流通常会导致糟糕的用户体验。这就是为什么 web 音频节点位于主线程上,而实际的音频处理(我称之为内部处理)发生在专用的独立线程上。
不管是好是坏,Web Audio API 对开发人员隐藏了低级音频实现。这意味着你不必从头开始写振荡器、滤波器或压缩器,它是由实现提供的。但这也意味着,当你想操控裸机时,事情可能会很快变得复杂,比如实现自己的过滤器来处理音频样本。
图6
对于这种用例,Web Audio API 有 AudioWorklet。有了这个对象,您可以使用 JavaScript 和 WebAssembly 编写自己的音频处理模块。
另一个有趣的方面是:Web Audio API 是一个JavaScript API。正如你已经知道的,JavaScript 是一种垃圾收集语言,有一些有争议的怪癖,比如键入和作用域等等。在构建更大规模的真实产品时,会遇到与垃圾收集和性能相关的问题。
这是你无法控制的事情,而且在不同的浏览器中有所不同,但你必须注意。
从技术上讲,垃圾收集不应该影响 Web Audio API 的呈现程序,因为它运行在不同的线程上,但情况并非总是如此。尽管你的代码是完美的,没有创建任何垃圾,但你使用的库可能是浪费的,它可能会导致垃圾收集。一次创建太多对象最终会给音频渲染器带来压力,因为音频节点是垃圾收集的对象,尽管内部不是,但它们仍然关联在一起。

实例分析

我们可以检查和分析其性能,明白事情发生的时间和方式。
在Chrome中,你可以使用Web Audio perf toolkit,这是我今天的第一个分享。
图7
首先是 Web Audio DevTools 面板。这是一个非常简单的工具,允许您监控音频系统的运行状况及其渲染能力。
如果您遇到音频故障,很可能是两种情况之一。
A:回调时间是不规则的,当渲染器在低优先级线程上运行时可能会发生这种情况
B: 音频处理负载超出了 CPU 容量。发生这种情况的原因有很多,但最终,你做得太多了,回调超时了。
DevTools 面板提供了这两个方面的指标。
图8
其次,我们有音频图形可视化工具扩展。这是我们工具包中最近添加的内容。这不是 Chrome 附带的,所以你必须从 Chrome 网络商店安装,这只是一个一次性过程。
此工具至少在两种情况下有用。首先,一个更大规模的 web 音频应用程序通常会构造和销毁很多音频节点。通过阅读源代码,很难发现它们之间的错误连接。
可视化技术在精确定位错误方面有很大的优势。
其次,它允许您了解图形的冗余程度。您可能无缘无故创建了太多增益节点。使用多个增益节点包装子图是非常常见的技术。
此外,可能会创建一个孤立节点,但它没有连接到任何东西,这也非常常见。
最后,你可以使用Chrome的追踪工具。与之前的选项相比,这有点复杂,但它的功能很全面。
图8
这个工具之所以重要还有两个原因。首先,这准确地显示了事情发生的时间和方式。你将能够看到音频流何时出现故障,比如缓冲区不足,并对原因做出猜测。
其次,当你与 Chromium 工程师交流时,这是非常有用的。我们很可能没有与您完全相同的设置,因此可能无法复制该问题。因此,在修复 bug 时,与我们交换跟踪文件确实有助于沟通。

用户隐私

当你在构建客户端应用程序时,比如乐器、录音机、编辑器或DAW,很快你就会意识到,缺乏对音频设备的访问是 web 和本机平台之间的一个巨大差距。
这意味着与设备相关的设置,例如通道数、采样率和缓冲区大小,不适合您的应用程序。
浏览器实现者,实际上已经意识到这对开发人员来说是一个巨大的痛点,但这并非没有原因。
广告商或攻击者可以利用此设备相关信息推断用户身份。这种技术被称为指纹识别,这是我们不能在网上拥有隐私的原因之一。
当然,对于这种类型的利用,有一些对策,例如基于约束的API模式。应用程序可以进行查询,平台将根据当前客户端的能力接受或拒绝查询。
保护用户隐私被认为是一件麻烦事,这肯定是 web 平台的一个限制因素,但我相信所谓的 API 设计隐私正在逐渐成为一种规范,即使在本机平台上也是如此。
如今,在 MacOS 或 Windows 等其他操作系统中,你会发现类似的保护机制,比如系统范围的麦克风访问权限 UI。

设备延迟

现在我们来谈谈延迟。至少在网络音频方面,Chrome 做得不好是一个棘手的问题。
对于音频制作应用程序,延迟非常重要,至少有两个原因。首先,在录制或监控时,尽可能减少延迟非常重要,但平台准确的延迟报告对于事后补偿音频至关重要。
但对于浏览器来说,这是一个棘手的问题。浏览器需要在许多不同的平台上支持各种配置。这意味着我们正在进行精简,可能缺少一些明显的特定于平台的优化。
当经验丰富的音频开发人员加入 Chrome 的音频基础设施,指出一些问题时,我们总是对此心存感激,而这在过去确实发生过好几次。
此外,网络音频并不是平台上唯一的音频API。WebRTC和媒体元素在Chrome中也与Web audio共享相同的音频基础设施。这使得它很难带来一个只对网络音频有利的大变化。
RTC media通常关注弹性,这意味着更多的缓冲,但 Web Audio 更关注低延迟和交互性,这意味着更少的缓冲。这种冲突使得应用只对网络音频有利的激进优化变得困难。
目前的状况是怎样的?
对于网络音频,您必须使用 getUserMedia 进行麦克风输入,输出只需进入系统默认的音频设备。
但是如果你想使用默认设备以外的音频设备呢?唯一已知的解决方案是使用音频元素。通过将网络音频输出流式传输到所选设备的音频元素。
在这里,流通常意味着在下面的某个地方有一些缓冲。这对延迟不好。
我们能做些什么?音频工作组目前正在努力创建一个新的API,允许您为音频上下文选择音频输出设备。理论上,这将保证代码路径最小化输出延迟。此外,人们还梦想为输入设备选择创建一个新的API。

总结

我们讨论了 Web Audio API 的设计和体系结构,还介绍了Chrome 的 Web Audio perf toolkit,还讨论了设备访问和延迟方面的问题。
附上演讲视频:
推荐阅读:
继续阅读
阅读原文