对于普通用户而言,互联网上的视频编解码问题已基本得到解决。如今,大多数消费类设备都配备了专用的硬件加速芯片,Vulkan® 视频扩展等 API 可直接调用这些芯片。与此同时,新一代编解码器越来越多地采用免版税的开放规范,或者单纯因授权期限届满而不再受限,使得这些标准对所有人开放。
人们很容易忘记,就在 18 年前,720p H.264 解码对 CPU 而言是多么吃力的任务。这一挑战曾引发软件实现方案间的激烈竞争与优化,将性能推向极限,直至硬件解码最终成为常态。
然而,在专业工作流程中,性能瓶颈依然存在。剪辑师需要处理数天的原始摄像机素材,调色师需要处理 8K 16 位母版,视觉特效艺术家需要渲染 32 位浮点 ACEScg 视频,档案管理员需要处理超高分辨率的无损胶片扫描,这些都仍然受到性能的限制。过去,普通用户或许还能容忍偶尔的掉帧,但如今,专业人士往往不得不选择昂贵的专有解决方案,或是配备数百GB内存、采用液冷散热的百核工作站。
本文将探讨 FFmpeg 如何利用 Vulkan Compute,在消费级 GPU 上无缝加速专业级视频的编码和解码,无需专用硬件即可大规模释放 GPU 并行计算能力。这种方法是对 Vulkan Video 固定功能编解码器支持的补充,将加速范围扩展到其未涵盖的格式和工作流程。
编解码器
编解码器是一类利用信号中的冗余和模式来对其进行压缩,以便存储或传输的算法。在 GPU 上将编解码器处理并行化有多容易?
以 JPEG 为例,它是压缩编解码器中的“C. elegans”,用以说明这一问题。图像编码需要进行二维频域变换(部分可并行化,先处理行再处理列)、直流值预测(完全串行)、量化以丢弃感知上无关的信息(完全并行),最后是哈夫曼编码(极度串行)。并行与串行步骤的混合,正是 GPU 编解码器加速面临的核心挑战。

