从 FFmpeg 性能加速到端云一体媒体系统优化

简介: 7 月 31 日,阿里云视频云受邀参加由开放原子开源基金会、Linux 基金会亚太区、开源中国共同举办的全球开源技术峰会 GOTC 2021 ,在大会的音视频性能优化专场上,分享了开源 FFmpeg 在性能加速方面的实战经验以及端云一体媒体系统建设与优化。

众所周知,FFmpeg 作为开源音视频处理的瑞士军刀,以其开源免费、功能强大、方便易用的特点而十分流行。音视频处理的高计算复杂度使得性能加速成为 FFmpeg 开发永恒的主题。阿里云视频云媒体处理系统广泛借鉴了开源 FFmpeg 在性能加速方面的经验,同时根据自身产品和架构进行了端云一体媒体系统架构设计与优化,打造出高性能、高画质、低延时的端云协同的实时媒体服务。

image.png

阿里云智能视频云高级技术专家李忠,目前负责阿里云视频云 RTC 云端媒体处理服务以及端云一体的媒体处理性能优化,是 FFmpeg 官方代码的维护者及技术委员会委员,参与过多个音视频开源软件的开发。

本次分享的议题是《从 FFmpeg 性能加速到端云一体媒体系统优化》,主要介绍三个方面的内容:

一、FFmpeg 中常见的性能加速方法
二、云端媒体处理系统
三、端 + 云的协同媒体处理系统

FFmpeg 中常见的性能加速方法

音视频开发者当前主要面临的挑战之一是对计算量的高需求,它不只是单任务算法优化,还涉及到很多硬件、软件、调度、业务层,以及不同业务场景的挑战。其次端、云、不同设备、不同网络,这些综合的复杂性现状,要求开发者要做系统性的架构开发与优化。

FFmpeg 是一个非常强大的软件,包括音视频的解码编码、各种音视频的 Filter 、各种协议的支持。作为一个开源软件 FFmpeg 主要的 License 是 GPL 或者 LGPL,编程语言是 C 和汇编。它还提供了很多打开即用的命令行工具,比如转码的 ffmpeg 、音视频解析的 ffprobe、播放器 ffplayer。其核心的 Library 有负责编解码的 libavcodec、处理音视频的 libavfilter 、支持各种协议的 libavformat。

image.png

FFmpeg 开发社区中,音视频性能优化是一个永恒的主题,其开源代码里也提供了非常多的经典性能优化的实现方法,主要是通用加速、CPU 指令加速、GPU 硬件加速。

image.png

通用加速

通用加速主要是算法优化、IO 读写优化、多线程优化。算法优化的目标是在不增加 CPU Usage 的前提下提高性能。最典型是编解码器的各种快速搜索算法,它可以在精度只有少数损失的情况下,大幅优化编码速度。各种前后处理 Filter 的算法也有类似的方法,也可以通过算法合并来减少冗余的计算,达到性能优化的目的。

下图是一种典型的降噪与锐化卷积模板,它需要做 3 乘 3 的矩阵卷积。我们可以看到,这里的锐化模板跟平滑模板是类似的,对于同时要做降噪和锐化的操作,只要在平滑的模板的基础上面做减法可以达到锐化化模板的结果,可以减少冗余的计算,提升性能。当然算法优化也存在局限性,比如编解码算法有精度上的损失,还可能需要牺牲空间复杂度换取时间复杂度。

image.png

第二种性能优化的方法是 IO 读写优化,常见方法是利用 CPU 预读取来改善 Cache Miss 。上图是通过两种读写的方法达到相同的运算结果,按行读写的方法比按列读写快,主要原因是 CPU 在按行读写时可做预读取,在处理当前像素的时候,CPU 已经预读本行的其他像素,可以大大加速 IO 读写的速度,提高性能。另外可以尽量减少 Memory Copy,因为视频处理 YUV 非常大,每读一帧对性能的损耗比较大。

image.png

