神器FFmpeg的使用

以前进行视频转换,用这个那个工具,最后发现神器FFmpeg太强大了。FFmpeg参数众多,以下记录ffmpeg的一些用法。

1.FFmpeg简介

以下内容来自维基百科 > FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了libavcodec——这是一个用于多个项目中音频和视频的解码器库,以及libavformat——一个音频与视频格式转换库。

2. FFmpeg命令行工具组成

FFmpeg命令行工具包含以下三个基本工具: 1) ffmpeg:视频文件转换命令行工具,支持经过实时电视卡抓取和编码成视频文件; 2) ffserver:基于HTTP、RTSP用于实时广播的多媒体服务器。也支持时间平移; 3) ffplay:用 SDL和FFmpeg库开发的一个简单的媒体播放器;

3. FFmpeg的下载

FFmpeg的下载地址:FFmpeg下载 ,一般下载static版本,解压后设置环境变量即可使用。

4. FFmpeg命令格式

FFmpeg工具的基本格式为:

1
ffmpeg [global_options] [[infile options] -i infile]... {[outfile options] outfile}...
其中,-i 参数表示输入参数;之后的参数是输出参数。

ffmpeg 的一般工作流,是从源文件开始,依次经过分流器、解码器、编码器、混流器,最后完成输出文件。下图是官网给出的示意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 _______              ______________
| | | |
| input | demuxer | encoded data | decoder
| file | ---------> | packets | -----+
|_______| |______________| |
v
_________
| |
| decoded |
| frames |
|_________|
________ ______________ |
| | | | |
| output | <-------- | encoded data | <----+
| file | muxer | packets | encoder
|________| |______________|

一些基本的概念: - 流(stream):视频文件中,一般具有视频流与音频流,有的具有字幕流。它们需要不同的解码/编码器。想要混合/分离视频与音频,就需要混流/分流器。 - 流复制(stream):如果某种数据流的内容不需要任何改动,那么可以直接跳过该数据流的解码与编码步骤。分离出该数据流后,直接等待参与混流即可。

ffmpeg 在有多个流的情况下,不会全部保留;默认只会选择同类流中质量最佳的。如果质量同样,那么选择索引号靠前的流。你可以选择手动控制流的选择,这需要额外的参数,我们下文介绍。

5. 主要参数

下表中: 1. 以 <> 包裹的表示由用户具体指定;以 [] 包裹的表示是可选参数,可以指定也可以省略。 2. 括号内的 i 表示该参数用于输入流,o 表示用于输出流,i/o 表示均可,global 表示全局参数。 3. 关键字 duration, positionoffset 满足:[-][HH:]MM:SS[.m...] 这种时间戳格式。或者以秒为单位的 SS[.m...] 格式。

