centos安装ffmpeg

1、下载并解压

$ wget https://ffmpeg.org/releases/ffmpeg-6.0.tar.xz
$ tar -xvf ffmpeg-6.0.tar.xz

2、安装yasm

wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
tar zxvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure --prefix=/usr/local
make
make install

3、编译


$ cd ffmpeg-6.0
yum -y install libass*
./configure --enable-shared --enable-gpl --enable-filter=subtitles --enable-libass --prefix=/usr/local/ffmpeg

4、安装

sudo make && sudo make install

echo "/usr/local/ffmpeg/lib" >> /etc/ld.so.conf
ldconfig

cp /usr/local/ffmpeg/bin/ffmpeg /usr/local/bin

说明:

  • -enable-gpl,如果不想使用delogo等滤镜,可以不加。
  • 为方便使用,可以将/usr/local/ffmpeg/bin加入环境变量PATH中

5、查看版本

ffmpeg -version

FFmpeg实用教程_缩放

如果要查看支持的滤镜,可以使用如下命令:

ffmpeg -filters


FFmpeg的官方文档简洁有力,但它的排版逻辑是技术文档而不是工具书或问答,因此可能并不是一个好的参阅选择。

本文将以实际用例为主。毕竟照搬 FFmpeg 的文档实在没有什么意义。不过例子是由浅入深的,如果读者没有任何的 FFmpeg 使用经验,仍然建议按顺序依次浏览。

在开始之前,先介绍一个全局参数 -hide_banner;它可以阻止 FFmpeg 在每次执行时开头打印的那一堆版本信息文本。例如,在展示 FFmpeg 的许可证时,隐藏这部分默认打印的版本信息:

ffmpeg -hide_banner -L

格式转换

这毫无疑问是最常使用的功能。关于各种常用的视频/音频编码格式或编码器,请参考 常用的编码格式 一节的内容。

转码

比如将一个 FLV 文件转为 MP4 文件并重编码,FFmpeg 会自动寻找编解码器:

ffmpeg -i video.flv video.mp4

其中,在 -i 后指定输入文件的文件名,在所有命令的最后指定输出文件的文件名。 如果文件名带有空格,请用双引号将文件名包裹。 上述的 video.mp4 在 -i 参数之后,称为 输出参数 ;反之,在 -i 之前的称为 输入参数。

转码过程可能较慢。关于快速地进行格式转换,请参阅下文 流复制 一节的内容。

用户也可以显式地指定编码器,比如使用 h.264 视频编码器与 flac 音频编码器。

ffmpeg -i video.flv -c:v libx264 -c:a flac video.mkv

其中 -c 是 -codec 的简写。用 v 表示视频(video)编码器、 a 表示音频(audio)编码器。

流复制

格式转换还有一种快速的情形。如果两者的所有流都不改动且输出容器支持输入的所有流,那么可以直接向 -c 传递 copy 以进行流复制。这样省去了重新编码的时间,格式转换将十分迅速:

ffmpeg -i video.avi -c copy video.mp4

其中,-c 是 codec 的简称,表示所有流的编解码器。该命令表示所有流均不进行额外操作,直接复制到新容器中。

提取流(音频、字幕)

有时需要指定流来完成格式转换,比如将一个 MP4 视频文件转为 AAC 音频文件(此处实质上是直接提取):

ffmpeg -i video.mp4 -c:a copy audio.aac

此处的 -c:a 表示音频流;视频流 -c:v 与字幕流 -c:s 自然也类似。 注意:如果音频流与容器冲突时,你需要将 copy 改为正确的编解码器(或者删去 -c:a copy 来让 FFmpeg 自动选择),以执行重编码。

对于内挂了字幕的视频文件,也可以将其字幕单独提取出来,例如:

ffmpeg -i video.mkv -c:s copy subtitle.srt

截取视频

下面,以想要截取 video.mp4 视频的第2到第5分钟为例。

对于容易计算片段秒数的截取任务(本例中片段长为 (5-2)*60=180秒),可以使用 -t 参数,即指定片段长度。

ffmpeg -ss 00:02:00 -i video.mp4 -t 180 cut.mp4

其中, -ss 参数指定了起始的时间戳记,而 -t 参数指定了片段长度(秒)。传递给 -t 的片段长度可以写成 00:03:00 的形式。它也可以带有小数,比如用 180.5 表示 180.5 秒。

