FFmpeg 实用命令:从入门到进阶
FFmpeg 的官方文档非常全面,但更像是一本技术手册,不太适合新手入门。 本文将以实际例子为主,由浅入深地介绍 FFmpeg 的常用命令。 即使你没有任何 FFmpeg 使用经验,也能轻松上手。
开始之前:隐藏版本信息
每次运行 FFmpeg,都会在开头打印一堆版本信息。你可以使用 -hide_banner
参数来隐藏这些信息,让输出更简洁。
ffmpeg -hide_banner -L
1. 格式转换:视频变形记
格式转换是 FFmpeg 最常用的功能之一。 它可以将视频文件从一种格式转换成另一种格式,例如从 FLV 转换成 MP4。关于各种常用的视频/音频编码格式,请参考 常用编码格式 。
1.1 转码:重新编码
转码是指将视频文件从一种格式转换成另一种格式,并且重新编码视频和/或音频。
ffmpeg -i video.flv video.mp4
-i
:指定输入文件,后面跟着输入文件的文件名。video.mp4
:指定输出文件,这是命令的最后一个参数。- 注意: 如果文件名包含空格,请用双引号将文件名包裹起来。
- 输入参数 vs 输出参数:
-i
之前的参数称为输入参数,-i
之后的参数称为输出参数。
注意
转码过程可能比较耗时。 如果你只是想改变视频的容器格式,而不需要重新编码,可以使用流复制(见下文)。
指定编码器:
你可以使用 -c
(或 -codec
) 参数显式地指定编码器。
ffmpeg -i video.flv -c:v libx264 -c:a flac video.mkv
-c:v
:指定视频编码器。-c:a
:指定音频编码器。libx264
:H.264 视频编码器。flac
:FLAC 音频编码器。
1.2 流复制:快速转换
流复制是一种更快的格式转换方法。 如果你只是想改变视频的容器格式,而不需要重新编码视频和音频,可以使用流复制。
ffmpeg -i video.avi -c copy video.mp4
-c copy
:表示所有流(视频、音频等)都不进行重新编码,直接复制到新的容器中。- 优点: 速度非常快。
- 缺点: 只能在输出容器支持输入的所有流的情况下使用。
1.3 提取流:音频、字幕我全都要
有时候,你可能只需要提取视频文件中的某一个流,例如音频或字幕。
提取音频:
ffmpeg -i video.mp4 -c:a copy audio.aac
-c:a
:指定音频流。copy
:表示直接复制音频流,不进行重新编码。- 注意: 如果音频流与容器格式不兼容,需要将
copy
改为正确的编解码器,或者删除-c:a copy
让 FFmpeg 自动选择。
提取音频并转码:
ffmpeg -i video.mkv -c:a libmp3lame -q:a 2 audio.mp3
-c:a libmp3lame
:将音频流转换为 MP3 格式。-q:a 2
:指定 MP3 音频的质量。
提取字幕:
ffmpeg -i video.mkv -c:s copy subtitle.srt
-c:s
:指定字幕流。copy
:表示直接复制字幕流,不进行转换。
2. 截取视频:剪刀手爱德华
视频截取可以分为两种:截取视频片段和截取帧为图片。
2.1 截取帧为图片:定格美好
方法一:使用 select
过滤器 (推荐)
# Unix
ffmpeg -i video.mp4 -vf 'select=eq(n\,105)' -vframes 1 extract%03d.png
# Windows
ffmpeg -i video.mp4 -vf 'select=eq(n\\\,105)' -vframes 1 extract%03d.png
-vf
:指定视频过滤器。select=eq(n\,105)
:选择第 105 帧。n
:表示帧序号。eq
:表示等于。- 注意: 帧序号从 0 开始,所以
eq(n\,105)
实际选择的是第 106 帧。
-vframes 1
:只输出 1 帧。extract%03d.png
:输出文件名,%03d
表示三位数的帧序号。
方法二:使用时间戳 (不推荐)
# 假设该视频每秒 30 帧
ffmpeg -ss 00:00:03.50 -i video.mp4 -vframes 1 extract%03d.png
-ss
:指定开始时间戳。- 缺点: 如果时间戳的小数不能除尽,很难准确地选中想要的帧。
- 推荐: 使用
select
过滤器配合帧序号的方法。
2.2 截取视频片段:掐头去尾
场景: 截取 video.mp4 视频的第 2 分钟到第 5 分钟。
方法一:使用 -t
参数 (指定片段长度)
ffmpeg -ss 00:02:00 -i video.mp4 -t 180 cut.mp4
-ss
:指定起始时间戳。-t
:指定片段长度(秒)。- 计算方法: (5 - 2) * 60 = 180 秒。
-t
参数也可以写成00:03:00
的形式,也可以带小数,如180.5
表示 180.5 秒。
方法二:使用 -to
参数 (指定终止时刻)
ffmpeg -ss 00:02:00 -to 00:05:00 -i video.mp4 cut.mp4
-ss
:指定起始时间戳。-to
:指定终止时间戳。- 注意:
-ss
和-to
都可以放在-i
之前(输入参数)或之后(输出参数)。
输入参数 vs 输出参数:
-ss
放在-i
之前(输入参数):- 优点: 检索速度更快,因为根据关键帧来检索。
- 缺点: 在流复制 (
-c copy
) 时,定位可能不精确。
-ss
放在-i
之后(输出参数):- 优点: 定位更精确。
- 缺点: 检索速度更慢,因为逐帧检索。
- 推荐:
- 如果需要精确的定位,使用输出参数。
- 如果速度更重要,可以使用输入参数,但不要使用流复制。
总结:
# 任务:截取视频的第 2 至 5 分钟。
# 1. 可接受起始片段前的额外内容,可能长达数秒 —— 方案 A
# 2. 不可接受上述精度,要求精确到给定时刻最近的关键帧 —— 方案 B
# 3. 不可接受上述精度,要求精确到给定时刻最近的帧 —— 方案 C
# 根据上述问题的回答,选择合适的方案:
# A) 用快速截取(输入参数),配合流复制。该方案截取速度非常快。
## 以 -t 参数指定片段长,或以 -to 参数指定终止时间戳
ffmpeg -ss 00:02:00 -t 00:03:00 -i video.mp4 -c copy cut.mp4
ffmpeg -ss 00:02:00 -to 00:05:00 -i video.mp4 -c copy cut.mp4
# B) 用快速截取,但不能使用流复制,片段会被重编码。截取速度近似于编码等长视频的速度。
ffmpeg -ss 00:02:00 -t 00:03:00 -i video.mp4 cut.mp4
ffmpeg -ss 00:02:00 -to 00:05:00 -i video.mp4 cut.mp4
# C) 用慢速截取(输出参数),片段之前的内容也会被重编码。截取速度极慢。
ffmpeg -i video.mp4 -ss 00:02:00 -t 00:03:00 cut.mp4
ffmpeg -i video.mp4 -ss 00:02:00 -to 00:05:00 cut.mp4
扩展阅读:为什么流复制时使用快速检索,起始时刻会变得不精确?
使用流复制 -c copy
和输入参数 -ss
会导致定位不精确,是因为 -ss
会先快速定位到起始时间戳之前的一个关键帧,然后在编码过程中舍弃多余的视频段。 但是流复制不会进行编码,所以多余的视频段不会被舍弃。 详细解释请参考 FFmpeg 官方文档 中关于 -ss
参数的说明。
3. 分辨率改变:魔法瘦脸
INFO
缩放与裁切总是会重新编码视频,请注意这一点。
3.1 分辨率缩放:变大变小
分辨率缩放可以使用 FFmpeg 提供的视频过滤器 (-vf
参数)。
# 输出到 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
scale
:指定缩放过滤器。w
:指定输出视频的宽度。h
:指定输出视频的高度。iw
:输入视频的宽度。ih
:输入视频的高度。ow
:输出视频的宽度。oh
:输出视频的高度。-1
:表示按原视频宽高比自动计算。
指定缩放算法:
你可以使用 flags
参数指定缩放算法。
- 降分辨率: 使用
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
3.2 裁切:剪掉多余的部分
# 从原视频距左上角横 20、竖 30 的位置,向右下角裁切一个宽 100、高 200 的矩形
crop=w=100:h=200:x=20:y=30
crop=100:200:20:30
# 在视频的正中央进行裁切
crop=100:200
# 也可以使用倍率的写法,用 iw、ih 代表输入视频的宽和高
## 裁切视频的中间 3/5 宽度画面
crop=3/5*iw:ih:iw/5:0
crop
:指定裁切过滤器。w
:指定裁切区域的宽度。h
:指定裁切区域的高度。x
:指定裁切区域左上角的横坐标。y
:指定裁切区域左上角的纵坐标。
例子:
# 裁切 1/6 到 5/6 宽的画面范围,并用 x265 编码器以 CRF 30 的质量来编码
ffmpeg -i video.mp4 -c:v libx265 -crf 30 -vf "crop=2/3*iw:ih:iw/6:0" -c:a copy out.mp4
自动检测裁切区域:
FFmpeg 还支持自动检测裁切区域的参数 cropdetect
,常用于四周有黑色边框的情形。
# 自动检测黑色边框来裁切
ffmpeg -i video.mp4 -vf "cropdetect" -c:a copy out.mp4
4. 视频旋转/翻转:乾坤大挪移
旋转: 使用
-display_rotation
参数指定视频的逆时针旋转角度。 该操作是元数据操作,无需重新编码视频。shellffmpeg -display_rotation 90 -i video.mp4 -c copy out.mp4
翻转: 使用
-display_hflip
进行水平翻转、-display_vflip
进行竖直翻转。 如果同时使用了翻转与旋转,FFmpeg 会先执行旋转、再执行翻转。
5. 设置视频预览图:给视频穿上漂亮的外衣
准备预览图:
自动抽取: 使用
thumbnail
过滤器自动从视频中抽取一张预览图。shell# 自动选取 1 张预览图,按宽边为 1080 缩放分辨率,然后保存到文件 ffmpeg -i clip.mp4 -vf thumbnail,scale=-1:1080 -vframes 1 thumb.png # 以 30 帧为扫描步长,从视频中自动选取 3 张预览图以供挑选(并在保存时进行三位数编号) ffmpeg -i clip.mp4 -vf thumbnail=30,scale=-1:1080 -vframes 3 thumb-%03d.pngs
指定帧: 使用 截取帧为图片 一节中提到的方法,指定截取某一帧作为图片。
shell# 指定截取视频中的第 100 帧 ffmpeg -i clip.mp4 -vf 'select=eq(n\,100)' -vframes 1 thumb.png
手动准备: 使用其他软件自行准备。
嵌入预览图:
MP4 文件: 使用
disposition
参数。shellffmpeg -i video.mp4 -i thumb.png -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic out.mp4
MKV 文件: 使用
-attach
参数。MKV 封面图片的文件名必须为 cover 例如 cover.jpg 或 cover.png。否则,嵌入的封面不能作为预览图被正常地显示。
shell# 如果使用 PNG 文件(cover.png),请相应地将后续参数改为 mimetype=image/png ffmpeg -i video.mkv -c copy -attach cover.jpg -metadata:s:t:0 mimetype=image/jpeg out.mkv
6. 更改帧率/速度:时间都去哪儿了
变速的目的: 常见应用场景是将 60 帧视频通过变速输出为 30 帧的视频。
不推荐的方法: 使用
-vf
参数的fps
或者setpts
键指定视频帧率。 这些方法 不是一个无损转换。shell# 以下方法会抽帧,均不推荐! ffmpeg -i video-60p.mp4 -vf fps=30 video-30p.mp4 ffmpeg -i video-60p.mp4 -vf "setpts=0.5*PTS" video-30p.mp4
推荐的变速方法: 无损且无需重新编码。 原理是将视频输出为不包含时间戳的数据流,然后在重封装时指定变速后的时间戳。
shell# 如果是 H265,使用: ... -bsf:v hevc_mp4toannexb raw.h265 ffmpeg -i video-60p.mp4 -map 0:v -c:v copy -bsf:v h264_mp4toannexb raw.h264 # 只处理视频流 ffmpeg -fflags +genpts -r 30 -i raw.h264 -c:v copy video-30p.mp4
如果需要同时对视频、音频进行降速,可以利用 atempo 过滤器。
shellffmpeg -fflags +genpts -r 30 -i raw.h264 -i video-60p.mp4 -map 0:v -c:v copy -map 1:a -af atempo=0.5 output.mp4
atempo
过滤器只支持 0.5 到 100 之间的变速倍率。- 可以重复调用,例如
-af "atempo=0.5,atempo=0.5"
将会把音频降速为 0.25 倍。
如果需要对加速或减速后的帧之间进行动态插值(运动补偿),可以使用 minterpolate 过滤器。 但这就需要对视频重新编码了。
shellffmpeg -i input.mkv -filter:v "minterpolate='mi_mode=mci:mc_mode=aobmc:vsbmc=1:fps=30'" output.mkv
更多变速内容请参考 How to speed up / slow down a video。
7. 字幕操作:妈妈再也不用担心我看外国片了
FFmpeg 可以将字幕内挂到封装容器内,也可以内嵌到视频流中。
- 注意事项:
- 独立的字幕文件请使用 UTF-8 编码。
- Windows 系统可能缺少一个字体接口,需要自己配置一份
fonts.conf
文件,并放在%FONTCONFIG_PATH%
这个环境变量对应的路径下。- 如果用户没有该变量,请新建一个;其默认值一般是
C:\Users\用户名\
。 - 关于
fonts.conf
文件,请参考本文的附录 fonts.conf。
- 如果用户没有该变量,请新建一个;其默认值一般是
7.1 内挂字幕:软字幕
内挂字幕是一种相对于外挂字幕的称呼。外挂字幕是指将字幕存放在一个独立的字幕文件中,在播放视频时,通过视频播放器来加载这个字幕文件。而内挂字幕,是将这样一个独立的“字幕文件”,封装在了视频文件内部作为独立的字幕数据流。这样既能按需开启或关闭字幕,也免去了字幕文件丢失、匹配等烦恼。
内挂字幕的本质是将字幕文件单独作为字幕流封装,因此不需要对视频流进行编码。因此,将字幕文件内挂到指定的视频一般非常快:
ffmpeg -i input.mp4 -i input.srt -c:v copy -c:a copy -c:s ass output.mkv
在封装时,一般需要选择
-c:s ass
这个字幕转码器。 上例中使用了早年间非常流行的内挂字幕容器 mkv,实际上 mp4 容器也可以进行内挂操作。要从有字幕流的视频文件中移除字幕,可以使用
-sn
参数。更多信息,参考本文替换流与删除流部分的内容。
内挂字幕的元数据操作:
FFmpeg 支持以元数据(metadata)的形式指定流的信息,这也包括字幕流(内挂字幕)。 其中,比较常用的元数据可能是指定字幕的语言。 下例向一个无字幕的视频文件中添加了一个 ass 字幕文件,并指定其语言为中文(语言码应遵循 ISO639-2 标准的三位代码,例如英文 eng、日文 jpn):
ffmpeg -i input.mp4 -i input.ass -c:v copy -c:a copy -c:s ass -metadata:s:s:0 language=chi output.mkv
如何将内挂字幕设定为默认加载的字幕?
FFmpeg 支持使用 -disposition
参数将某个流设定为默认。 例如 -disposition:s:0 default
表示将文件的第一个字幕流设置为默认(default);要取消将该字幕流设置为默认,请将其值从 default
设置为 0
。
一个常见的批处理操作是将文件夹内的所有字幕文件批量内挂到同名的视频文件中。 下面以使用 Powershell 命令向 mkv 中内挂 ass 字幕为例,该命令在内挂字幕的同时将字幕语言标记为中文、并设置为默认字幕:
(Get-ChildItem *.mkv).Basename | ForEach-Object { ffmpeg -i "${_}.mkv" -i "${_}.ass" -y -c:v copy -c:a copy -c:s ass -metadata:s:s:0 language=chi -disposition:s:0 default "${_}-output.mkv"}
上例的输出结果在 VLC 播放器中识别通过,播放时该字幕会自动启用;而不指定 -disposition default
的输出文件则需要手动启用内挂字幕。
7.2 内嵌字幕:硬字幕
内嵌字幕(或称硬字幕)是指将字幕与原视频图像混叠的一种字幕,它直接嵌入到图像中,因此无法关闭,也无法调整字幕的大小、字体等样式。内嵌字幕的本质是将字幕作为图像输出,因此需要对视频流进行编码,往往速度慢:
ffmpeg -i input.mp4 -vf subtitles=input.srt output.mp4
如果字幕以字幕流的形式存在于另一个视频文件中,可以直接调用,无需将字幕流先提取成文件:
shellffmpeg -i input.mkv -vf subtitles=input.mkv output.mp4
8. 合并视频:合二为一,天下无敌
最简单的视频合并方法,是将所有待合并的视频文件路径,依次列在一个 txt 文件中,然后让 FFmpeg 读取它。
创建
mylist.txt
文件:将所有待合并的 mp4 文件放在同一个文件夹中,并按照合并的顺序进行命名。
在该文件夹中用 Shift + 鼠标右键打开 PowerShell 控制台,然后依次输入以下命令:
shellls eg-*.mp4 | % Name | foreach { "file '${_}'" } > mylist.txt
该命令会生成一个
mylist.txt
文件,内容如下:nonefile 'eg-01.mp4' file 'eg-02.mp4' ...
如果视频文件数量不多,用户也可以自行创建这样一个
mylist.txt
文件。
合并视频:
shellffmpeg -f concat -i mylist.txt -c copy output.mp4
清理:
- 合并完成后,用户可以删除文件夹中的
mylist.txt
文件。
- 合并完成后,用户可以删除文件夹中的
9. 替换或删除流:乾坤挪移
除了格式转换中提到的提取流的操作,删除或替换也是常见的选择。
9.1 删除流:断舍离
删除流有两种操作方法: 一是利用 -vn/-an/sn/-dn
参数,跳过视频/音频/字幕/数据流,并手动指定保留哪些流; 二是利用 -map
参数,反选要删除的流并保留其他所有流。
方法一:使用 -vn/-an/sn/-dn
参数
功能局限,但很好理解。 比如从文件中删除音频:
shellffmpeg -i video.mp4 -c:v copy -an VideoWithoutAudio.mp4
-c:v copy
:传递视频编解码器,copy
表示不进行编解码操作而是直接拷贝。-an
:删除音频流。- 注意: 音频、视频以外的流都会被抛弃。
方法二:使用 -map
参数
用于删除其中的一个流,而保留其他所有的流。
shellffmpeg -i video.mp4 -map 0 -map -0:a EverythingButAudio.mp4
-map 0
:表示接受第 1 个输入文件(即 video.mp4)的所有流。-map -0:a
:表示丢弃第 1 个输入文件的音频流。
9.2 替换流:偷梁换柱
替换流的常用场景是将一段音频替换原视频中的音频流:
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 个音频流。- 由于此例中输出的视频不存在多个同类流,因此第二个冒号可以省略。
FFmpeg 自动选择:
- 不使用
-map
手动指定时,FFmpeg 会自动选择:输入文件的所有视频流(一个文件可能有多个流)中分辨率最高的。
输入文件的所有音频流中声道数最多的。
输入文件的所有字幕流中最靠前的。
注意: 如果字幕流是图像型而不是文字型的,需要显式地指定
c:s
参数。 比如,如果video.mkv
的字幕流是图像型的,那么下例中的out1.mkv
不含字幕流(因为默认的 MKV 字幕流编码器只接受文字型字幕流),而out2.mkv
则包含字幕流(因为 dvdsub 用于图形型字幕流):shellffmpeg -i video.mkv out1.mkv ffmpeg -i video.mkv -c:s dvdsub out2.mkv
10. 压制与码率:精打细算
注意 在大多数压制场合,CRF 都是更受欢迎的,也是保持画面质量的一选。 如果要严格限制文件大小,那么就使用二压; 如果要严格限制视频码率,才会考虑使用定限码率压制(或者二压)。
本节以下的例子将以 libx264 编码器为例,并只是进行了粗略的介绍。 关于编码器的更多详细内容,请参考 常用编码器与参数 一节。
视频的压制主要有 CRF(Constant Rate Factor,恒定率系数)与二压(2Pass)两种常用的方法,以及定限码率压制这种相对不常用的方法(不太推荐):
CRF(Constant Rate Factor): 指定一个 0~51 的数值作为视频质量标准值。
- FFmpeg 中,libx264 默认 23,常用范围是 17~28;libx265 默认 28。
- CRF 的数值越小,恒定率系数越好,压缩率也越低。
- 恒定律系数的视频码率是根据画面动态调整的,与恒定码率(CBR)恰好是对立的。
- CRF 为 0 表示无损,51 表示 FFmpeg 所能达到的最差效果。
- 如果设置一个小于默认值 23 的值,那么输出视频的画面会(从视觉观感上)保留较好的效果,但同时文件的体积也较大。
- 如果设置一个大于 23 的值,那么输出的视频大小会被压缩,但会在画面观感上有一定损失。
- 对于 H.264 编码,CRF 在 17 左右时,输出的视频损失就非常小了,因此选择比 17 更小的 CRF 意义不大。
- 类似地,CRF 如果低于 28,其效果相比于原视频可能就会出现明显的损失,因此通常也不建议选择大于 28 的数值,
二压(2Pass): 需要生成固定大小文件时的压制方法,顾名思义,需要编码两次(因此较慢)。 用户可能需要自行计算视频码率限值。
定限码率(Limited bitrate): 仅在网络上传有严苛要求时才使用的方法,并不是画面质量的第一选择。
10.1 恒定率系数(CRF)
除了 -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 参数。
10.2 二压(2Pass)
注意 通常只在强制要求文件大小时使用二压。
设想一个二压的应用场景(本例取自 FFmpeg Wiki):需要将一个 10 分钟(600 秒)长的视频压制