通用加速的多线程优化主要是利用 CPU 多核做多线程的并行加速来大幅提升性能。上图中左下角的图表表明随着线程数的增加,性能提升了 8 倍。随之也会产生一个问题,普遍被使用的多线程加速,线程数是不是越多越好呢?

答案是 No 。

首先,因为 CPU 核数限制,多线程等待和调度,多线程优化会碰到性能瓶颈。以左下角的图表为例,线程数等于 10 的加速比跟线程数等于 11 时非常接近,可以看出多线程优化存在边际效应的递减,同时也会带来延时和内存消耗的增加(尤其是普遍使用的帧间多线程)。

第二,帧间(Frame level)多线程需要建立 Frame Buffer Pool 进行多线程并行,这需要缓冲很多帧,对于延迟非常敏感的媒体处理,比如低延时直播、RTC ,会带来比较大的负面效应。与之对应的是,FFmpeg 支持帧内(Slice level)的多线程,可以把一帧划分成多个 Slice,然后做并行处理,这样能够有效避免帧间的 Buffer 延迟。

第三,当线程数增多,线程同步和调度的成本也会增加。以右下图的一个 FFmpeg Filter 加速为例,随着线程数的增加,CPU Cost 也在明显增加, 且图表曲线末端开始往上倾斜,这表明其成本的增加变得更明显。

CPU 指令加速

CPU 指令加速即 SIMD(单指令多数据流)指令加速。传统通用的寄存器和指令,一条指令处理一个元素。但一条 SIMD 指令可以处理一个数组中的多个元素,从而达到非常显著的加速效果。

现在主流的 CPU 架构都有对应的 SIMD 指令集。X86 架构 SIMD 指令包括 MMX 指令、SSE 指令、AVX2、AVX-512 指令。其中 AVX-512 的一条指令可处理 512 个 bits,加速效果非常明显。

image.png

FFmpeg 社区的 SIMD 指令写法包括内联汇编、手写汇编。FFmpeg 社区不允许使用 intrinsic 编程,原因是它对编译器的版本存在依赖,不同编译器编译出的代码及其加速效果也是不一致的。

虽然 SIMD 指令有好的加速效果,但它也存在一定的局限性。

首先,很多的算法不是并行处理的,不能进行 SIMD 指令优化。

其次,编程难度比较大。汇编编程的难度就要大些,另外 SIMD 指令对编程有一些特殊的要求,比如 Memory 对齐。AVX-512 要求 Memory 最好能够做到 64 字节对齐,如果没有对齐的话,可能会有性能上的损耗,甚至会引起程序的 Crash。

我们看到不同的 CPU 厂商都在做指令集竞赛,支持的位宽越来越多,从 SSE 到 AVX2 再到 AVX-512 ,位宽显著增加。那位宽是不是越宽越好?上图可以看到 X265 编码 AVX 512 相对 AVX 2 的提速情况,AVX 512 的位宽是 AVX 2 位宽的两倍,但性能提升实际上往往远达不到一倍,甚至达不到 10%。在某些情况下,AVX 512 的性能会比 AVX 2 还要低。

原因是什么呢?

首先,一次性的数据输入可能并没有 512 bits 这么多,可能只有 128 bits 或者是 256 bits。

第二,有很多的复杂运算步骤(如编码器)不能做指令集的并行。