或者,用户可以不用 -t 指定片段长度,而是用 -to 指定终止时刻。下例中把参数 -ss 与 -to 都放在了 -i 参数之前:

ffmpeg -ss 00:02:00 -to 00:05:00 -i video.mp4 cut.mp4

需要注意,在上面的例子中, 参数 -ss 均被放在了 -i 参数之前,这称为输入(inputing)参数语法;对应的时间戳检索方式称为输入检索(inputing seek)。如果放在 -i 参数之后,则称为输出(outputing)参数与输出检索。

  • 输入检索根据关键帧来检索,而输出检索是逐帧地检索地。因此输入检索地速度会比输出检索更快。
  • 由于关键帧定位的特性,输入检索在执行流复制操作(如 c copy )时定位可能并不精确;在非流复制(即重新编码)并指定了 accurate_seek (默认已指定)时,则无此问题。关于为何会造成定位的不精确,请参考本小节末尾的注释。
  • 输入检索会提前将 ss 参数指定的时间戳设置为 0;因此,如果将 t/-to 参数放在 i 参数之后(作为输出参数),FFmpeg 都实质将参数值当作一个片段长度(而不是终止时刻)。例如:
ffmpeg -ss 00:02:00 -i video.mp4 -t 00:05:00 cut.mp4
ffmpeg -ss 00:02:00 -i video.mp4 -to 00:05:00 cut.mp4  # 意外的结果

这两种命令的结果是一样的,都截取了第 2 到第 7 分钟;这对于使用 -to 参数的用户来说,可能是不希望看到的。因此,我推荐将 -t/-to 参数一起都作为输入参数来使用。

  • 官方在早期的版本中,还给出了一种使用 copyts 参数的方法(参考 Seeking - FFmpeg ) 来修正上一条提到的 to 参数受到影响的问题。
ffmpeg -ss 00:02:00 -i video.mp4 -to 00:05:00 -copyts cut.mp4
  • 这种方法虽然会输出第 2 到 5 分钟的视频,但经笔者测试,其输出视频的时间戳会在一些视频播放器上出现问题。笔者 并不推荐 使用这种 -copyts 的修正语法。

这部分 FFmpeg 的语法比较复杂,稍微小结一下:

本小节的扩展阅读:

为什么在流复制时使用快速检索,起始时刻会变得不精确?

流复制操作 -c copy 与放在 -i 之前进行的快速检索 -ss hh:mm:ss -i video 同时使用,会导致不能精确定位起始时刻。例如:

ffmpeg -ss 00:02:00 -to 00:05:00 -i video.mp4 -c copy cut.mp4

这其中的原理是,作为输入参数的 -ss 会先快速定位到给定的起始时间戳(如上例的 00:02:00 )之前的一个位置。然后在视频流编码过程中,将该位置与起始时间之间的多余的这段舍弃。

由于在流复制 -c copy 时,视频流不会被编码而是直接复制,因此上述提到的多余的视频段就不会被舍弃。这会导致截取的视频将包含指定时间之前的一段视频内容。

具体的解释请参考 FFmpeg 官方文档 中关于 -ss 参数的说明。

分辨率缩放

注解:分辨率缩放总是会编码视频,请注意这一点。

分辨率缩放也是一个常见的需求,这需要使用到 FFmpeg 提供的视频过滤器(或称视频滤镜,video filter),也即 -vf 参数。由于过滤器的使用过于复杂,在此也不会详细介绍;这里只是针对过滤器中的缩放器(scaler)功能进行说明。缩放器还有许多复杂的用法详情也可以参考官方文档的 Video filter - Scaler 章节。

例如,我们要将一个高分辨率视频从 1440p 缩放,那么我们可以使用参数:

# 输出到1280x720的例子## 直接指定宽1280、高720。选择以下任意一种写法即可scale=w=1280:h=720
scale=1280:720
scale=1280x720
## 可以用-1表示按原视频宽高比自动计算
scale=1280:-1
scale=-1:720
## 也可以使用倍率的写法,用iw、ih代表输入视频的宽和高
scale=iw/2:ih/2

# 输出到方形720x720的例子。## 可以用ow、oh代表变换后输出视频的宽和高
scale=iw/2:ow

这些参数中,使用冒号作为分隔符、等号作为键值对的连接符。