解码过程则反向执行这些步骤,但串行瓶颈依然是个棘手的问题。
这就是根本性的矛盾:编解码管道中布满了串行依赖关系,而 GPU 则是专为同时执行数千个独立且互不关联的操作而设计的。
折中方案
历史上显而易见的方法是混合解码:在 CPU 上处理串行步骤(如系数解码),将中间结果上传至 GPU,然后让 GPU 运行其擅长的并行步骤。
但在实际应用中,这种方法会遇到一个根本性问题:GPU 与系统内存物理上相距甚远。即使借助 DMA 和高带宽传输,往返延迟往往导致混合解码比直接在 CPU 上执行并行步骤还要慢,尤其是考虑到现代支持 SIMD 的 CPU 已经变得多么强大。
混合编解码器实现的实际测试结果证实了这一点。dav1d 解码器曾尝试将其最后一个滤波器处理阶段(虽然复杂但高度可并行化)卸载至 GPU,但即使在移动设备上,其性能也未见提升。x264 虽添加了基础的OpenCL™支持,但帧上传延迟抵消了所有性能优势,相关代码最终也因缺乏维护而废弃。
这些失败使混合实现方案在多媒体社区中声名狼藉。教训很明确:要实现持续的高速、可维护性和广泛采用,基于计算的编解码器实现必须完全驻留于GPU,绝不与 CPU 进行数据交接。
有志者事竟成……
大多数编解码器在设计时都考虑到了 ASIC 硬件,即现代 GPU 上专用的视频引擎,这些引擎通过 Vulkan Video 对外提供接口。但即便是 ASIC 也并非无限快:编解码器通常会做出折中,定义一个可并行处理的最小单位,称为“切片”或“块”,它代表了可以独立处理的最小数据块。
大多数主流编解码器设计于数十年前,当时视频分辨率远低于如今。随着分辨率呈指数级增长,这些固定大小的最小处理单元如今仅占帧的极小部分,这意味着可以并行处理的单元数量大幅增加。现代 GPU 还具备了支持跨调用通信的功能,这为进一步优化提供了可能。
综合这些趋势,如今完全可以在计算着色器中实现某些编解码器——无需 CPU 参与。
基于计算的编码器还拥有一个容易被忽视的优势:它们在内存使用和搜索时间上不受限制。只要有足够的线程对每个块进行穷举扫描,完全可以达到甚至超越软件编码器的质量。
可访问性
FFmpeg 是一套免费的开源库和工具集合,支持处理各种格式和编解码器的多媒体流。虽然 FFmpeg 以其跨平台的、经过手工汇编优化的编解码器实现而闻名,但它同时也提供了便捷的硬件加速访问方式。
关键在于,FFmpeg 中的硬件加速是构建在软件编解码器之上的。头信息解析、线程管理、帧调度、切片处理以及错误校正/处理均在软件中完成。唯一被卸载到硬件端的仅是视频数据的解码过程。这种设计将经过充分测试的健壮代码与硬件加速相结合。我们可以直接将软件实现中独立帧的线程管理机制,转化为通过调度多个帧进行并行解码,从而充分利用 GPU 的处理能力。
此外,用户还可通过切换开关在软件和硬件实现之间动态切换,无论硬件解码是基于 Vulkan Video 还是 Vulkan Compute 着色器实现,均无差异。
FFmpeg 在编辑软件、媒体播放器和浏览器中的广泛应用,加上其能够为任何软件实现添加硬件加速支持的能力,使其成为推广基于计算的编解码器实现的理想起点,而非依赖专用库实现。
FFv1
FFmpeg 视频编解码器第 1 版(FFv1)已成为档案管理领域以及需要无损压缩的应用程序中的标配。它开源、免版税,并且是 IETF 的官方标准。
FFmpeg 中将编解码器实现于计算着色器的相关工作始于此。尽管 FFv1 编解码器支持多达 1024 个切片,但在 CPU 上运行时速度非常缓慢。这部分归因于高分辨率 RGB 视频所需的巨大带宽,以及熵编码设计中存在的瓶颈。
FFv1 第 3 版的设计已逾 10 年,得益于档案保存社区的采用,该标准才得以广泛应用。然而,这些瓶颈使得高分辨率档案影片扫描的编解码过程耗时过长,难以承受。
因此,得益于档案保护社区的支持,FFv1 编解码器应运而生。它们最初是基于软件编解码器移植而来,但随着时间的推移,通过针对 GPU 的专用函数进行了日益深入的优化。
FFv1编码的最大挑战在于其范围编码器系统,该系统缺乏AV1范围编码器所具备的优化。每个符号(像素差值)的每个比特都有其自身的8位自适应值,因此在编码或解码时,需要从数千个值中随机查找32个连续值(每个平面都需要查找!)。我们通过将工作组大小设置为32来加速这一过程,每个本地调用并行地查找并执行自适应操作,而单个调用则执行实际的编码或解码。

对于 RGB,我们执行可逆颜色变换(RCT)以进一步降低像素值的相关性。最初,我们使用了一个单独的着色器来实现这一点,并将其编码到单独的图像中。然而,对于非常高分辨率的图像,这样做所需的带宽超过了其优势。由于解码或编码图像只需要 2 行,因此我们分配 width*horizontal_slices*2 个图像,并在 32 个辅助调用的帮助下,在对每一行进行编码之前执行 RCT。
APV
APV 是由三星设计的一种新型编解码器,旨在作为中间层视频压缩的免版税、开源替代方案。最近,它也已成为 IETF 标准。该技术正在视觉特效(VFX)和专业媒体制作领域获得广泛应用,同时也作为智能手机的摄像机录制格式被采用。
本文中提到的大多数编解码器不同,APV 从一开始就针对并行处理进行了设计。与 JPEG 类似,每一帧都被细分为多个分量,每个分量又被细分为多个图块,每个图块包含多个块。每个块都经过简单的变换,通过标量量化器(简单除法)进行量化,并使用可变长度编码进行编码。甚至不需要任何直流分量预测。
为了将其实现为计算着色器,我们首先在一个着色器中处理每个图块的解码,然后运行第二个着色器,该着色器在每次调用时处理单个块的一行。

