如何提高 FFmpeg 中的视频流解码速度

大家好!我是大聪明-PLUS

随着 4K(超高清)等大型视频格式的出现,视频流解码效率问题变得尤为紧迫。在普通计算机上,必须采取特殊措施才能实时处理此类视频流。本文探讨了在基于 FFmpeg 的解决方案中提高视频流解码速度的可能方法,并展示了测量 H264 和 HEVC (H265) 编码的 4K 视频流解码速度的实验结果。


1. 提高视频流解码速度的三种方法

我们将探讨三种提高视频流解码速度的方法。

  1. 在标准解码器中连接额外的工作线程。
  2. 在标准解码器中启用硬件加速(HW Acceleration)。
  3. 使用在图形处理器上实现解码的专用解码器。

第一个程序仅使用 CPU 功能,而另外两个程序则使用 GPU 功能。

提高视频流解码速度的可用方法很大程度上取决于操作系统、计算机硬件和 FFmpeg 配置。本文中展示的所有结果均在以下硬件和软件配置上进行了测试:操作系统 --- Windows 10,CPU --- Intel i5 8400 2.80 GHz(6 核,无超线程),集成 GPU --- Intel UHD Graphics 630,内存 --- 16 GB,FFmpeg 版本 4.2.1。

1.1 在标准解码器中连接其他工作流程

许多解码器(但并非全部)允许您设置用于解码的工作线程数。为此,请在调用之前将结构avcodec_open2()体成员设置为所需值。另一种方法是将该选项添加到作为第三个参数传递给的选项字典中。thread_count``AVCodecContext``threads``avcodec_open2()

h264用于处理大型格式( ,, )hevc的最流行的解码器vp9支持此功能,但theora不支持。

要在命令行上连接其他流,您需要使用带有键的选项-threads

1.2. 在标准解码器中启用硬件加速

FFmpeg 支持部分解码器的硬件加速。使用 FFmpeg API 进行编程时,连接到硬件加速解码器所需的一切都位于头文件中libavutil/hwcontext.h。该文件定义了一个枚举enum AVHWDeviceType,其中每个元素对应于一种特定的硬件加速类型。可以使用以下代码查找当前 FFmpeg 版本中可用的硬件加速类型:

复制代码
void` `print_hwtypes_all`()
{
    `AVHWDeviceType` `hwtype` `=` `AV_HWDEVICE_TYPE_NONE`;
    `while` ((`hwtype` `=` `av_hwdevice_iterate_types`(`hwtype`)) `!=`
                            `AV_HWDEVICE_TYPE_NONE`)
    {
        `printf`(`"%s\n"`, `av_hwdevice_get_type_name`(`hwtype`));
    }
}`

对于上述硬件和软件配置,我们得到:

复制代码
    在哪里
    dxva2
    qsv
    d3d11va

显然,cuda它需要安装 Nvidia 显卡和相应的软件,qsv使用 Intel Quick Sync Video (QSV) 技术,该技术在集成 Intel 图形处理器上实现(参见 [1]),dxva2d3d11va使用 DirectX 视频加速技术(参见 [2]),该技术仅在 Windows 中可用,但适用于不同制造商(Intel、Nvidia、AMD)的显卡。

并非所有解码器都必须支持所有这些硬件加速类型(甚至无需支持其中任何一种)。要确定特定解码器支持哪些类型,可以使用以下代码:

复制代码
void` `print_hwtypes`(`const` `char*` `dec_name`)
{
    `const` `AVCodec*` `decoder` `=` `avcodec_find_decoder_by_name`(`dec_name`);
    `for` (`int` `i` `=` `0`; ; `++i`) { 
        `const` `AVCodecHWConfig` `*config` `=`
               `avcodec_get_hw_config`(`decoder`, `i`);
        `if` (`config`) {
            `if` (`config->methods` `&`
                    `AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX`) {
                `printf`(`"%s\n"`, 
                  `av_hwdevice_get_type_name`(`config->device_type`));
            }
        }
        `else` {
            `break`;
        }
    }
}`

