来源:W3C/SMPTE Joint Workshop on Professional Media Production on the Web 主讲人:James Pearce (Grass Valley)、Junyue Cao (ByteDance) 翻译:冯冬辉 演讲 1 中,James 主要演示了其视频编辑器,并详细介绍了 Web 端实现所需的支撑技术,包括 Webcodecs 音视频解码、Web Worker 和 WebGL 渲染。演讲 2 中,Junyue 主要介绍了其视频编辑器的技术框架,以及遇到的非常深入的技术问题,包括 Webcodecs 性能、WASM 调试和 EMScripten 文件系统性能。
W3C: 开发专业媒体制作应用 (1)
W3C: 开发专业媒体制作应用 (2)
W3C: 开发专业媒体制作应用 (3)
W3C:开发专业媒体制作应用(4)
W3C: 开发专业媒体制作应用 (5)
目录
- 浏览器托管的视频编辑
- 视频编辑演示
- 支撑技术
- 缓冲区管理
- 基于 WebAssembly 的非线性视频编辑器
- 视频编辑器
- 工作原理
- 需求:更好的解码性能
- 需求:更好的调试经验
- 需求:更好的文件访问
浏览器托管的视频编辑
视频编辑演示
James Pearce 的浏览器端视频编辑器
James Pearce 首先展示了基于 web 的视频编辑器。它遵循了编辑应用程序的普通的三窗口布局。在左上角有一个源视频查看器,用于加载视频源,然后将它们剪辑并添加到时间线中。在底部有一个时间轴,用以展示了各种轨迹,以及这些轨迹中的片段。在右上角有一个序列播放器,它可以播放正在构建的时间轴。最左边是所有视频源的列表,可以找到一个源,并将其加载到源查看器中,或者直接将其拖放到时间线中。
编辑器的功能非常全面,允许有任意数量的音轨,任意数量的视频片段。此外还可以添加特效,目前已经实现了一系列的特效,未来还会实现更多。在转场特效方面,可以实现模糊,或是简单的圆擦除,并将其添加到时间轴上。
该工具在回放方面有相当高的性能。可以在时间轴上任意移动,并将剪辑、转场以及所有的效果渲染到序列播放器中。允许以不同的速度播放,或者反向播放。也可以一帧一帧地移动,以寻找演讲中的某一关键点。
支撑技术
该编辑器正在使用 Webcodecs 来解码 h264 和 AAC。在 WebCodecs 之前,讲者使用的是 WebAssembly,所以建立了自己的解码器,用 WebAssembly 编译了这些解码器,并在 Web Worker 中使用这些解码器对码流进行解码、缓冲,然后将其用于在播放器中需要的快速、随机访问。得益于构建播放引擎的方式,讲者能够非常迅速地从 WebAssembly 解码器切换到 WebCodec 解码器,性能和功耗都有了明显的改善。在 WebCodecs 不可用的情况下,仍然使用 WebAssembly 来执行所需的 MP4 文件的解析,以去除基本流的复用。
在大多数情况下,可以在一个 Worker 中执行整个解码和渲染管道。在 OffscreenCanvas 可用的地方,可以在一个 Worker 中完成整个端到端的视频解码和视频渲染。而音频就有点问题了,因为 Web Audio API 的大部分 API 都与主 UI 线程相连,这带来了一些问题。例如开始滚动网页里的列表,会给 UI 线程带来很大的负荷。为避免音频不能及时被解码,会尽可能多地进行缓冲,以便播放不会受到影响。在未来,笔者希望看到一个更好的解决方案,也许会将 Web Audio API 推到一个后台 Worker 上。
该编辑器也在使用 WebGL 进行合成、过渡、特效以及任何涉及到将视频渲染到屏幕上的东西。WebGL 的好处在于,它的着色器语言是标准的,因此可以与自建的渲染引擎共享着色器代码。这意味着,当最终的时间线被渲染成高分辨率的形式时,渲染引擎能够使用相同的着色器来产生与我们在这个低质量的、代理的、浏览质量的媒体中看到的相同的结果。
缓冲区管理
在问答环节,James 介绍了如何管理内存缓冲区。保留的缓冲区往往以当前播放位置为中心,鉴于用户正在做什么,来决定缓冲什么以及缓冲多长时间。如果用户以 1x 的速度向前播放,将主要缓冲在当前光标位置之前的帧,且将更积极地丢弃已经播放过的、在当前光标位置之后的缓冲。如果是向后播放,那么情况恰好相反。
此外,总是在光标两侧缓冲至少几帧或是一两秒,因为无法预测用户不会停止播放并突然倒退,所以确实需要能够快速改变方向。显然,也不能缓冲大量的内容,如果用户在时间轴上跳到另一个位置,那就会使缓冲的所有内容失效,必须重新获取。
附上演讲视频:http://mpvideo.qpic.cn/0bc37yabiaaaoyaffdtslvrfb7wdct7aafaa.f10002.mp4?dis_k=f2a9a60d91967c8b8ab4c1fbd8ec499a&dis_t=1653388300&vid=wxv_2383439372039061505&format_id=10002&support_redirect=0&mmversion=false
基于 WebAssembly 的非线性视频编辑器
视频编辑器
Junyue CaoJ 的浏览器端视频编辑器
视频编辑对于每个视频内容创作者来说都是非常重要的。这是视频创作的最后步骤之一。它把你的媒体剪辑变成一个完整的故事。有很多视频编辑软件,包括桌面应用程序和移动应用程序,或者一些基于云的软件。
Junyue 正在构建的是一个基于 web 的多轨视频编辑器。用户可以使用网页浏览器添加视频剪辑、音频剪辑、字幕、过渡和特殊效果。通过利用 web 技术和基于云存储的优势,用户可以在任何时候用任何计算机打开项目并继续工作。
工作原理
起初,项目的核心引擎代码是用 C 为本地平台编写的。它是一个多线程引擎,运行在安卓,iOS,Windows 和 MacOS 上。随着 WebAssembly 浏览器支持的改进,现在有机会将引擎迁移到 web 上。项目使用 WebGL 和其他技术实现实时视频渲染。
项目的简单结构
上图展示了应用程序的最简单结构。项目为网页提供 JS api 来控制引擎。JS api 调用用 EMScripten 编译的 C 引擎。C 引擎调用 EMScripten 提供的浏览器特性 api,使用所有的特性,如用于视频和特效渲染的 WebGL、用于音频播放的 WebAudio、用于多线程运行时的 Web Workers 和用于文件系统持久性的 IndexedDB。
项目调用浏览器特性的过程
上图用于说明在处理路径中使用哪些浏览器特性的最简单过程。首先,视频部分。对于每个视频首先使用 WebAssembly 对视频帧进行解码。获取解码后的视频后,将其转换为纹理,然后混合多个视频轨道的纹理,并通过 WebGL 将其显示在画布上。对于音频部分,使用 WebAssembly 进行解码,然后将解码后的数据发送到 Web Audio API 进行回放。当然,对于任何类型的视频播放器,音频和视频同步都是必不可少的。这里是最简单的处理路线,省略了许多步骤,实际情况比图中显示的要复杂得多。
需求:更好的解码性能
到目前为止,整个应用程序基本工作,但在某些方面,事情并不令人满意。第一个问题是解码性能。
视频解码需要同时解码多个视频曲目,这对视频解码的性能提出了很大的挑战。使用 WebAssembly 进行解码将占用大量 CPU 资源,而且速度也不如原生的快。目前,为了支持尽可能多的视频轨道,不得不将视频分辨率限制在 480p。随着 Webcodec 的引入,将有可能提高视频分辨率。要在 Web Worker 中使用 Webcodecs,提供同步 Webcodecs API 对于 C 代码的集成将更加友好。
需求:更好的调试经验
另一个问题是项目需要更好的调试经验。
启用 DWARF 调试模式时,WASM 文件将变得非常大。在讲者的例子中,它超过了 1 GB,这使得浏览器非常不稳定。浏览器在运行一段时间后很容易崩溃,而且速度很慢。即使在本地主机上进行调试,加载 DWARF 信息仍然需要超过 10 秒的时间。而开发工具往往反应迟钝并且卡住。
其次,在多线程应用程序中,一个有意义的辅助线程线程名称对于调试非常有用。当前的 Web Worker 只支持在创建时指定其名称。我们希望在使用 Web Worker 时可以重命名它,这对于查找所需的线程非常有帮助。
第三,需要更好的批量暂停线程。目前,开发工具只能选择一个线程并单击暂停按钮。它将暂停工作线程或主线程。但是当有很多线程时,开发人员必须一个接一个地点击暂停按钮或继续按钮。在这方面,讲者认为可以参考流行的 IDE 的调试习惯。
需求:更好的文件访问
如图所示,EMScripten 提供了一个支持直接使用本地 POSIX 文件 api 的文件系统。在浏览器上,它主要包括 MEMFS 和 IDBFS。
然而,视频文件通常很大。当使用 MEMFS 读取大型文件时,内存消耗将非常大,因为它将整个文件加载到内存中,这非常容易导致内存不足错误。同时,多线程访问总是代理主线程,这会影响主线程的性能。
因此,讲者认为需要一个支持更好的多线程访问、更少的内存消耗和更好的持久性解决方案的文件系统。
附上演讲视频:http://mpvideo.qpic.cn/0bc3vqabiaaataafhrtsl5rfblgdcswaafaa.f10002.mp4?dis_k=b2a108055263de9a39d8f3adc4969335&dis_t=1653388300&vid=wxv_2383442258005426177&format_id=10002&support_redirect=0&mmversion=false