缘起
FFmpeg是一个非常强大的音视频处理工具,可以用来剪辑、转码、合并、分离、提取视频音频等等。FFmpeg是一个自由开源软件,最初由法国程序员法布里斯·贝拉(Fabrice Bellard)发起,现在由米夏埃尔·尼德迈尔(Michael Niedermayer)维护。目前市面上的很多播放器、视频剪辑软件、转码软件,比如 Blender,Kodi, Plex, Shotcut, VLC media playe, YouTube等,都是基于FFmpeg的。当然,也有很多软件使用了FFmpeg的代码,但并未遵守FFmpeg的开源协议,被钉在了“FFmpeg耻辱柱”上。
FFmpeg是一个命令行工具,使用起来稍有些复杂,但功能非常强大。我只用到过几个简单的功能,比如视频剪切、合并、调整分辨率、转码,视频加速、慢放,音视频分离。但我之前一直都只用CPU来处理,现在电脑上有了显卡,就想使用GPU来加速。这确实费了我一点儿功夫,这里就来总结记录一下。
前提
- 电脑上有NVIDIA显卡
- Linux系统
从源码编译安装FFmpeg
我使用Ubuntu 22.04系统,之前我的FFmpeg是通过apt安装的,但是这个版本不支持GPU加速。所以我需要从源码编诹安装。如果你之前使用apt安装的FFmpeg,需要先卸载掉:
```bash
sudo apt-get remove ffmpeg
```
编译安装过程我参考了英伟达官方的文档。但遗憾的是,这个文档似乎有些过时了,直接按照文档的步骤编译安装,会出现一些问题。下面是我成功编译安装的步骤。
安装依赖
依赖主要有三方面:
-
英伟达的显卡驱动。这个请参考我之前的文章Ubuntu 22.04安装英伟达显卡驱动。
-
英伟达的编码接口库。使用下面的命令从源码编译安装。
1 2
git clone https://github.com/FFmpeg/nv-codec-headers.git cd nv-codec-headers && sudo make install && cd –
-
FFmpeg的依赖库。使用下面的命令安装。
1
sudo apt-get install build-essential yasm cmake libtool libc6 libc6-dev unzip wget libnuma1 libnuma-devsudo apt-get install build-essential yasm cmake libtool libc6 libc6-dev unzip wget libnuma1 libnuma-dev
编译安装
-
下载FFmpeg源码。
1
git clone https://git.ffmpeg.org/ffmpeg.git
-
配置编译参数。
1
./configure --enable-nonfree --enable-cuda-nvcc --enable-libnpp --extra-cflags=-I/usr/local/cuda/include --extra-ldflags=-L/usr/local/cuda/lib64 --disable-static --enable-shared --disable-x86asm
-
这里的
/usr/local/cuda
是英伟达显卡驱动的安装路径,如果你的安装路径不同,请修改。 -
--enable-nonfree
是为了支持非自由的编码器。 -
--enable-cuda-nvcc
是为了支持CUDA加速。 -
--enable-libnpp
是为了支持NPP加速。NPP是NVIDIA Performance Primitives的缩写,是英伟达提供的一套高性能图像和信号处理函数库,FFmpeg默认是不支持的。 -
--disable-x86asm
是为了避免编译时出现如下错误:1
nasm not found or too old. Please install/update nasm or use --disable-x86asm for a build without hand-optimized assembly.
如果上述命令执行没有问题,则可以继续编译。
-
-
编译。
1
make -j8
这里的
-j8
是指使用8个线程并行编译,可以根据自己的CPU核心数来调整。 -
安装。
1
sudo make install
上述命令会把FFmpeg安装到
/usr/local/bin
目录下。
问题解决
在按照上面步骤编译安装后,当我在命令行运行ffmpeg
时,出现了如下错误:
|
|
这是因为FFmpeg的库文件没有正确链接,需要手动链接:
|
|
如果上述命令不能解决问题,这是因为FFmpeg在编译安装时把链接库安装到了/usr/local/lib
目录下,而系统默认的链接库路径是/usr/lib
。这时需要把/usr/local/lib
添加到链接库路径中:
|
|
FFmpeg的基本使用
这里简单列举几个我使用过的命令:
-
视频剪切。
1
ffmpeg -i input.mp4 -ss 00:00:00 -t 00:00:10 -c copy output.mp4
-i input.mp4
:输入文件。-ss 00:00:00
:开始时间。-t 00:00:10
:持续时间。-c copy
:复制编码。
-
视频转码。
比如把mkv格式转为mp4格式。
1
ffmpeg -i input.mkv -codec copy output.mp4
-codec copy
:复制编码。
-
视频加速或慢放。
1
ffmpeg -i input.mp4 -vf "setpts=0.5*PTS" output.mp4
-vf "setpts=0.5*PTS"
:加速倍数。小于1表示加速,大于1表示减速。这里的0.5表示加速2倍。
-
视频分辨率调整。
1
ffmpeg -i input.mp4 -vf scale=1920:1080 output.mp4
-vf scale=1920:1080
:目标分辨率。
-
音视频分离。
1 2
ffmpeg -i input.mp4 -vn -acodec copy output.aac ffmpeg -i input.mp4 -an -vcodec copy output.mp4
-vn
:不包含视频。-acodec copy
:复制音频编码。-an
:不包含音频。-vcodec copy
:复制视频编码。
-
视频合并
如果要合并的视频分辨率、帧率、编码等参数一致,且视频只有两个,可以使用下面的命令:
1
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" output.mp4
-filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]"
:合并视频和音频。
如果要合并的视频数量较多,则推荐把视频列表写入一个文本文件,然后使用
concat
协议来合并。1
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
-
-f concat
:指定协议。 -
-safe 0
:允许读取任意文件。 -
-i list.txt
:视频列表文件。内容如下:1 2 3
file 'input1.mp4' file 'input2.mp4' file 'input3.mp4'
-
-c copy
:复制编码。
使用GPU加速
使用GPU加速需要在编译时添加--enable-cuda-nvcc
和--enable-libnpp
参数。上面的编译安装步骤中已经添加了这两个参数。
使用GPU加速需要指定-hwaccel cuda
参数。比如:
|
|
-hwaccel cuda
:指定使用CUDA加速。-hwaccel_output_format cuda
:指定输出格式为CUDA。-c:v h264_nvenc
:指定使用NVIDIA的h264编码器。当然你也可以使用其他编码器,比如hevc_nvenc
。若要查看支持的编码器,可以使用ffmpeg -h encoder=nvenc
命令。
当然,上面介绍的那些命令也可以使用GPU加速,只需要添加-hwaccel cuda
参数即可。
我的CPU是i5-9600K,显卡是英伟达的RTX 4060 Ti。只使用CPU时,处理一个分辨率为1920x1080的视频,CPU占用率约100%,处理速度约为每秒30帧。使用GPU加速后,GPU占用率约为33%,处理速度约为每秒500帧。可以看到,GPU加速后处理速度提升了约16倍。
清晰度问题
使用GPU加速后,视频的清晰度可能会有所下降。这是因为GPU加速时,FFmpeg会使用NPP库来处理图像,NPP库的处理精度可能不如CPU。
如果你分别用CPU和GPU处理同一个视频,然后对比两个视频的大小,会发现GPU处理的视频大小要小很多。例如,我用CPU剪切的一个视频大小为300MB,用GPU剪切同一个视频,大小只有50MB。
查看两个视频的码率,会发现GPU剪切的视频的码率远小于源视频的码率。这是因为GPU处理时,会对视频进行压缩,导致视频的清晰度下降。如果你想保持视频的清晰度,可以指定码率。
|
|
-b:v 20M
:指定码率为20M。这里的20M是指20Mbps,可以根据自己的需求调整。
FFmpeg在使用GPU加速时默认优先考虑速度,而不是清晰度。如果你想保持视频的清晰度,还可以使用-preset slow
参数来指定编码速度。
|
|
-preset slow
:指定编码速度为slow。这里的slow是指慢速,处理的速度慢了,但清晰度会提高。
你也可以将上述两个参数结合起来使用。
|
|