0x11 - FFMpeg libs 编译以及 CLI 使用总结

TL;DR 太长不看我想看官网

FFMpeg 使用已经有一段时间,这里对其作了一些整理,以备不时之需。

  • FFMpeg 官方文档 Link
  • FFMpeg linux pre-built binary Link
  • Ubuntu 简易安装
    1
    apt-get update && apt-get install ffmpeg

从源码编译

如果想用已经编译好的 binary,可以直接使用 apt-get 安装。
或者使用第三方已经编译好的库。
https://johnvansickle.com/ffmpeg/

如果希望自己编译的话可以参考这个链接
https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu

我的编译环境如下

1
2
3
4
5
Ubuntu 18.04
CUDA 10.2
GNU Make 4.1
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
cmake version 3.10.2

源码 & 依赖安装

首先从官网下载 ffmpeg 的源码,这里以 ffmpeg 4.4.3 为例(官方推荐使用 master 分支,请根据需求自行调整)

1
2
3
wget https://ffmpeg.org/releases/ffmpeg-4.4.3.tar.xz

tar -xf ffmpeg-4.4.3.tar.xz

如果需要支持硬件编解码,我们还需安装/编译以下的包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git

cd nv-codec-headers

⚠️ 由于cuda 版本会有不同的地方,请根据自己的情况决定到底应该使用哪个branch
cat README # 查看支持的驱动版本
git branch -a # 查看有哪些 branch
# 这里以 cuda 10.2 为例
git checkout -b sdk-10.0 remotes/origin/old/sdk/10.0

# 很多新版本 ffmpeg 不一定支持 sdk-10.0,届时需要调整这个值,推荐 11 或以上

make
sudo make install

安装一些第三方的依赖包,这部分需要根据具体的需求来,基本依赖和一些编码库是必须的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 仅测试在 ubuntu 18.04 下可以直接安装
apt-get install nasm yasm # 基本依赖
apt-get install libx264-dev # 使之支持第三方 libx264
apt-get install libx265-dev libnuma-dev # 使之支持 libx265
apt-get install libfdk-aac-dev # 使之支持 fdk-aac

apt-get install libharfbuzz-dev
apt-get install libfreetype6-dev # 使之支持 freetype

apt-get install libopenexr-dev

# all in one
apt-get install -y libx264-dev libx265-dev libnuma-dev libfdk-aac-dev libharfbuzz-dev \
libfreetype6-dev libopenexr-dev libtbb-dev

编译过程

紧接着我们写一个 compile_ffmpeg.sh。

大部分情况下这样编译可以满足大部分的需要。
如果需要额外的包或者功能,需要参考官网的说明来添加/安装对应的包。
https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/bin/bash
set -e
INSTALL_PREFIX="/path/to/your/ffmpeg_install_path"

cd ffmpeg-4.4.3
./configure \
--prefix="${INSTALL_PREFIX}" \
--pkg-config-flags="--static" \
--extra-libs="-lpthread -lm" \
--ld="g++" \
--bindir="${INSTALL_PREFIX}/bin" \
--enable-libfreetype \
--enable-libharfbuzz \
--enable-shared \
--enable-gpl \
--enable-libx264 \
--enable-libx265 \
--enable-libfdk-aac \
--enable-ffnvcodec \
--enable-cuda-nvcc \
--enable-cuvid \
--enable-nvenc \
--extra-cflags=-I/usr/local/cuda/include \
--extra-ldflags=-L/usr/local/cuda/lib64 \
--nvccflags="-gencode arch=compute_70,code=sm_70 -O2" \
--enable-nonfree

make -j$(nproc)
make install

exit 0

如果发现有 ERROR: failed checking for nvcc. 的错误
需要添加一条flag,这个需要根据具体的显卡型号决定

1
2
3
--pkgconfigdir="/usr/local/lib/pkgconfig"
--nvccflags="-gencode arch=compute_70,code=sm_70 -O2"
# 需要根据你的真实情况填写

如果不需要硬件编解码,可以把下面这些选项去掉

1
2
3
4
5
6
7
--enable-ffnvcodec \
--enable-cuda-nvcc \
--enable-cuvid \
--enable-nvenc \
--extra-cflags=-I/usr/local/cuda/include \
--extra-ldflags=-L/usr/local/cuda/lib64 \
--nvccflags="-gencode arch=compute_70,code=sm_70 -O2" \

补充说明

编译好以后的包会输出到指定的 --prefix 下,你可以使用 ldd 来确认具体引用了什么包。
ffmpeg 如果要支持硬件加速的话需要确认你的系统里有对应的 so 包,具体来说是以下这两个