对于上述硬件和软件配置,解码器支持以下h264类型的硬件加速:hevc``vp9``vc1

复制代码
    dxva2
    d3d11va
    在哪里

theora它完全不支持硬件加速。

现在我们简要回顾一下连接硬件加速解码器的步骤。

复制代码
void` `init_hwdevice`(`AVHWDeviceType` `hwtype`, `AVCodecContext` `*codec_ctx`)
{
    `AVBufferRef` `*dev_ctx` `=` `NULL`;
    `int` `ret` `=` `av_hwdevice_ctx_create`(`&dev_ctx`, `hwtype`, `NULL`, `NULL`, `0`);
    `if` (`ret` `>=` `0`) {
        `codec_ctx->get_format` `=` `get_hw_format`; `// см. hw_decode.c`
        `codec_ctx->hw_device_ctx` `=` `av_buffer_ref`(`dev_ctx`);
    }
}`

解码后,帧数据采用由设备类型决定的特殊格式,因此必须使用函数将其转换为标准像素格式之一av_hwframe_transfer_data()。对于dxva2和,d3d11va此格式将是NV12

要在命令行中启用硬件加速,您需要使用带有键的选项-hwaccel

1.3. 使用在图形处理器上实现解码的专用解码器

FFmpeg 包含两类编解码器,它们在 GPU 上实现编码和解码。

其中一款产品系列采用英特尔快速同步视频 (QSV) 技术,该技术已集成到英特尔 i3、i5、i7 和 i9 处理器系列的视频处理器中。更多详情请参见 [1]。这些编解码器带有后缀。所讨论的_qsvFFmpeg 版本包含以下解码器:h264_qsv,,,,。hevc_qsv``vp8_qsv``mpeg2_qsv``vc1_qsv

另一系列解码器使用在Nvidia 板上实现的 NVDEC和NVENC技术。这些解码器带有后缀。所讨论的_cuvidFFmpeg 版本包含以下解码器:h264_cuvid,,,,,,,。hevc_cuvid``mpeg2_cuvid``vc1_cuvid``vp8_cuvid``vp9_cuvid``mjpeg_cuvid``mpeg4_cuvid

输入流打开后,对解码器的访问通常按如下方式实现:

复制代码
AVStream` `*strm`;
`// ...`
`AVCodecID` `cid` `=` `strm->codecpar->codec_id`;
`const` `AVCodec` `*decoder` `=` `avcodec_find_decoder`(`cid`);`

但是,这只会检索给定编解码器 ID 的默认解码器。要查找其他解码器,请使用解码器名称,例如:

复制代码
const` `AVCodec` `*decoder` `=` (`cid` `==` `AV_CODEC_ID_H264`)
    `?` `avcodec_find_decoder_by_name`(`"h264_qsv"`)
    : `avcodec_find_decoder`(`cid`);`

要在命令行中使用备用解码器,需要使用带有键的选项-c:v,将其放在键之前,-i如下所示:

复制代码
ffmpeg -c:v h264_qsv -i INPUT ...

2. 测量解码速度

为了测量解码速度,我们选择了两个视频:一个采用 H264 编码,另一个采用 HEVC (H265) 编码。帧大小为 3840x2160(超高清),帧速率为 30 fps。我们测试了标准解码器h264hevc相应的 QSV 解码器。标准解码器配置了四种模式:默认、双线程、四线程和硬件加速。在我们的实验中,标准解码器的性能优于 QSV 解码器,因此后者未纳入解码速度测量。为了进行测试h264_qsv,我们编写了一个程序,该程序从文件中提取数据包并以尽可能高的速度进行解码,忽略时间戳,且不进行渲染或其他处理。该程序有两种模式:第一种模式仅执行解码,第二种模式还使用 QSV 库将解码后的帧转换为 32 位格式。 (解码器的输出帧通常为 12 位平面格式。)测量程序运行时间,并记录其相对于视频流标称持续时间的相对时间(以百分比表示)。因此,如果结果小于 100%,则有可能实时处理视频流;如果大于 100%,则不可能实时处理。同时,使用任务管理器记录了 CPU 和 GPU 的大致负载。使用的是 64 位版本的 FFmpeg。hevc_qsv``dxva2``dxva2``d3d11va``BGRAlibswscale YUV420P``NV12

| || h264 ||| 氢氯乙烯 |||

配置 # 时间 中央处理器 GPU 时间 中央处理器 GPU
默认 1 75 26 0 125 25 0
默认 2 132 28 0 180 27 0
线程数=2 1 47 42 0 74 42 0
线程数=2 2 79 48 0 104 46 0
线程数=4 1 35 60 0 46 64 0
线程数=4 2 60 54 0 71 70 0
dxva2 1 45 14 72 34 28 70
dxva2 2 107 28 35 99 30 36
xxxx_qsv 1 25 34 80 25 34 72
xxxx_qsv 2 70 39 54 70 40 50
[表格. 解码速度测量]

结果可能无需过多讨论。唯一值得注意的是转换成本相当高昂BGRA。更重要的是,尽管所有测试配置下的工作内容都非常相似,但这些成本却因测试配置的不同而存在显著差异。

我们还使用 32 位 FFmpeg 版本进行了实验。结果基本相似,只有一个例外:hevc在没有硬件加速的配置下,解码器的性能下降了 2-3 倍。这是一个相当出乎意料的结果。

所述测试可在命令行运行。使用全局选项-benchmark并将输出设置为零。以下是一些示例:

复制代码
ffmpeg -benchmark -i INPUT -an -f null -
ffmpeg -benchmark -threads N -i INPUT -an -f null -
ffmpeg -benchmark -c:v h264_qsv -i INPUT -an -f null -
ffmpeg -benchmark -hwaccel dxva2 -i INPUT -an -f null -
ffmpeg -benchmark -i INPUT -an -pix_fmt bgra -f null -

输出将显示实际值fps,参数speed将显示该值比标称值高多少倍。如果未指定关键选项-threads或为该N选项指定了特殊值auto,则解码器将使用最大可能的线程数,CPU 负载为 100%。

3. 关于QSV解码器的说明

有问题的 FFmpeg 版本包含以下 QSV 解码器:h264_qsv,,,,。后两个解码器无法正常工作。解码器生成的图像失真,并且在发送解码数据包时返回错误。诚然,这些解码器本身并不特别重要,但hevc_qsv发布无法正常工作的组件的原因仍然不明。vp8_qsv``mpeg2_qsv``vc1_qsv``mpeg2_qsv``vc1_qsv

还有一些关于剩余解码器的投诉。它们通常都能正常工作,但有一个问题:它们无法正确处理调用avcodec_flush_buffers()。虽然没有报错,但调用之后定位功能就无法正常工作了。

相关推荐
苦学编程的谢43 分钟前
RabbitMQ_2_RabbitMQ快速入门
linux·centos·rabbitmq
RisunJan44 分钟前
Linux命令-free命令(查看系统内存(RAM)和交换空间(Swap)使用情况)
linux·运维·服务器
Gauss松鼠会44 分钟前
【GaussDB】如何从GaussDB发布包中提取出内核二进制文件
linux·数据库·database·gaussdb
大聪明-PLUS1 小时前
如何向 FFmpeg 添加编解码器
linux·嵌入式·arm·smarc
linux修理工1 小时前
vagrant file 设置固定IP并允许密码登录
java·linux·服务器
·s.*1 小时前
So-arm 101机械臂训练搭建全流程
linux·ubuntu·机器人
繁华似锦respect1 小时前
C++ 设计模式之代理模式详细介绍
linux·开发语言·c++·windows·设计模式·代理模式·visual studio
似水流年 光阴已逝1 小时前
拒绝“失联”:Linux 云服务器无法登录的全链路排查手册
linux·运维·服务器
脏脏a2 小时前
【Linux】进程优先级:谁先 “上车” 谁说了算?
linux·运维·服务器