除了分辨率,我们有时候也会用 flags 参数指定缩放算法(参见官方文档 Scaler Options)。关于视频缩放算法的选择(与图片可能不同),可以参考 StackExchange 上的这一篇回答 Which resize algorithm to choose for videos? ;简单地说, 该回答建议在降分辨率时使用 Lanczos 或 spline,在升分辨率时使用 bicubic 或 Lanczos

一些分辨率缩放的命令示例:

# 使用默认的  bicubic 算法缩放到高720并保持原宽高比,并用默认编码格式(H.264)编码
ffmpeg -i video.mp4 -vf scale=-1:720 out.mp4

# 指定使用 Lanczos 算法缩放到原视频的宽高的各一半,并用 H.265 格式以默认质量编码
ffmpeg -i video.mp4 -vf scale=iw/2:ih/2:flags=lanczos -c:v libx265 -c:a copy out.mp4

添加字幕

FFmpeg 可以将字幕内挂到封装容器内,也可以内嵌到视频流中。

一些注意事项:

  • 独立的字幕文件请使用 UTF-8 编码。
  • Windows 系统可能缺少一个字体接口,需要自己配置一份 fonts.conf 文件,并放在 %FONTCONFIG_PATH% 这个环境变量对应的路径下。
  • 如果用户没有该变量,请新建一个;其默认值一般是 C:\Users\用户名\ 。
  • 关于 fonts.conf 文件,请参考本文的附录 附录:fonts.conf 。

内挂字幕

内挂字幕是一种相对于外0挂字幕的称呼。外0挂字幕是指将字幕存放在一个独立的字幕文件中,在播放视频时,通过视频播放器来加载这个字幕文件。而内挂字幕,是将这样一个独立的“字幕文件”,封装在了视频文件内部作为独立的字幕数据流。这样既能按需开启或关闭字幕,也免去了字幕文件丢失、匹配等烦恼。

内挂字幕的本质是将字幕文件单独作为字幕流封装,因此不需要对视频流进行编码。因此,将字幕文件内挂到指定的视频一般非常快:

ffmpeg -i input.mp4 -i input.srt -c:v copy -c:a copy -c:s ass output.mkv

在封装时,一般需要选择 -c:s ass 这个字幕转码器。上例中使用了早年间非常流行的内挂字幕容器 mkv,实际上 mp4 容器也可以进行内挂操作。

内嵌字幕

内嵌字幕(或称硬字幕)是指将字幕与原视频图像混叠的一种字幕,它直接嵌入到图像中,因此无法关闭,也无法调整字幕的大小、字体等样式。内嵌字幕的本质是将字幕作为图像输出,因此需要对视频流进行编码,往往速度慢:

ffmpeg -i input.mp4 -vf subtitles=input.srt output.mp4

如果字幕以字幕流的形式存在于另一个视频文件中,可以直接调用,无需将字幕流先提取成文件:

ffmpeg -i input.mkv -vf subtitles=input.mkv output.mp4

合并视频

最简单的视频合并方法,是将所有待合并的视频文件路径,依次列在一个 txt 文件中,然后让 FFmpeg 读取它。

假设我们已经将所有待合并的 mp4 文件放在当前文件夹中,并且按照合并的顺序进行了命名。那么,用户可以在该文件夹中用 Shift + 鼠标右键打开 PowerShell 控制台,然后依次输入以下命令:

ls eg-*.mp4 | % Name | foreach {"file '" + $_ + "'"} > mylist.txt
ffmpeg -f concat -i mylist.txt -c copy output.mp4

注解

以上命令第一行中的操作需要 不低于 6.0 版本 的 Powershell(Windows 10/11 自带的是 5.x 版本),读者可以前往 Powershell 的 Github 开发仓库页面 选择版本进行下载。这是因为低于 Powershell 6.0 的版本会在写入文本文件时包含 BOM,而包含 BOM 的文本文件并不能正确地被 FFmpeg 所识别;而从 6.0 版本开始,Powershell 新增并默认使用了 -Encoding utf8NoBOM 选项。

用户可以输入 get-host | select-object Version 来查看当前运行的 Powershell 版本。

如果用户对于通配符 * 语法有所了解,应当可以理解上例中 eg-*.mp4 通配了所有以 eg- 开头、以扩展名 mp4 结尾的文件,比如 eg-01.mp4eg=02.mp4……Powershell 的管道(pipeline)操作符 | 选择了输出结果的 Name 字段(即文件名),然后将其逐行添加前缀与后缀,最后写入到一个 txt 文件(本例命名为 mylist.txt)中:

file 'eg-01.mp4'
file 'eg-02.mp4'
...

当然,如果视频文件数量不多,用户也可以自行创建这样一个 mylist.txt 文件。在执行完第二行的 ffmpeg 指令进行视频合并后,用户可以删除文件夹中的 mylist.txt 文件。

替换或删除视频数据流

除了格式转换中提到的提取流的操作,删除或替换也是常见的选择。

删除流

利用 -vn/-an/sn/-dn 参数可以跳过视频/音频/字幕/数据流,比如一段没有音频流的视频:

ffmpeg -i video.mp4 -c:v copy -an NoAudio.mp4

上例中的 -c:v 是传递视频编解码器, copy 表示不进行编解码操作而是直接拷贝。

替换流

替换流的常用场景是将一段音频替换原视频中的音频流:

ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -map 0:v:0 -map 1:a:0 out.mp4
# 或者省略第二冒号
ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -map 0:v -map 1:a out.mp4

这里输入了两个文件。视频流将直接复制。复制对象由 -map``手动指定了,其后的 ``0:v:0 表示指定第0个输入文件(即 video.mp4)的视频流,在处理后作为输出文件的第0个视频流(单个文件可以有多个视频流)。类似地,-map 1:a:0 表示指定第1个输入文件(即 audio.mp3)的音频流,在处理后作为输出文件的第0个音频流。由于此例中输出的视频不存在多个同类流,因此第二个冒号可以省略。

不使用 -map 手动指定时,FFmpeg 会自动选择:

  • 输入文件的所有视频流(一个文件可能有多个流)中分辨率最高的。
  • 输入文件的所有音频流中声道数最多的。
  • 输入文件的所有字幕流中最靠前的。注意:如果字幕流是图像型而不是文字型的,需要显式地指定 c:s 参数。比如,如果 video.mkv 的字幕流是图像型的,那么下例中的 out1.mkv 不含字幕流(因为默认的 MKV 字幕流编码器只接受文字型字幕流),而 out2.mkv 则包含字幕流(因为 dvdsub 用于图形型字幕流):
ffmpeg -i video.mkv out1.mkv -c:s dvdsub out2.mkv

压制

注解

在大多数压制场合,CRF都是更受欢迎的,也是保持画面质量的一选。如果要严格限制文件大小,那么就使用二压;如果要严格限制视频码率,才会考虑使用定限码率压制(或者二压)。

视频的压制主要有 CRF(Constant Rate Factor,恒定率系数)与二压(2Pass)两种常用的方法,以及定限码率压制这种相对不常用的方法(不太推荐):

  • 在编码器 libx264/libx265 中(vp9等其他编码器中的情形并不同)
  • CRF 为 0 表示无损,51 表示 FFmpeg 所能达到的最差效果。
  • 如果设置一个小于默认值 23 的值,那么输出视频的画面会(从视觉观感上)保留较好的效果,但同时文件的体积也较大;如果设置一个大于 23 的值,那么输出的视频大小会被压缩,但会在画面观感上有一定损失。
  • 对于 H.264 编码,CRF 在 17 左右时,输出的视频损失就非常小了,因此选择比 17 更小的 CRF 意义不大;类似地,CRF 如果低于 28,其效果相比于原视频可能就会出现明显的损失,因此通常也不建议选择大于 28 的数值,
  • 二压(2Pass)是需要生成固定大小文件时的压制方法,顾名思义,需要编码两次(因此较慢)。用户可能需要自行计算视频码率限值。
  • 定限码率(Limited bitrate)压制是仅在网络上传有严苛要求时才使用的方法,并不是画面质量的第一选择。

恒定率系数(CRF)

CRF 的压制中还有一个参数,称为预案 -preset 。较慢的预案能够更好地发挥压制的效果,按压制后质量从低到高分为 ultrafast , superfast , veryfast , faster , fast , medium , slow , slower , veryslow 这9种。预案越慢,压缩效果(指视频质量与文件体积之比)越好,或者说同等视频质量下输出文件的体积越小。

下例中使用了 slow 预案来进行压制,即期望得到较好的压缩效果。视频编解码器设置为 libx264,设定了一个恒定率系数优于默认的 CRF 值(设定的20比默认的23小,即效果优于默认转码),并对音频流进行复制:

ffmpeg -i video.mp4 -c:v libx264 -preset slow -crf 20 -c:a copy out.mp4

编码器 libx264 还提供了一个 -qp 参数,即量化参数(Quantization Parameter)。它可以取 -1 以上的整数值(默认值 -1 表示自动)。简单地理解,CRF 就是自动根据画面中运动的多与少来调整 QP ,来达到好的压缩效果。通常情况下,用户都应当选择 CRF,而不是 QP 参数。

二压(2Pass)

注解

通常只在强制要求文件大小时使用二压。

设想一个二压的应用场景(本例取自* FFmpeg Wiki ):需要将一个10分钟(600秒)长的视频压制到200MB,并保持音频码率在 128 kbps。

首先计算压制后视频流的比特率值。1 MB = 8192 kbit,下式的第一项即为总文件的比特率值,减去第二项音频流的比特率值,就得到了视频流的比特率值:

FFmpeg实用教程_缩放_02

在上式的 2602 kbit/s 的基础上留一定余量,设置为 2600 kbit/s:

# 对于 H.264 二压,使用 -pass 参数。请注意首行行尾的续行。
ffmpeg -y -i video.mp4 -c:v libx264 -b:v 2600k -pass 1 -an -f null NUL && `
ffmpeg -i video.mp4 -c:v libx264 -b:v 2600k -pass 2 -c:a aac -b:a 128k out.mp4

# 对于 H.265 二压,则应使用 -x265-params 参数。同样,请注意首行行尾的续行。
ffmpeg -y -i video.mp4 -c:v libx265 -b:v 2600k -x265-params pass=1 -an -f null NUL && `
ffmpeg -i video.mp4 -c:v libx265 -b:v 2600k -pass 2 -c:a aac -b:a 128k out.mp4

大部分参数比较好理解,需要说明的是这几个参数:

  • y 是一个全局参数,表示覆盖文件时不询问。
  • NUL 表示二压的第一步不输出,而行尾的符号表示续行。
  • 如果使用 CMD 而不是 Powershell,请使用 ^ 代替首行行尾的续行符。
  • 在 Linux 系统上,请使用 /dev/null 代替 NUL,并使用 \ 代替首行行尾的续行符。
  • an 表示忽略音频流。同理还有 vn/sn/dn

定限码率压制

定限码率压制并不考虑文件大小,而是只限制文件码率;这多见于网络上传视频(或者流媒体传输受到网络条件限制)的场合(参考 FFmpeg - Limiting the output bitrate)。以 libx264/libx265 编码器为例,有以下几种码率限制参数:

  • b:v 目标平均码率,也即希望得到的输出文件的平均码率(单位 bit/s)。该参数也在二压中被使用。
  • 值得说明的是,输出视频的码率总是大于指定的平均码率的。这是由于容器本身还需要记录元数据等内容(可能占用数百 KB),因此我们总是需要对传入码率参数进行调低。这个码率超出问题在压制短时长视频压制时比较明显,请特别注意。

  • maxrate 最大码率,需要与 bufsize 参数同时使用。
  • minrate 最小码率。这个较少使用。

只给定平均码率 -b:v 是一种比较粗糙的码率控制方法。正如上面所说,它会使得输出文件的码率总是略高于指定值。

ffmpeg -i input -c:v libx264 -b:v 8M output.mp4

相对的,利用最大码率参数 -maxrate 与缓冲区参数 -bufsize 可以更严格地控制码率上限。它会完成一段缓冲区大小就检验一次码率是否符合要求,因此在缓冲区设置上也存在一些技巧。通常,我们将缓冲区设置为与码率值相同。你也可以增大缓冲区,直到发现码率输出开始大幅度高于或低于目标值的临界点,然后以略低于该临界点的值作为缓冲区大小;当然,这需要更多的时间去尝试。

ffmpeg -i input -c:v libx264 -b:v 8M -maxrate 8M -bufsize 8M output.mp4

添加章节信息

FFmpeg 支持在混流时向视频文件中写入元数据;这其中最实用的大概是章节(chapter)跳转的元数据,它得到了许多主流播放器的支持(例如 MPV)。

元数据需要存放在一个外部文件中,并遵循类似 ini 文件的格式。下面是官方文档 Metadata - FFmpeg 页面给出的例子:

;FFMETADATA1title=bike\\shed
;this is a commentartist=FFmpeg troll team

[CHAPTER]TIMEBASE=1/1000
START=0
#chapter ends at 0:01:00END=60000
title=chapter \#1

[CHAPTER]TIMEBASE=1/1000
START=60000
#chapter ends at 0:02:00END=120000
title=chapter \#2

[STREAM]title=multi\
line
  • 文件一般在首行含有标记头,以分号开启。上例中的标记头是 FFMETADATA,版本是 1
  • 元数据以键值对 key=value 的形式书写,每对占一行。注意,等号两侧的空格会被保留,因此不推荐留空格。
  • 特殊字符
  • 文件中的行如果以 ; 或 # 开头,则表示注释。
  • 如果键值对中的值含有字符等号 =,反斜线 \,或者注释符号 ; 或 #,那么必须在它们前面加上一个额外的反斜线来转义。
  • 元数据可以按章(chapter)或者流(stream)分节,每一节的名称需要 大写
  • 每一节内可以指定一个 TIMEBASE=n1/n2 键,表示章节起止时刻的时间单位,其中 n1 与 n2 都必须是正整数。上例中的 1/1000 表示千分之一秒(即毫秒),因而 END=60000 表示第一章在第 60000 毫秒也即第 60 秒处结束。如果未指定 TIMEBASE 键的值,那么会默认使用纳秒(十亿分之一秒,即 10−9 秒)。

要将上述元数据(例如存储在 FFMETA.ini 中)写入到视频文件,使用:

ffmpeg -i video.mp4 -i FFMETA.ini -map_metadata 1 -c copy out.mp4

其中的 1 表示将第二个(因为从0开始索引)输入文件,即第二个 -i 之后的参数值 FFMETA.ini 映射为元数据。

更正色彩空间*

以我们最常用的 BT.709 色彩空间为例,许多视频播放器必须检测到色彩空间的元数据信息为 BT.709 才能正常地播放,否则可能引起偏色等问题。如果一个视频在播放时发生了色彩异常(通常发生于在不同设备、屏幕上播放时),那么我们需要检查视频的色彩空间信息并进行修正。

要检查一个视频的现有色彩空间信息,可以使用 FFmpeg 安装时附带的 ffprobe 工具:

ffprobe -v quiet -show_streams -select_streams v:0 -i video.mp4 | select-string "color"

以上是 Windows 平台 Powershell 的示例,在 Mac/Linux 平台可以将 select-string 换为 grep 命令。上述命令会返回类似的输出结果:

color_range=tv
color_space=bt709
color_transfer=unknown
color_primaries=bt709

以普通的 SDR 视频为例,上述就是一个正常的输出(因为 color_transfer 默认跟随了其他两项设置;如果是 HDR 视频,则该参数取值可能不同)。其中, color_range 也可能是 tv/limited 或者 pc/full。一般而言,只要视频中的 color_space 与 color_primaries 已赋值(即不是 unknown),那么视频在播放时就能够正常地还原色彩。

如果发现上述值有较多 unknown,可以在重新编码视频的过程中指定正确的色彩空间。下例对视频用 H.265 进行了重编码并指定 BT.709 色彩空间,建议指定一个 CRF 值或者码率(此处为 8Mb/s):

ffmpeg -i "video.mp4" -colorspace bt709 -color_trc bt709 -color_primaries bt709 -c:v libx265 -b:v 8M -c:a copy "colorspace.mp4"

关于更多色彩空间的参数信息,可以参考 FFmpeg 官方文档的 setparams 这一节。

显卡硬件加速*

FFmpeg 支持显卡硬件加速;本节主要以 Nvidia 的显卡与 H.264 编码方式为例展示一些用法。

硬件支持

关于用户当前的显卡支持哪些编码格式的硬件加速,可以参考 Nvidia 给出的一张表格: Video Encode and Decode GPU Support Matrix。简要来说,大概是:

  • 大多 Maxwell 一代显卡(GTX 745/850/850M/960M 及同代更高型号)支持完整的 H.264 编码硬件加速
  • Maxwell 二代(GTX 750/950/965M 及同代更高型号)还支持 4K YUV 4:2:0 的 H.265 编码硬件加速
  • 大多 Pascal 显卡(GTX 1050 及同代更高型号)及之后架构的显卡,都支持完整的 H.265 编码硬件加速
  • 较新的显卡对于其他主流的编码格式,如 VP9 等,也有硬件加速支持