ProRes
ProRes 是事实上的中间格式编解码器标准,广泛应用于剪辑、摄像机素材处理及母版制作。它是一种相对简单的编解码器,与 JPEG 和 APV 类似,这使得我们能够实现解码器,并应广大用户需求,开发了编码器。
在解码方面,我们基本上采用与 APV 相同的流程。但在编码方面,我们会通过运行着色器来寻找能使块数据在帧位预算内适配的量化器,从而实现恰当的速率控制和估计。
遗憾的是,与列表中的其他编解码器不同,ProRes 编解码器既非免版税,也没有公开的规范。FFmpeg 中的实现属于非官方版本。但鉴于其极高的普及度,为了与专业领域的大部分系统实现互操作,此类实现是必要的。尽管如此,开发者们仍会亲自测试这些实现,并监控其输出以确保与官方实现一致。
ProRes RAW
ProRes RAW 的比特流与 ProRes 几乎没有共同之处,因为它专为压缩原始(未经去拜耳化)有损传感器数据而设计。它对每个分量执行 DCT 变换,并使用系数编码器预测各分量的直流分量,然后以正常的锯齿形顺序高效地编码来自多个分量的交流分量值。其熵编码系统并非传统的变长编码,而是更接近于指数编码。

切片包含多个块,每个分量都可以并行解码。与 FFv1 不同,ProRes RAW 对每幅图像的图块数量没有限制,这意味着可能需要解码数十万个独立的块。这非常有利于并行处理,从而实现高效的解码。
解码器采用两遍渲染的方式实现:第一遍着色器解码每个图块,第二遍着色器使用行/列并行方式转换每个图块内的所有块(由于能够充分利用 GPU 的工作组大小限制,因此被称为“碎块配置”)。
DPX
DPX 不是一种编解码器,而是一个带有头部信息的像素打包容器。它是 SMPTE 的官方标准,在胶片扫描仪中非常流行。它并非以最优布局和紧密打包像素为目标,而是以 32 位块的形式打包像素,必要时进行填充。或者,它也可以不打包像素,这取决于头部信息的设置。
由于它是一种未压缩格式,且规范宽松,制定于几十年前,这意味着许多厂商会以各种“创造性”的方式来解读规范,从而导致解码完全失败。幸运的是,头部信息中保留了一个文本“制作人”字段,供这些实现者用来标记他们的“艺术”,以便确定如何正确解包而不会出现乱码。
所有这些最终都归结为在着色器中编写启发式算法。开销永远不会是查找像素集合所需的计算,而是实际从内存中读取数据并将其写入其他位置的过程。

