window10_ffmpeg-with-nvidia-gpu编译

发布于 2022年 05月 04日 12:40

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

本文主要介绍 如何在window10的环境下 编译ffmpeg 的 硬件编解码器,NVENC 跟 NVDEC。

首先电脑需要装 一块 NVIDIA 的显卡,我的是 GeForce RTX 2060 。

NVENC 跟 NVDEC 编解码器所支持的GPU显卡可以通过下面地址查看:

developer.nvidia.com/video-encod…

编译出 NVENC 跟 NVDEC 编解码器 需要先安装 CUDA toolkits ,参考官网教程安装,CUDA 安装教程 。

开始操作:

1,先查看自己本地 NVIDIA 的驱动版本。点击菜单栏的 "帮助" -> "系统信息" 。

从上图可以看到,本地显卡 GeForce RTX 2060 的驱动版本号是 456.71,再点击此链接,查看 显卡驱动 456.71 版本对应的 CUDA Toolkit 版本

由于Toolkit Driver (显卡驱动) 是向后兼容的,所以我们选择 CUDA toolkits 10.1 进行安装。 CUDA toolkits 10.1 支持的 显卡驱动 最低版本是 418.96。我们的显卡驱动 版本号是 456.71,所以用 CUDA toolkits 10.1 是没问题的。

CUDA Toolkits 历史版本下载:developer.nvidia.com/cuda-toolki…

这里需要讲解一下 Toolkit Driver (显卡驱动) 跟 CUDA toolkits (CUDA工具套件)的关系。因为要编译硬件编解码器 NVENC 跟 NVDEC,所以需要安装 CUDA toolkits 进行编译。 CUDA toolkits 只是编译程序的时候需要,一旦编译出来 exe运行文件,CUDA toolkits 就没用了,也就是说客户的目标机器只需要安装 Toolkit Driver (显卡驱动) 就能用 ffmpeg.exe 里面的硬件编解码 NVENC 跟 NVDEC,客户的目标机器不需要 安装CUDA toolkits 。官网的教程有提及,如下:

Running a CUDA application requires the system with at least one CUDA capable GPU and a driver that is compatible with the CUDA Toolkit.

CUDA 的全称是 Compute Unified Device Architecture,计算机统一设备架构。

2,开始安装,CUDA toolkits 10.1安装界面如下,选择自定义安装

这个CUDA toolkits 经常安装失败,本人装了2个小时,官网英文教程也看了。最后是重启 window 10 再安装就好了。

CUDA toolkits 安装完毕,在CMD命令行输入 nvcc -V,查看安装是否成功。


本文 编译的 ffmpeg.exe 是64位,因为 64位才能最大发挥 CUDA toolkits 的作用。官网的教程有讲如何编译 32位,但是我看了下,32位会少很多库,应该会少很多功能,所以本文不讲解32位的编译。

CUDA toolkits 里面只有lib后缀的库,所以 ffmpeg 只能用 MSYS2 + MSVC 的方式编译。

NVIDIA 官方 FFmpeg 编译教程 : ffmpeg-with-nvidia-gpu

因为需要用到 MSVC ,所以需要安装 vs2019 ,请自行安装。vs2017 应该也是可以的,本文是基于 vs2019的。MSYS2 在之前的文章已经装过了。

本文虽然也是用的MSYS2,但是跟《window10_ffmpeg调试环境搭建-自己编译》不同,不能直接在 window10 CMD 里面直接 输入  .\msys2_shell.cmd -mingw64

因为需要用到 vs2019 里面的 cl.exe 来编译 ffmpeg 的代码,需要用 vs2019 里面的 link.exe 来链接库。还需要用到 vs2019 里面的 某些lib 库,例如 LIBCMT.lib。

所以MSYS2 需要继承 vs2019 的环境变量,怎么继承呢?

MSYS2 环境继承vs2019 的环境变量 操作教程:

1,修改 C:\msys64\msys2_shell.cmd 中的 rem set MSYS2_PATH_TYPE=inherit,去掉rem,取消这⼀句的注释。使MSYS2的环境变量继承当前CMD的窗口的环境变量。

2,重命名 C:/msys64/usr/bin/link.exe 为 C:/msys64/usr/bin/link.bak , 避免和MSVC 的link.exe抵触。这个是网上文章的做法,这个其实在新版的ffmpeg 里面是不需要的,在 configure里面编译的时候,调用的是 ./compat/window/mslink ,如下:

 ./compat/window/mslink 代码