FFmpeg支持

由于 FFmpeg 存在众多的编译版本,用户正使用的不一定包含了硬件加速功能。但官方提供的预编译版本均涵盖了该功能,本文也不再对如何在编译时引入硬件加速支持进行介绍。

显卡加速使用特殊的编码器(而不是 CPU 编码时的标准编码器),它们通常以 nvenc (或者 cuvid )结尾。用户可以使用 -codec 来查找当前安装的 FFmpeg 是否在编译时添加了这些编码器的支持。下面是我的古董级 GTX 960M 机器返回的信息,例中可以看到对 H.264 解码器支持 h264_cuvid 、编码器支持 h264_nvenc

# Windows Powershell 用户:ffmpeg -codecs | select-string nvenc
ffmpeg -codecs | grep nvenc
...
DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_qsv h264_cuvid ) (encoders: libx264 libx264rgb h264_amf h264_mf h264_nvenc h264_qsv nvenc nvenc_h264 )
DEV.L. hevc                 H.265 / HEVC (High Efficiency Video Coding) (decoders: hevc hevc_qsv evc_cuvid ) (encoders: libx265 nvenc_hevc hevc_amf hevc_mf hevc_nvenc hevc_qsv )

以 h264_nvenc 编码器为例,说明几个注意点:

  • 编码器 h264_nvenc 使用与常规编码器 libx264 不同的 preset 参数选项,可以通过如 ffmpeg -h encoder=h264_nvenc 的命令查看。
  • 编码器 h264_nvenc 不支持 CRF 参数控制压制质量,用户需要使用其他的参数,比如粗糙的 qp 参数,或者 rc 参数来指定码率控制模式并配合其他参数(例如 b:v 参数)。
  • 关于该编码器的详细帮助,可以参考 ffmpeg -h encoder=h264_nvenc 命令给出的参数列表。

硬件加速命令

硬件加速有混合模式(CPU 与 GPU 共同工作)与独占模式(完全 GPU 工作)两种。

混合模式直接指定编码器为支持硬件加速的编码器即可,比如 h264_nvenc

# CPU+GPU 混合模式
ffmpeg -i video.mp4 -c:v h264_nvenc -c:a copy out.mp4

独占模式需要指定额外的输入参数 -hwaccel 与 -hwaccel_output_format 的值为 cuda,表示启用 cuvid 解码器与 nvenc 编码器。

# GPU 独占模式
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i video.mp4 -c:v h264_nvenc -c:a copy out.mp4

上述命令会自动以 2000 kbps(即 2Mbps)左右的总文件比特率(视频、音频多轨综合,因此单独看视频码率可能会略高于 2M )来压缩视频。

硬件加速下的质量控制

由于 h264_nvenc 编码器不支持 CRF 参数,我个人的习惯是通过 -rc 参数来设置 vbr_hq 可变码率模式,并手动指定 -b:v 视频码率的数值。例如下述命令使用可变码率模式,并将视频设置在 2Mbps 附近:

ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i video.mp4 -c:v h264_nvenc -rc vbr_hq -b:v 2M -c:a copy out.mp4

在此基础上,用户还可以配合 -maxrate 来限制最大码率、用 -bufsize 来调整缓冲区大小(缓冲区越小,码率波动越小)、用 rc_lookahead 来设定前览帧数等。用户可以参考 FFmpeg Wiki - Limiting the output bitrate 页面。

另一种方式是使用 -cq 参数。默认的硬件加速结果 q 值(据笔者测试)大约在 25 左右,用户可以通过稍微调高该值来获得压缩效果,例如:

ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i video.mp4 -c:v h264_nvenc -rc vbr_hq -cq 28 -qmin 28 -c:a copy out.mp4

其中 -qmin 参数能够限制最小的 q 值(控制质量上限,减小文件体积);类似的还有 -qmax 参数,只不过作用相反。作为参考,笔者使用该命令来转码一个 2250Kbps 视频码率、时长6分钟的 99M 大小的视频文件,得到了 1814Kbps 的大小为 80M 的输出结果。

要查看更多 FFmpeg 硬件加速的内容,比如对 AMD 等硬件的支持,请查看 HWAccelIntro 页面。

转自:https://www.aiexplorer.blog/article/ffmpeguse