常用的如下: - -b[:stream_specifier] (o):输出比特率。 - -f <fmt> (i/o):指定 fmt 作为输入或输出的视频格式。一般会根据文件扩展名自动选择,但有时需要手动指定。
- -i <filename> (i):指定 filename 作为源文件。
- -y (global):文件存在时直接覆盖。
- -n (global):文件存在时不覆盖并立即退出。
- -bsf[:stream_specifier] <bitstream_filters> (o):设置比特流滤镜。bitstream_filters 是一个逗号分隔的滤镜列表。 - -stream_loop <num> (i):指定输入流的循环次数。0 表示不循环,-1 表示无限循环。
- -c[:stream_specifier] <codec> (i/o)-c可写为-codec。选择一个 codec ,即编码器(输出时)或一个解码器(输入时),参与到 stream_specifier 指定的一个或多个流的编码/解码中。在输出时,<codec> 可以被指定为 copy,表示复制数据流。
- -t <duration> (i/o):(在参数 -i 之前指定)工作持续 duration 时长。一般用于指定剪辑数据流的范围。 它与 -to 参数相互冲突,但本参数优先。
- -to <position> (o):到 position 位置后,终止输出。与 -t 参数冲突,本参数优先级低。 - -fs <limit_size> (o):输出文件大小达到 limit_size 后停止输出,单位是 byte.
- -ss <position> (i/o):(在参数 -i 之前指定)从 position 指定的位置开始工作。注意:大多数情形下,工作起始位置是不精确的。ffmpeg会找到其前部的一个点作为真正的起始,并在结束工作后将该点与用户指定点之间的内容抛弃。然而,如果你使用了 copy 参数,这部分内容却会被保留。
- -sseof <position> (i/o):类似 -ss 参数,只不过是从数据流末端向前寻找 position。此时 0 表示数据流末。
- -itsoffset <offset> (i):指定输入流以原时间戳加上 offset 作为其输入时间戳。
- -metadata[:metadata_specifier] key=value (o):以键值对的形式设置元数据。
- -frames[:stream_specifier] <num> (o):在输出 num 帧后停止写入。 - -qscale[:stream_specifier] q (o):使用固定质量(VBR)。
- stats (global):输出编码过程,是系统默认值。可以使用 -nostats 关闭。
- -attach <filename> (o):将 filename 文件附加到输出文件。附件流作为文件的最后一个流,只有很少的文件类型被支持(例如字体)。

6. 视频参数

  • -vframes <num>:文件的总帧数。-frames:v 的别名。
  • -r[:steam_specifier] <fps> (i/o):文件的帧率。
  • -s[:steam_specifier] <size> (i/o):帧尺寸。参数 size 需要满足格式 <width>x<height>,例如320x240。 -aspect[:steam_specifier] <asp> (o):宽高比,例如 4:3。如果使用了 -vcodec copy,那么指定容器的宽高比而不是视频的。
  • -vn (o):禁止输出视频。
  • -vcodec <codec> (o):设置视频编码器。-codec:v 的别名。
  • -pass[:stream_specifier] <n>:选择当前编码数(1或者2),常用于二次编码的情况。在第一次编码中,音频输出往往被设置为 NULL,对于 Windows 与 Unix 系统分别是:
    1
    2
    ffmpeg -i foo.mov -c:v libxvid -pass 1 -an -f rawvideo -y NUL
    ffmpeg -i foo.mov -c:v libxvid -pass 1 -an -f rawvideo -y /dev/null

7. 音频参数

  • -aframes <num> (o):文件的总帧数。-frames:a 的别名。
  • -ar[:stream_specifier] <freq> (i/o):采样率。默认输出等于输入。仅当输入文件为真实设备或者 raw 数据时,该参数才能用于输入过程。
  • -aq <q> (o):音频品质(VBR)。 -q:a 的别名。
  • -ac[:stream_specifier] <channel> (i/o):设置音频通道数。默认输出等于输入。仅当输入文件为真实设备或者 raw 数据时,该参数才能用于输入过程。
  • -an (o):禁止输出音频。
  • -acode <codec> (i/o):设置音频的解码器或编码器。-codec:a 的别名。

8. 字幕参数

  • -scodec <codec> (i/o):字幕解码器或编码器。codec:s 的别名。
  • -sn (o):禁止输出字幕。
  • canvas_size <size>:设置字幕渲染区域的尺寸。

9. 其他参数

以下直接在 ffmpeg 后使用,例如:ffmpeg -version

  • -bsfs:可用的比特流滤镜。
  • -h [arg]:帮助。arg 的内容可以是:
    • decoders:可用的解码器。或特指:decoder=<name>
    • encoders:可用的编码器。或特指:encoder=<name>
    • filters:所有滤镜。或特指:filter=<name>
    • formats:可用的分流器与混流器。或特指分流器:demuxer=<name>,或特指混流器:muxer=<name>
  • -protocols:支持的协议。
  • -version:版本信息。

10.FFmpeg 实用例子

10.1 合并视频

参考:FFmpeg Wiki - Concatenate