#!/bin/sh
​
LINK_EXE_PATH=$(dirname "$(command -v cl)")/link
if [ -x "$LINK_EXE_PATH" ]; then
    "$LINK_EXE_PATH" $@
else
    link.exe $@
fi
exit $?

上面是 mslink 的代码,可以看到,他的逻辑就是优先 使用 跟 cl.exe 同目录下的 link.exe。cl.exe 只有在vs2019 那里才有,C:/msys64/usr/bin 目录下没有 cl.exe,所以会优选使用 vs2019 里面的link.exe,所以不重命名 C:/msys64/usr/bin/link.exe 也没关系。

3,上面的 C:\msys64\msys2_shell.cmd 文件已经修改好了,现在要找到 x64 Native Tools Command Prompt for VS 2019 这个命令工具。

点击 x64 Native Tools Command Prompt for VS 2019 打开命令行,一定要这样打开命令行,这样命令行才能有 vs2019 的环境变量,然后才能让 msys2 来继承,我之前说的 MSYS2_PATH_TYPE=inherit 继承的环境变量 是当前窗口的环境变量就是这个意思。

千万不要用下面这种 win+R 的方式打开命令行,这样子打开命令行是没有 vs2019 的环境变量的。

4,演示完错误的打开CMD的方式,我们继续操作,在 x64 Native Tools Command Prompt for VS 2019 命令窗口输入 cd c:\msys64 先回到 msys64目录,然后再输入 .\msys2_shell.cmd -mingw64,启动 msys2 命令行窗口,如图:

#回到 MSYS2 的安装目录
cd c:\msys64\
#启动 msys2 命令行
.\msys2_shell.cmd -mingw64

5,在 msys2 命令行窗口 输入 echo $LIB ,可以看到 msys2 命令行窗口 已经继承了 vs2019 的 lib 环境变量。

6,再输入一下 which cl.exe ,确认一下 cl.exe 是在vs2019的目录下,同时看下cl.exe的目录是否有link.exe。


MSYS2 + MSVC 环境已经准备好了,MSYS2 命令行已经继承了 vs2019 的环境变量,下面开始做一些 编译 ffmpeg 前的准备工作。

编译 ffmpeg NVENC NVDEC 教程:

1,下载 FFmpeg-n4.4.1.zip ,百度网盘,提取码:rpo3 ,下载好之后解压到 C:\msys64\home\loken\ffmpeg ,这个目录是我们之前文章经常用的目录。

2,安装所需软件,pacman -S diffutils make pkg-config yasm

这里我需要讲解一下 pkg-config 这个软件的作用,pkg-config 这个命令行在linux安装软件经常使用,他到底是做什么用的呢?

参考阅读:Linux的pkg-config命令,在本文中 pkg-config 命令的作用是提取 ffnvcodec.pc 的变量。

3,下载 nv-codec-headers,nv-codec-headers 是 与 Nvidias 编解码器 API 接口(interface)所需的头文件的 FFmpeg 版本。

这里跟 官方教程 ffmpeg-with-nvidia-gpu 不太一样,不能直接执行 git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git 因为拉下来的 nv-codec-headers 需要的显卡驱动至少要 471.41 以上,我们本地显卡驱动是 456.71,如果直接用官网的命令会编译失败。

nv-codec-headers 的Readme 如下,可以看到 所需显卡驱动版本:

FFmpeg version of headers required to interface with Nvidias codec APIs.

Corresponds to Video Codec SDK version 11.0.10.

Minimum required driver versions:
Linux: 470.57.02 or newer
Windows: 471.41 or newer

所以 nv-codec-headers 需要手动下载,nv-codec-headers下载地址,我们选 Version 9.1.23.3 下载,因为显卡驱动是向后兼容的, 9.1.23.3 的版本可用。

4,下载完 nv-codec-headers 之后,解压到 C:\msys64\home\loken\ffmpeg 目录,如下:

5,安装 nv-codec-headers-n9.1.23.3。到这一步,也不能像 官方教程 ffmpeg-with-nvidia-gpu 那样直接进入 nv-codec-headers-n9.1.23.3,然后执行make install。我们需要手动改下 nv-codec-headers-n9.1.23.3 里面的 ffnvcodec.pc 文件,改动最后一行,改动如下:

Cflags: -I${includedir}
改为
Cflags: -IC:/msys64/home/loken/ffmpeg/nv-codec-headers-n9.1.23.3/include

为什么要这样改,是因为 configure 里面用 cl.exe 编译的时候,不能引入 C:/msys64/usr/include 里面的头文件,会跟 MSVC 的头文件同名混乱,导致报错。