VC-2
VC-2 是另一种中间编解码器。它由 BBC 开发,基于其 Dirac 编解码器,免版税,并符合 SMPTE 官方规范。其主要用途是实时流媒体传输,尤其适用于在千兆连接上以亚帧延迟传输高分辨率视频。与 APV 或 ProRes 不同,VC-2 基于小波变换。每一帧都被细分为大小为 2 的幂次方的小片。
小波变换非常有趣。它将一帧细分为一个四分之一分辨率的图像,以及三个作为残差的四分之一分辨率图像。与 DCT 不同,小波变换具有高度局部性,这意味着它可以对每个小片单独进行变换,但组合起来后,其效果就像对整个帧进行了变换一样。这消除了所有基于 DCT 的编解码器都会遇到的块效应。
这也意味着它们的编码效率较低,因为它们的频率分解能力受到了影响。此外,它们的失真特性也远不如 DCT 的模糊效果那样美观。这是它们在2000年后的编解码器中未能获得广泛应用的主要原因之一。
由此产生的系数通过简单的交错式Golomb-exp码进行编码,虽然这种编码方式不可并行化,但可以在解码器中进行巧妙的简化,从而消除所有位解析,转而直接对整个字节进行操作。
JPEG
开头举例的编解码器实际上存在一个非常有趣的攻击,它不仅为并行化打开了大门,而且还为并行化任意数据压缩标准(例如 DEFLATE)打开了大门。
其原理是,尽管 VLC 流本身无法并行化,但 VLC 解码器(实际上所有满足 Kraft-McMillan 不等式的编解码器)可以进行虚假的重新同步。在一段出乎意料的短暂延迟后,VLC 解码器往往会输出有效数据。
所需的仅仅是运行 4 个着色器来逐步同步每个 JPEG 流中的起始点。JPEG 也有多种变体,例如渐进式和无损配置文件,它们也可以进行类似的并行化。
DC 预测可以通过并行前缀和来实现,这是计算着色器中最常用的操作之一。与其他编解码器一样,DCT 可以通过 shred 配置来实现。
未来
随着 FFmpeg 8.1 的发布,我们实现了 FFv1 编码和解码、ProRes 编码和解码、ProRes RAW 解码以及 DPX 解包。如果启用了 Vulkan 加速解码,则会自动启用并使用基于 GPU 的处理。
VC-2 编码器和解码器以及 JPEG 和 APV 解码器仍在开发中,在合并之前还需要进行一些额外的工作。
展望未来,目前只有 JPEG2000 和 PNG 编解码器具有显著的 GPU 加速潜力——其余编解码器要么实际应用场景有限,要么无法从基于计算的加速中获益。
遗憾的是,JPEG2000(以及其衍生的 JPEG2000HT)与大多数现代编解码器不同,它融合了多种编解码器最糟糕的特性:半串行化的编码系统需要大量的领域知识,并且比特流复杂到足以让大多数现代机构望而却步。由于其以ASIC芯片为中心的设计以及计算编码器设计不足,JPEG2000的软件解码速度在所有广泛使用的编解码器中名列前茅。尽管如此,它仍然是数字电影、医疗和法医领域的主要编解码器。
PNG 加速仍是一个悬而未决的问题:它作为GPU目标的可行性将取决于DEFLATE并行化的效率。
Vulkan 计算
Vulkan 常被简单地归类为带有计算功能的图形 API,但这种说法已经过时了。它的计算能力已经发展到足以媲美,甚至在某些方面超越了专用的计算 API。现代 Vulkan 提供指针、丰富的子组操作、共享内存别名、原生位运算、定义完善的内存模型、着色器特化、64 位寻址以及对 GPU 矩阵单元的直接访问。这些特性使得程序员能够在比抽象程度更高的 API 更低的层面上进行优化。
即便如此,Vulkan 计算 API 的潜力尚未完全发挥,因为它尚未完全展现 SPIR-V™ 的全部功能。SPIR-V™ 作为一种中间表示形式,其表达能力非常出色。对更广泛的 SPIR-V 特性集的支持正在积极扩展——无类型指针和 64 位寻址已经可用,对非 32 位整数类型的位运算支持也即将推出。
来自 GPU 厂商的竞争性计算 API 通常捆绑了数百种专门优化的算法实现,并可通过更易用的编程语言访问,这无疑极具诱惑力。当然,问题在于厂商锁定,这对于像 FFmpeg 这样可移植且长期使用的软件来说可能是一个严重的问题。
FFmpeg 为了避免依赖性,可能会编写自己对常用算法的实现,例如哈希函数、排序算法、CRC 校验或频率变换。但另一方面,如此庞大的面向对象 API 真的有必要吗?通常,格式化数据以供通用实现使用,耗时更长,而且生成的代码效率也低于直接编写一个针对特定用例的小型算法实现。很多情况下,面向对象编程可以通过预处理器进行模板化来实现。链接多个代码片段可能只需要一个 `#include` 指令即可。此外,那些针对特定供应商 API 版本(而该 API 又依赖于特定旧版本的 gcc)的脆弱代码,可以被可靠、持久且自包含的着色器所取代。
Vulkan 应用广泛,从小型 SoC 到平板电脑、嵌入式 GPU、独立 GPU 和专业服务器 GPU,其行业主导的治理模式为广泛支持新扩展提供了强有力的激励。持续的自动化测试通过全面的一致性测试套件进行。最后,Vulkan 拥有丰富的调试、优化和性能分析工具生态系统,庞大的全球开发者社区意味着,你发现的几乎任何 GPU 特性或优化技巧都已被发现、记录并反馈到规范中。
无论是使用 Vulkan 视频着色器还是 Vulkan 计算着色器,Vulkan 都已成为访问 GPU 加速视频处理的强大 API。
作者:Lynne,Khronos 成员,FFmpeg Vulkan 维护者
原文:https://www.khronos.org/blog/video-encoding-and-decoding-with-vulkan-compute-shaders-in-ffmpeg
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/65620.html