第一种方案:将这几个视频放在一个新文件夹内,Shift 右键运行 cmd,输入(注意:如果要保存为批处理文件,请循环变量的双写百分号。):

1
2
(for %i in (*.flv) do @echo file '%i') > mylist.txt
ffmpeg -f concat -i mylist.txt -c copy output.flv
这样速度很快也没有中间文件,原则上要求文件规格相近。

另一种方案:先将这几个视频无损地转为 mpegts 文件,再通过 concat 协议合并。以常见的 H.264 视频与 aac 音频为例:

1
2
3
ffmpeg -i "1.flv" -c copy -bsf:v h264_mp4toannexb -f mpegts 1.ts
ffmpeg -i "2.flv" -c copy -bsf:v h264_mp4toannexb -f mpegts 2.ts
ffmpeg -i "concat:1.ts|2.ts" -c copy -bsf:a aac_adtstoasc "All.mp4"

这种方案是早期方案,支持更广,包括非 mpeg 容器的 mpeg 编码内容(H.264, MPEG4, MPEG2, AAC, MP3等)。不过这样会产生中间文件。

10.2 分割视频

指定视频的起始与持续时长就可以分割视频了。下例截取了视频的前 5 秒(00:05:00),注意-t后接“截取视频段长度”而不是“截取终点时刻”:

1
ffmpeg -i "input.mp4" -ss 00:00:00 -t 5 -c copy "output.mp4"

建议使用规范的 mp4 格式文件,否则可能出现视频无法正常混流的现象。

10.3 批量格式转换

比如,对于数据流用 mpeg 编码的一个 flv 文件,可以这样转为 mp4 文件:

1
ffmpeg -i "input.flv" -c copy "output.mp4"

因此一个批量转换也很容易通过 for 语句实现(%~n 表示保留不含扩展名的文件名):

1
for %i in (*.mp4) do ffmpeg -i "%i" -c copy "%~ni.flv"

10.4 静态图水印

下例添加 png 或其他静态格式的水印,放置在距左侧 20 像素,距顶端 40 像素的地方。水印与视频的基准点都是左上角点。

1
ffmpeg -i input.mp4 -i wm.png -filter_complex "overlay=20:40" output.mp4

如果要放在右下角使用overlay= main_w-overlay_w:main_h-overlay_h,参数的含义应该较好理解。

如果要指定水印的大小,比如 384x216:

1
ffmpeg -i input.mp4 -i wm.png -filter_complex "[1:v]scale=384:216[wm];[0:v][wm]overlay=0:0" output.mp4

参数 0:v 表示第1个输入的视频流(本例即input.mp4的视频流),1:v 表示第2个输入的视频流(本例即wm.gif)。分号前的[wm]用于引用。

10.5 GIF水印

添加 gif 水印与静态图水印有一些不同之处: - 需要将 ignore_loop 参数指明为 0,表示 gif 无限循环。 - 需要用到复合过滤器 filter_complex。 - 需要过滤器的 shortest=1 选项,表示至少在一个视频流循环一次后,再终止输出。如果不加该选项,输出将无法自行停止。

一个指定 50x50 大小 GIF 水印在左上角的例子:

1
ffmpeg -y -i input.mp4 -ignore_loop 0 -i wm.gif -filter_complex "[1:v]scale=50:50[wm];[0:v][wm]overlay=0:0:shortest=1" output.mp4

10.6 外挂字幕

将字幕作为单独的数据流(而不是混入视频流中),封装到容器内。一般对此特性有良好支持的容器是 mkv。在封装时,一般需要转为 ass 格式。

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

一些注意点: - 字幕文件请用 UTF-8 编码。 - Windows 系统缺少一个字体接口,因此需要自己配置 fonts.conf 文件,放在 %FONTCONFIG_PATH% 这个环境用户变量里(往往需要你自己新建)。该变量应该指向C:\Users\用户名\

网上流传了一份 fonts.conf 文件内容(见附录),请复制后粘贴到你对应文件夹的 fonts.conf 文件中。