Cflags: -I${includedir} 的运算结果就是 Cflags: -IC:/msys64/usr/include 。

如果不改,configure的时候,在ffbuild/config.log 中会报 C2054 等等错误,如图:

这个C2054 错误非常隐蔽,这个错误不会导致 configure失败,所以看configure的输出是成功的,只是编译出来的 ffmpeg.exe,执行 ffmpeg.exe -hwaccels 显示硬件加速的时候,没有cuda这个选项输出。也无法使用硬件编解码。

所以学会看 ffbuild 目录下的 config.log 日志非常重要。

6,改好 ffnvcodec.pc 之后,我们进入 nv-codec-headers-n9.1.23.3 目录,执行 make install PREFIX=/usr

从上图可以看到,make install PREFIX=/usr 命令最主要的就是把 ffnvcodec.pc 拷贝到 /usr/lib/pkgconfig 目录,/usr/include/ffnvcodec 里面的头文件我们configure编译的时候用不到,上面已经把 ffnvcodec.pc 里面的 Cflags 参数改了。所以你可以不用 make install,手动把 ffnvcodec.pc 拷贝到 /usr/lib/pkgconfig 目录,效果是一样的。可以进入 nv-codec-headers-n9.1.23.3 目录看看,实际上只有几个 .h 头文件跟 .pc 文件。

扩展知识:

网上有些文章是旧版的编译规则,旧版的需要下载 Video_Codec_SDK,我们可以下载一个来看看,Video_Codec_SDK 里面主要是一些 lib 库跟 .h头文件,如下图。

我用 Everything 软件 搜索 nvcuvid.lib 这个库,

我电脑是装了 4个版本的 CUDA toolkits,8.0,9.0,9.2,10.1,可以看到 10.1 版本的 CUDA toolkits 并没有出现在 everything 的搜索里面,所以CUDA toolkits从 10.1 版本开始就去掉了 nvcuvid.lib 这个库,之前的硬件解码器好像是 叫cuvid,现在好像改了个名字,叫nvdec。

所以本文编译 ffmpeg NVENC NVDEV,不需要下载安装 Video_Codec_SDK ,那是旧版的方式。咱们用的 CUDA toolkits 版本是 10.1,是新版的。

编译 ffmpeg NVENC NVDEV 特别重要的一个地方就是版本号是否匹配。

  • 显卡驱动版本号
  • CUDA toolkits 版本号。
  • nv-codec-headers 版本号

上面这个 3个东西的版本号必须匹配才能编译成功。

7,安装好 nv-codec-headers 之后,继续操作,回到 /home/loken/ffmpeg 目录,创建 nv_sdk_10.1 文件夹。

cd /home/loken/ffmpeg/
mkdir nv_sdk_10.1

8,复制 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\include 下面所有的文件到 nv_sdk_10.1 目录

9,复制 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\lib\x64 下面所有的库文件到 nv_sdk_10.1 目录


操作到这里,nv-codec-headers 已经安装好了,CUDA toolkits 的lib跟头文件也已经拷贝到 C:\msys64\home\loken\ffmpeg\nv_sdk_10.1 目录,下面正式开始编译 FFmpeg NVENC NVDEC。

FFmpeg NVENC NVDEC 编译:

1,进入 FFmpeg-n4.4.1 源码目录

cd /home/loken/ffmpeg/FFmpeg-n4.4.1

2,添加 /usr/lib/pkgconfig 路径到 $PKG_CONFIG_PATH

export PKG_CONFIG_PATH="/usr/lib/pkgconfig":$PKG_CONFIG_PATH

这样做是因为 我们的 ffnvcodec.pc 是安装到 /usr/lib/pkgconfig 下的。/usr/lib/pkgconfig 目录默认不在 $PKG_CONFIG_PATH 环境变量里面,如果不加进去,会导致 configure的时候找不到 ffnvcodec,然后跳过编译 cuda 硬件加速,虽然能configure成功,最后也能编译成功,但是 ffmpeg.exe -hwaccels 的时候没有 cuda这个选项输出,也就没法使用硬件编解码,从 ffbuild/config.log 可以看到错误,如下:

3,FFmpegn4.4.1 版本源码在 编译 CUDA 硬件编解码 还会报一个错误,错误提示如下:

"libavfilter/vf_scale_cuda_bicubic.ptx.c(1925): fatal error C1091: compiler limit: string exceeds 65535 bytes in length"