第三,AVX 512 功耗较高,会引起 CPU 的降频,导致 CPU 整体处理的速度会有所下降。我们看上图的 X265 ultrafast 档次编码,AVX 512 的编码比 AVX 2 还要慢。(详见:https://networkbuilders.intel.com/docs/accelerating-x265-the-hevc-encoder-with-intel-advanced-vector-extensions-512.pdf

硬件加速

FFmpeg 比较主流的硬件加速是 GPU 加速。硬件加速接口分为两大块,一是硬件厂商提供不同的加速接口。英特尔主要提供 QSV 和 VAAPI 的接口,英伟达提供 NVENC、CUVID、NVDEC 、VDPAU,AMD 提供 AMF 和 VAAPI 的接口。二是不同的 OS 厂商提供不同的加速接口和方案,比如 Windows 的 DXVA2,安卓的 MediaCodec ,苹果的 VideoToolbox 。

image.png

硬件加速可以显著提升媒体处理的性能,但是它也会带来一些问题。

第一,硬件的编码质量受限于硬件的设计及成本,硬件编码的质量往往是会比软件编码质量差。但硬件编码有非常明显的性能优势,可以用性能去换编码质量。下图的例子可看出,硬件编码器运动搜索的窗口比较小,导致编码质量的下降。其解决方法是 HME 算法,在搜索窗口比较小的情况下,先把比较大的 Picture 缩放到非常小的 Picture,在这个 Picture 上做运动搜索,再逐级做搜索和放大,这样可以显著提高运动搜索的范围,然后找到最匹配的块,从而改善编码质量。

image.png

第二,硬件加速的 CPU 和 GPU 的 Memory Copy 性能交互会带来性能的下降。

image.png

CPU 和 GPU 的交互不仅仅是简单的数据搬移过程,实际上要做对应的像素的格式转换,比如 CPU 是 I420 linear 格式,GPU 擅长矩阵运算,采用 NV12 Tiled 格式。

这种内存格式的转化,会带来明显的性能损耗。可以通过构建纯硬件的 Pipeline ,有效的规避 CPU/GPU Memory 交互的问题。当 CPU 和 GPU 必须交互的情况下,可以采取 Fast Memory Copy 的方式,采用 GPU 去做 Memory Copy ,加速这一过程。

下图是性能优化的总结大图。除了前面提到一些优化方法外,客户端上的媒体处理还有一些特殊性,比如手机 CPU 是大小核架构,线程调度如果调度到小核上,它的性能会明显比大核要差,导致性能的不稳定。

image.png

另外很多的算法不管如何优化,在某些机型上就是无法跑通,这时要在业务策略上面做优化,比如制定黑白名单,不在支持名单的机器就不开启该算法。

云端媒体处理系统优化

对于媒体处理来说分为两大挑战,一是云端的成本优化,二是客户端设备适配及兼容。下图是云端媒体处理的典型系统:包括单机层、集群调度层、业务层。

image.png

单机层包括 FFmpeg Pipeline 处理框架、编解码、硬件层。以云端转码系统为例,它的核心技术指标包括画质、处理速度、延时、成本。画质方面,阿里云视频云独创了窄带高清的技术以及 S265 编码技术,可以显著改善编码画质。处理速度和延时优化方面,我们广泛借鉴 FFmpeg 性能加速方法,比如 SIMD 指令、多线程加速以及异构计算的支持。成本是一个比较复杂的系统,它会包括调度层、单机层、业务层,需要进行快速的弹性扩缩容,单机资源精确画像,减少单任务的计算成本。

云端成本优化

云端成本优化的核心是围绕三条曲线去做优化,针对单任务实际资源消耗、单任务资源预估分配、总资源池这三条曲线分别做对应的优化。在优化过程中需要面对四个现实的问题:

第一,在视频云的业务里,业务多样性的趋势会越来越明显,业务包括点播、直播、RTC、 AI 编辑部、云剪辑。业务多样性带来的挑战是如何将多种业务共用一个资源池做混跑。

第二,大颗粒的任务越来越多,几年前主流视频是 480P,而现在的主流是 720P、1080P 的处理任务,未来可以预见 4K、8K、VR 这样的媒体处理会越来越越多,这带来挑战是对单机性能的渴求会越来越大。

第三,调度层需要预估每个任务的资源消耗,但单机任务的实际消耗会受到非常多因素的影响。视频内容的复杂度,不同算法参数,多进程的切换,都会影响任务资源消耗。

第四,编码的前处理会越来越多,同时一个转码任务需要做多码率或者多分辨的输出。各种前处理(画质增强 / ROI 识别 / 超帧率 / 超分等)都会大幅增加的处理成本。

我们看下从整体思路来看怎么去优化这三条曲线。

image.png

实际任务的资源消耗优化,主要方法是每个任务的性能优化、算法的性能优化、Pipeline 架构优化。

资源的分配核心目标就是使上图的黄色曲线能够不断地贴近黑色曲线,减少资源分配的浪费。当资源分配不足的情况下,可以做算法的自动升降级,以免线上任务出现卡顿,比如编码器的 preset 从 medium 档降低为 fast 档。

对于总资源池优化,首先可以看到黄色曲线有波峰波谷,如果当前点播任务处在波谷的状态,可以把直播的任务调过来,这样可以在整个池子的峰值没有变化的情况下跑更多的任务。第二,总资源池怎么样能够快速的弹性,在一定时间窗口内能够快速释放掉资源,降低资源池消耗,这也是调度需要考虑的成本优化的核心。

下面展开谈下一些优化方法。

CPU 指令加速

image.png

CPU 机型优化的主要目标是增加 CPU 单机的吞吐量,减少 CPU 碎片。上图描述了多核 CPU 带来的优势。但多核 CPU 也可能会带来多个 NUMA node 直接的内存访问,从而导致性能下降。

单机资源精确画像

单机资源精确画像主要目标是能够精确知道每个任务它需要多少资源,它是一个系统性的工具,需要有画质评估的工具、计算资源统计的工具、需要包括各种多场景复杂的视频集、以及能够去做各种计算资源和迭代反馈,修正成本计算的消耗,指导算法的自适应升降级。

image.png

1-N 架构优化

一个转码任务可能要输出不同的分辨率和码率,传统的方法是起 N 个独立的一转一的进程。这样的架构显而易见会有一些问题,比如冗余的解码计算和编码前处理。一个优化方法是把这些任务做整合,从 N 到 N 的转码变成一到 N 的转码。这样视频解码和编码前处理都只需要做一次,从而达到成本优化的目标。

image.png

1-N 转码也会带来新的挑战。FFmpeg 转码工具支持 1-N 转码,但是各模块是串行处理的,单个一转 N 任务的速度会比单个一转一的任务慢。第二,调度的挑战,单任务资源颗粒度会更大,所需要资源的分配也更难估计。第三,算法效果的差异,因为有的视频的前处理可能是在 Scale 之后,对于一到 N 的转码架构会把前处理放到 Scale 之前。媒体处理的流程变化会引起算法效果的差别(通常这个问题不是特别大,因为在 Scale 前处理没有画质损失,在 Scale 前做处理反而是更好的)。

端 + 云的协同媒体处理系统

端侧媒体处理的优势是可利用手机端现成的算力来降低成本,所以理想情况是充分利用各种端侧的算力,每个算法都做非常好的性能优化、端侧的适配,在每个端都能零成本的落地。

但是理想很美满,现实很骨感。会有四大现实问题:

第一,端侧适配的困难。需要大量的 OS 硬件机型适配。

第二,算法接入的困难。现实情况下不可能把所有的算法在所有端上都进行优化,所以端侧的性能瓶颈会导致算法落地困难。

第三,体验优化的困难。客户会有不同的 SDK ,或者说阿里云的 SDK 也会有不同的版本,SDK 本身存在碎片化导致一些方案难以落地。比如非标的 H264 编码,实际上 H265 编解码算法的落地也遇到挑战,一些设备并不支持 H265。

第四,用户接入的困难,客户升级 SDK 或替换 SDK 的周期比较漫长。

面对这样的现实,我们提出云和端协同的媒体处理解决方案。主要思路是通过云上处理 + 端侧渲染的方案,达到比较好的用户体验。

image.png

云和端的协同的媒体处理主要类型是转码类和预览类。转码类是单向的数据流。预览类需要把各种流先推到云端,再加各种特效,然后再拉回来给主播看效果是不是符合他的预期,最后再从 CDN 推到观众端。

这样的方案也会碰到一些挑战。首先,云端处理会有计算成本的增加(当然可以有各种方式优化,因为客户端没有直接体感)。第二,延时会增加,云端的处理增加了链路的延时。

随着 RTC 技术越来越成熟,通过 RTC 低延时的传输协议,再加云端的各种成本优化,可以低成本 / 低延时地支持云上的媒体处理,打造一个云加端的实时媒体处理服务。阿里云视频云构建的 RTC 实时媒体处理服务 RMS,可以做到高性能、低成本、高画质、低延时、更智能的云端协同媒体处理的方案。

image.png

上面的左图是 RMS 整体架构图,分为 Pipeline 层、模块层、硬件适配层,硬件层。Pipeline 可以做各种业务场景的组装模块层,模块层是音视频处理的核心,实现各种 AI 或者是低延时高画质的效果。

端加云的协同媒体处理:赋能 RTC+

以剪辑云渲染为例,传统的剪辑方案要保证多端体验一致性及流畅的性能是比较困难的。我们的思路是端上只做指令的下发,视频的合成、渲染都是在云上实现,可以支持非常多的特效,也能够保证多端的效果一致性。

image.png

我们看下剪辑云渲染的 Pipeline。云渲染的网页负责信令下发,通过调度层把剪辑指令转发到 RMS 媒体处理引擎做云上媒体处理的渲染,合成之后再编码通过 SFU 推流,最后在剪辑的网页端看剪辑效果。上图的 Demo 可以看到,网页把很多 Track 合成一个 Track ,4 乘 4 的宫格在端上处理的话,在低端机跑起来是比较费力的,但云能够轻易跑出这样的效果。

高码率低清晰度的视频流到云端后,通过阿里云视频云的云端窄带高清技术处理,可以达到更高清晰度更低码率的目标。下图 Demo 中启用窄带高清后,视频的清晰度有明显的提升(码率也有显著下降)。

image.png

同时利用 RTC 低延时,再加 AI 特效的处理,可以产生很多有意思的场景。把真人的流推到云端,云端做卡通人像的输出处理,然后输出卡通人像做实时的交流,在会场的观众相互看到的是各自的卡通人像。

搭配云端的抠图技术很容易就能实现虚拟的教育场景和虚拟的会议室场景。比如虚拟课堂,可以把人像抠到 PPT 里增加整个效果演示的沉浸感。虚拟会议室里不同的参会者通过抠图把他们排列到虚拟的会议室场景里来,达到虚拟会议室的效果。


扫码入群和作者一起探讨音视频技术
获取更多视频云行业最新信息👇
image.png

「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云产品技术交流群,和业内大咖一起探讨音视频技术,获取更多行业最新信息。
相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
相关文章
|
6月前
|
存储 编解码 算法
探索FFmpeg复用:深入理解媒体数据的组织与封装(三)
探索FFmpeg复用:深入理解媒体数据的组织与封装
133 0
|
6月前
|
存储 编解码 安全
探索FFmpeg复用:深入理解媒体数据的组织与封装(二)
探索FFmpeg复用:深入理解媒体数据的组织与封装
138 0
|
6月前
|
存储 编解码 算法
探索FFmpeg复用:深入理解媒体数据的组织与封装(一)
探索FFmpeg复用:深入理解媒体数据的组织与封装
180 0
|
JSON 数据格式
ffmpeg音视频开发: 使用ffprobe获取媒体信息
ffmpeg音视频开发: 使用ffprobe获取媒体信息
467 0
|
开发工具 Windows 编解码
基于FFMPEG SDK流媒体开发1---解码媒体文件流信息
最近项目涉及到流媒体等开发,由于有过开发经验深知其难度所在,没办法只能重新拾起,最新版的SDK被改的一塌糊涂,不过大体的开发思路都是一样的,看多少书查多少资料都无用,一步一步的编写代码 才是学好的关键。
884 0
|
28天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
102 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
1月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
55 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
1月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
133 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
71 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势