1
2
3
ll /usr/lib/x86_64-linux-gnu/ |grep nv
# libnvcuvid.so
# libnvidia-encode.so

FFMpeg CLI 使用说明

这里是 ffmpeg 命令行的一些常用选项说明。

基本使用

1
2
3
4
5
使用 -i 参数
ffmpeg -i ${input} ${output}

使用 -y 参数可以默认跳过覆盖确认
ffmpeg -y -i ${input} ${output}

-vf 参数的简单使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
添加 -vf 选项使其能够控制更多


ffmpeg -i ${input} -vf scale=1080:1920 ${output}

scale 控制分辨率


ffmpeg -i ${input} -vf transpose=1 ${output}

transpose 控制旋转和翻转
0-逆时针旋转90度,垂直翻转。这也是默认设置。
1-顺时针旋转90度。
2-逆时针旋转90度。
3-顺时针旋转90度,垂直翻转。


ffmpeg -i ${input} -vf reverse ${output}

reverse 视频倒放

...
还有一些其他的使用方式,这里列出一些其他 -vf 后的使用参数

pad={new_width}:{new_height}:{position_x}:{position_y}:{color}
e.g. -vf pad=1920:1080:12:30:white
生成一个 1920x1080 的画布,将原视频贴在 x=12 y=30 的位置(对齐左上角),其它部分补白色


crop={width}:{height}:{x}:{y}
e.g. -vf crop=500:400:210:220
以 x=210 y=220 为左上角点,裁出 500x400 的位置


多个 vf filter 可以按照处理的先后顺序相连,排列成一个进行处理

e.g.
ffmpeg -i {input} \
-vf pad=1920:1080:12:30:white,crop=500:400:210:220,scale=300:300 \
{output}

先 padding,再裁切,最后 scale

编解码器控制

1
2
3
4
5
6
7
8
-c:v / -vcodec
-a:v / -acodec
视频编码、音频编码

如需视频硬解,则可以参考这个参数
-c:v h264_nvenc

ffmpeg -i ${input} -c:v h264 ${output}

清晰度设置

1
2
3
4
5
6
7
8
9
10
11
12
有一些编码器会带有编码清晰度设置
直接使用默认设置可能不够清晰

-crf 15 设定 crf,在 15 ~ 18 之间可以保证视觉无损
仅在使用 h264 或者 libx264 有效,h264_nvenc 无效

在使用 h264_nvenc 的时候可以考虑使用 preset,level,qp 等其它参数控制

-b:v 设定视频码率 1080p 4M ~ 8M 左右为合理区间
-b:a 设定音频码率 一般不作设置

ffmpeg -i ${input} -c:v h264 -crf 15 ${output}

色彩格式设置

1
2
3
4
更改视频的色彩空间需要指定
-pix_fmt bgr

ffmpeg -i ${input} -pix_fmt yuv420p ${output}

逐行扫描 / 隔行扫描

一般来说不会用到这个选项,
如果有出现需要从隔行转为逐行的情况的话,可以使用以下的选项。

1
2
e.g. 1080i to 1080p
ffmpeg -i <input> -deinterlace <output>

如果要查看视频是否已经是隔行或是逐行扫描了,则需要用到 mediainfo。

1
mediainfo some_video.mp4

时间操作

1
2
3
4
5
6
7
8
9
10
截取时间段
-ss 起始时间
-t 持续时间
-to 到某一时间

比如截取 10 ~ 30 s 这段时间,以下写法均可

ffmpeg -i ${input} -ss 10 -to 30 ${output}
ffmpeg -i ${input} -ss 10 -t 20 ${output}
ffmpeg -i ${input} -ss 00:00:10 -to 00:00:30 ${output}

帧率控制

1
2
使用 -r 参数
ffmpeg -i ${input} -r 25 ${output}

倍速播放

1
2
3
4
5
6
7
设置 PTS 和 atempo 即可,不同的倍速要设置不同的倍率,下面是2倍速的例子
# atempo = 1 / PTS

ffmpeg -i ${input} \
-filter_complex "[0:v]setpts=2*PTS[v];[0:a]atempo=0.5[a]" \
-map "[v]" -map "[a]" \
${output}

音视频轨道的概念

除了特殊的视频以外,一般来说一个视频有一个音频轨道和一个视频轨道。
可以通过设定这些轨道达到一些比较复杂的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用 -map 参数可以控制对应的输入输出

将 第 0 个 文件的 视频轨道 映射到 第 0 个 输出文件上
-map 0:v:0

将 第 1 个 文件的 音频轨道 映射到 第 0 个 输出文件上
-map 1:a:0


提取视频 A 的视频和视频 B 的音频,输出
ffmpeg -i A.mp4 -i B.mp4 -map 0:v:0 -map 1:a:0 output.mp4