这是一个bug,bug地址,ffmpeg 已经修复了,出了一个 patch ,patch 地址 。请按照patch补丁的方法修改以下文件。

  • FFmpeg-n4.4.1\compat\cuda\ptx2c.sh
  • FFmpeg-n4.4.1\configure

或者用以下命令应用这个 patch。

 git am 0001-Patch-for-ticket-9019-CUDA-Compile-Broken-Using-MSVC.patch

4,上面的操作都执行之后,可以正式开始编译 ffmpeg 了,命令如下:

./configure \
--prefix=/home/loken/ffmpeg/build64/ffmepg-4.4-nv-10.1 \
--enable-gpl \
--enable-nonfree \
--disable-shared \
--enable-cuda-nvcc \
--enable-libnpp \
--toolchain=msvc \
--extra-cflags="-I/home/loken/ffmpeg/nv_sdk_10.1" \
--extra-ldflags="-LIBPATH:/home/loken/ffmpeg/nv_sdk_10.1"
​
make -j8
make install

上面这个configure 命令有个重要知识点需要提及一下,用了 --toolchain=msvc 之后,使用的是 vs2019 目录的 cl.exe 来编译C代码,不是 MSYS2 环境的 gcc.exe了。所以 --extra-ldflags 指定库搜索路径的方式 是 -LIBPATH ,不是 -L。 -L 是 gcc.exe 用的命令参数。-LIBPATH 才是 cl.exe用的命令参数。

5,编译完成之后,build64/ffmpeg-4.4-nv-10.1 目录如下:

可以看到,msys2 + msvc 编译出来的 ffmpeg.exe 非常精简,不像之前 msys2 + MinGW 编译出来的 ffmpeg.exe 运行需要拷贝一堆dll库过来。

6,执行  ./ffmpeg.exe -hwaccels 显示ffmpeg的硬件加速方法,如下图所示,可以看到 cuda 的选。

cd /home/loken/ffmpeg/build64/ffmepg-4.4-nv-10.1/bin
./ffmpeg.exe -hwaccels


至此,ffmpeg.exe 已经编译完毕。下面找一个高清电影,来测试ffmpeg 的NVENC 硬件编码。

./ffmpeg.exe -hwaccel cuvid -i juren.mp4 -vcodec h264_nvenc -acodec copy juren_h264_nvenc.mp4

从上图可以看到, cpu的负载很小,而GPU 直接 99% 满功率了。ffmpeg.exe 硬件编码测试通过。


常见错误:

1, LINK : fatal error LNK1104: cannot open file 'LIBCMTD.lib'。

这个错误是因为没用 x64 Native Tools Command Prompt for VS 2019.exe 开启CMD,MSYS2 没继承vs2019的环境变量,所以找不到 vs2019 目录的 LIBCMTD.lib。

2,"libavfilter/vf_scale_cuda_bicubic.ptx.c(1925): fatal error C1091: compiler limit: string exceeds 65535 bytes in length"

这是一个bug,bug地址,ffmpeg 已经修复了,出了一个 patch ,patch 地址

重要知识点:

1, 如何查看自身电脑的 GPU 型号是否支持 NVENC NVDEC 硬件编解码?

NVIDIA 官网有 developer.nvidia.com/video-encod…

2,如何查看自身电脑的GPU驱动对应的 CUDA toolkits 版本?

docs.nvidia.com/cuda/cuda-t…

3,如何查看自身电脑的GPU驱动对应的 nv-codec-headers 版本?

下载 nv-codec-headers,直接打开 里面的Readme ,能看到最低驱动版本支持。

4,configure 用了 --toolchain=msvc 之后,使用的是 vs2019 目录的 cl.exe 来编译C代码,不是 MSYS2 环境的 gcc.exe了。所以 --extra-ldflags 指定库搜索路径的方式 是 -LIBPATH ,不是 -L。 -L 是 gcc.exe 用的命令参数。-LIBPATH 才是 cl.exe用的命令参数。

扩展知识:

1,CUDA 全称 Compute Unified Device Architecture,翻译为 "计算机统一设备架构"。

2,CUDA 编程范例,github.com/nvidia/cuda…

3,客户的目标机器只需要安装 Toolkit Driver (显卡驱动) 就能用 ffmpeg.exe 里面的硬件编解码 NVENC 跟 NVDEC,客户的目标机器不需要 安装CUDA toolkits 。

参考资料:

1,ffmpeg 硬件加速介绍

2,trac.ffmpeg.org/wiki/Compil…


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1,QQ:2338195090。

推荐文章