10.7 内嵌字幕

在播放器不支持独立字幕流的场合,需要将字幕混入视频流中(因此需要重编码)。

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

如果字幕以字幕流的形式位于一个视频文件中,可以直接调用:

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

如果希望在嵌入字幕时保留以前多个音轨,可以使用:

1
2
3
4
5
6
ffmpeg -i "input.mp4" -map 0:0 -map 0:1 -map 0:2 -vf subtitles=input.Chs.srt output.mp4

# 一个实际的例子

for I in `ls ../*.mp4`;do ffmpeg -y -i "../$I" -map 0:0 -map 0:1 -map 0:2 -vf subtitles=../${I%.*}.Chs.srt "${I##*/}";done

同样,Windows 用户需要配置 fonts.conf 文件(参加附录11)。

10.8 将多个图片制作为视频

使用Python绘制了一系列顺序命名的图像,现在将其转为视频:

1
ffmpeg -r 25 -f image2 -s 1200x800 -i result_png/epo_%05d.png -vcodec libx264 -crf 25  -pix_fmt yuv420p test.mp4
result_png/epo_%05d.png表示 result_png目录下的epo_xxxxx.png文件,xxxxx0000099999-r 帧率(fps); -crf 画质,越低表示质量越好,通常 15-25 已经不错了; -s 解析度 -pix_fmt yuv420p指定pixel format, 根据需要修改 输出文件名为 test.mp4。

10.9 截取视频片段

自开始时间(-ss参数,格式为HH:MM:SS)到结束时间(-to参数,格式为HH:MM:SS)截取视频,

1
ffmpeg -ss 00:02:40 -i "old_file.mp4" -to 00:43:24  -vcodec copy -acodec copy "new_file.mp4"
当需要获取视频时长时,可以使用ffprobe 文件名进行检测。Python脚本参见博文链接

10.10 下载m3u8视频流

用法比较简单,如下所示,该命令有时无法下载视频:

1
ffmpeg -i http://...../abc.m3u8 "输出文件名.mp4"

11. 附录:Windows 的 fonts.conf

参考页面:该用户的 Github

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?xml version="1.0"?>
<fontconfig>

<dir>C:\WINDOWS\Fonts</dir>

<match target="pattern">
<test qual="any" name="family"><string>mono</string></test>
<edit name="family" mode="assign"><string>monospace</string></edit>
</match>

<match target="pattern">
<test qual="all" name="family" compare="not_eq"><string>sans-serif</string></test>
<test qual="all" name="family" compare="not_eq"><string>serif</string></test>
<test qual="all" name="family" compare="not_eq"><string>monospace</string></test>
<edit name="family" mode="append_last"><string>sans-serif</string></edit>
</match>

<alias>
<family>Times</family>
<prefer><family>Times New Roman</family></prefer>
<default><family>serif</family></default>
</alias>
<alias>
<family>Helvetica</family>
<prefer><family>Arial</family></prefer>
<default><family>sans</family></default>
</alias>
<alias>
<family>Courier</family>
<prefer><family>Courier New</family></prefer>
<default><family>monospace</family></default>
</alias>
<alias>
<family>serif</family>
<prefer><family>Times New Roman</family></prefer>
</alias>
<alias>
<family>sans</family>
<prefer><family>Arial</family></prefer>
</alias>
<alias>
<family>monospace</family>
<prefer><family>Andale Mono</family></prefer>
</alias>
<match target="pattern">
<test name="family" compare="eq">
<string>Courier New</string>
</test>
<edit name="family" mode="prepend">
<string>monospace</string>
</edit>
</match>
<match target="pattern">
<test name="family" compare="eq">
<string>Courier</string>
</test>
<edit name="family" mode="prepend">
<string>monospace</string>
</edit>
</match>

</fontconfig>

参考

  1. Using ffmpeg to convert a set of images into a video
  2. FFmpeg命令行