将 视频/音频 指定为空可以提取纯音频/视频
-vn 指定空视频
-an 指定空音频

ffmpeg -i A.mp4 -an output.mp4
ffmepg -i A.mp4 -vn output.wav

其它的补充操作

有时候我们需要查看 ffmpeg 的一些编解码器信息。

1
ffmpeg -codecs

使用 grep 选取对应的编解码器以后可以判断是否支持 GPU 硬件加速。
同时,如果希望使用 GPU 加速,通用的做法是指定硬件加速和指定特定的编码/解码器。

1
2
在省略号处补充别的参数
ffmpeg -hwaccel cuvid -i ${input} ... -c:v nvenc_h264 ... ${output}

🌰 FFMpeg CLI 的例子

由于 ffmpeg 的功能比较复杂,不可能面面俱到,上述是一些简单的用法指南。
一些复杂的操作还需要使用 filter_complex 这个参数来设置。
如果一一介绍篇幅很长,比较建议去查看官方文档。

这里介绍一些我自己会用到的例子,大伙可以参考一下

合并 Alpha 通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
INPUT=$1         # 原始视频
ALPHA=$2 # alpha 通道视频
COLOR=78FF78 # 生成视频的背景颜色 e.g FFFFFF 白色
SIZE=$3 # 视频大小 e.g 1920x1080
OUTPUT=$4 # 输出文件

# 一般来说,ffmpeg 内 [aa] 这种表示的是某一轨道的输入/输出

ffmpeg -i ${INPUT} -i ${ALPHA} \
-filter_complex "color=s=${SIZE}:c=#${COLOR}[bg];[0:v][1:v]alphamerge[b1];[bg][b1]overlay=format=auto:shortest=1[b2]" \
-map [b2] \
-y \
-r 25 \
-c:v h264 \
-crf 15 \
-shortest \
${OUTPUT}

如果合并以后希望用一张图片作为背景的话

1
2
3
4
5
ffmpeg -i origin.mp4 -i alpha.mp4 \
-i path_to_img.png \
-filter_complex "[0:v][1:v]alphamerge[b1];[2:v][b1]overlay[b2]" \
-map [b2] -y -crf 15 -pix_fmt yuv420p \
output.mp4

拆分 Alpha 通道

1
2
3
ffmpeg -i input.mov \
-vf "[0:v] format=rgba, split [T1], fifo, lutrgb=r=minval:g=minval:b=minval, [T2] overlay [out]; [T1] fifo, lutrgb=r=maxval:g=maxval:b=maxval [T2]" \
out.mov

如果发现黑色和白色颜色相反,可以使用另一个

1
2
3
ffmpeg -i input.mov \
-vf "[0:v] format=rgba, split [T1], fifo, lutrgb=r=maxval:g=maxval:b=maxval, [T2] overlay [out]; [T1] fifo, lutrgb=r=minval:g=minval:b=minval [T2]" \
out.mov

从绿底视频中提取 alpha

使用 chroma key 可以比较轻松办到这点。注意 chromakey 后填写的具体数值。
如果边缘效果不佳,可以考虑增加颜色过滤相似度和模糊程度(下面例子中的 0.3 和 0.05)。

1
2
3
4
5
6
INPUT=$1 # 绿幕背景
OUTPUT=$2 # alpha 通道输出

ffmpeg -i ${INPUT} \
-vf "[0:v]chromakey=0x00FF00:0.3:0.05[ckout];[ckout]format=rgba, split [T1], fifo, lutrgb=r=maxval:g=maxval:b=maxval, [T2] overlay [out]; [T1] fifo, lutrgb=r=minval:g=minval:b=minval [T2]" \
${OUTPUT}

时间尺度上合并两个视频

1
2
3
4
5
6
7
ffmpeg -i a.mp4 -i b.mp4 \
-filter_complex '[0:0] [1:0] concat=n=2:v=1 [v]' \
-map '[v]' \
-crf 15 \
concat.mp4

# 如果音频也要合并的话可能需要添加一些额外的针对音频的操作

生成纯色背景

1
2
3
4
color=0x78FF78 # 色彩 RGB 16进制表示
size=1080x1920

ffmpeg -f lavfi -i color=${color}:${size} -frames:v 1 output.png

某些文件头的设置

1
ffmpeg -i input.mp4 -movflags faststart -c copy output.mp4

转换 color_range

从 limited range 转换为 full range

1
2
3
4
5
6
alpha_path=$1

ffmpeg -v error -stats -i ${alpha_path} \
-vf scale=in_range=limited:out_range=full \
-pix_fmt yuv420p -color_range tv -y \
-crf 15 convert_alpha.mp4