FFmpeg 安装与用法
一个 MKV 文件要放到网页上播放,最先要查的是里面装了什么。如果视频已经是 H.264,音频也是 AAC,通常只需要换成 MP4 容器。要压小体积、改分辨率或把字幕烧进画面,才需要重新编码。用 FFmpeg 时,这个区别比记一长串参数更管用。
1. FFmpeg 能做什么
平常能用到三个命令。ffmpeg 负责读写和转换音视频,ffprobe 用来查文件和轨道信息,ffplay 可以临时预览。有些发行包不包含 ffplay,前两个工具就足够处理本文的例子。
MP4、MKV 是容器,H.264、HEVC、AAC 是编码。一个文件可以装一条视频、好几条音频和字幕。如果换个 .mp4 后缀就能播放,那只是播放器认出了原来的内容。真正换容器需要把轨道重新封装进去。
2. 安装与检查
2.1 Windows 安装
从下载页的 Windows EXE Files 进入 Gyan 或 BtbN,选和系统架构匹配的预编译包。解压后,把包含 ffmpeg.exe 和 ffprobe.exe 的 bin 目录加到用户 Path,然后重新打开 PowerShell。比如解压后文件在 C:\Tools\ffmpeg\bin\ffmpeg.exe,就加 C:\Tools\ffmpeg\bin 这一层。 FFmpeg 下载页
Get-Command ffmpeg, ffprobe
ffmpeg -version
ffprobe -version
Get-Command 能看到实际调用的是哪一份。电脑里有多个版本时,先看路径,再看版本号。
2.2 macOS/Linux
macOS 已安装 Homebrew 时,可以直接用这一条。 Homebrew 的 FFmpeg 页面
brew install ffmpeg
Ubuntu 和 Debian 用发行版的软件包即可。
sudo apt update
sudo apt install ffmpeg
装完同样检查 ffmpeg -version 和 ffprobe -version。不同构建带的编码器、滤镜不一定一样。后面会用到 libx264、libmp3lame 和 subtitles,遇到缺失时先查本机列表。
ffmpeg -hide_banner -encoders
ffmpeg -hide_banner -filters
下面的媒体处理命令用 Windows 上的 FFmpeg 8.0.1 验证过。文件名是示例,运行时换成手头的素材。命令都以当前目录为基准,输出文件名也单独指定,方便和源文件对照。
3. 读懂第一条命令
3.1 先看输入文件
从当前目录里的 input.mkv 读容器、时长和轨道。-v error 只打印错误日志,-of json 让结果便于脚本处理。
ffprobe -v error -show_format -show_streams -of json "input.mkv"
先看 streams 下的 codec_type、codec_name,再看视频的宽高和音频的语言标签。标签可能缺失或写错。碰到多个音轨,还是要试听一下,再决定留下哪条。
3.2 参数放在哪里
一条常见命令从左到右是全局选项、输入选项、-i 和输入文件、输出选项、输出文件。多输入或多输出时,文件选项要放在它所对应的文件之前。比如同一个 -ss 放在 -i 前后,起作用的位置就不同。
下面选第一个输入的第一条视频和第一条音频,把普通 SDR 素材转成常见的 H.264 + AAC MP4。
ffmpeg -n -i "input.mkv" -map 0:v:0 -map "0:a:0?" -c:v libx264 -crf 22 -preset medium -pix_fmt yuv420p -c:a aac -b:a 160k -movflags +faststart "output.mp4"
0:v:0 里的两个 0 分别是输入编号和该类型轨道的编号。音频选择末尾的 ? 表示没有音轨也可以继续。-n 禁止覆盖现有文件。+faststart 把 MP4 的索引移到文件前部,方便边下载边播放。yuv420p 常用于兼容普通 SDR 播放器,HDR 转 SDR 还需要单独做色调映射。 FFmpeg 命令文档
4. 常用文件处理
4.1 换容器和压缩
如果刚才查到输入本来就是 MP4 可容纳的 H.264 和 AAC,可以直接复制这两条流。-c copy 省掉解码和编码,速度一般接近读写文件的速度。
ffmpeg -n -i "input.mkv" -map 0:v:0 -map "0:a:0?" -c copy -movflags +faststart "remux.mp4"
换容器通常不会明显压小文件。要减小体积,可以从上一节的 -crf 22 开始比较,调大 CRF 一般能减少体积,也会损失更多细节。-preset 控制编码耗时和压缩效率,输出大小还取决于画面内容。若必须卡在某个文件大小以内,先按时长估算目标总码率,再给音频和封装留余量。需要控制平均视频码率时,可考虑 libx264 的两遍编码。
4.2 截一段视频
从 2.25 秒开始取 3 秒,重编码适合需要精确起点的情况。默认准确定位会先跳到附近的寻址点,再丢掉起点前多解码的画面。实际切点仍受帧时间戳限制。
ffmpeg -n -ss 2.25 -i "input.mkv" -t 3 -map 0:v:0 -map "0:a:0?" -c:v libx264 -crf 20 -c:a aac "cut.mp4"
只想快速粗剪时也可以用 -c copy,但切点可能落在更早的关键帧附近,尤其要检查开头画面和音画同步。 FFmpeg 的 -ss 选项
4.3 提取声音和截图
第一条转成 MP3,第二条导出指定时间附近的一帧。音轨本来就是 AAC,而且想保留原始音频时,可以改用 -c:a copy,输出到 .m4a。
ffmpeg -n -i "input.mkv" -map 0:a:0 -vn -c:a libmp3lame -q:a 2 "audio.mp3"
ffmpeg -n -ss 2.25 -i "input.mkv" -frames:v 1 -update 1 "frame.png"
5. 滤镜与字幕
5.1 缩放和响度
-vf 处理视频帧,-af 处理声音。下面把视频缩到 720 像素高,-2 让宽度按比例取偶数值。源片不到 720p 时,这条会放大,可以把目标高度调小。后面的 loudnorm 按综合响度、响度范围和真峰值三个目标处理音轨,示例要求输入有声音。
ffmpeg -n -i "input.mkv" -map 0:v:0 -map 0:a:0 -vf "scale=-2:720" -af "loudnorm=I=-16:LRA=7:TP=-1.5" -c:v libx264 -crf 22 -c:a aac -ar 48000 "scaled.mp4"
加滤镜就会修改解码后的内容,因此被处理的流要重新编码。这里的响度参数是一个起点。要给一批成片做更可控的响度归一,可用 print_format=json 先测量,再把测量值填进第二遍的 measured_* 参数。 FFmpeg 滤镜文档
5.2 可关闭字幕和烧录
手头有一份 UTF-8 的 caption.srt。希望播放器仍能开关字幕时,可以把它作为新轨道装进 MKV。-map 1:s:0 选的是第二个输入中的第一条字幕。
ffmpeg -n -i "input.mkv" -i "caption.srt" -map 0:v:0 -map "0:a:0?" -map 1:s:0 -c copy "with-subtitle.mkv"
有些设备不显示独立字幕轨,可以把文字直接画进视频。这个滤镜需要构建带 libass,也需要系统安装能显示相应文字的字体。先把 SRT 放到当前目录,省去处理 Windows 盘符和滤镜路径转义。
ffmpeg -n -i "input.mkv" -map 0:v:0 -map "0:a:0?" -vf "subtitles=caption.srt" -c:v libx264 -crf 20 -c:a aac "burned.mp4"
6. 多输入与批处理
6.1 叠一张角标
-filter_complex 适合多输入或多输出。第二个输入 logo.png 先缩到 160 像素宽,经 [logo] 接到 overlay 上。W-w-20:H-h-20 把它放在距离右下角各 20 像素的位置。最后映射滤镜输出 [v] 和原片音轨。
ffmpeg -n -i "input.mkv" -i "logo.png" -filter_complex "[1:v]scale=160:-2[logo];[0:v][logo]overlay=W-w-20:H-h-20[v]" -map "[v]" -map "0:a:0?" -c:v libx264 -crf 21 -c:a aac "branded.mp4"
读滤镜图时可以顺着方括号找流向。逗号连接同一条链上的多个滤镜,分号隔开不同的链,标签把链与链接起来。 滤镜图语法
6.2 拼接两段素材
两段 MP4 的轨道顺序、编码、分辨率和时间基等参数一致时,可以用 concat demuxer 拼起来。把下列两行存到 clips.txt,和视频放在同一目录。
file 'clip-a.mp4'
file 'clip-b.mp4'
ffmpeg -n -f concat -safe 1 -i "clips.txt" -c copy "joined.mp4"
如果片源是不同手机拍的,即使后缀相同,也值得先用 ffprobe 对照轨道参数。规格不同时,先转成统一规格,或者用 concat 滤镜解码后拼接。 FFmpeg 容器文档
6.3 逐个处理文件
下面这段 PowerShell 处理当前目录的 MKV,结果写到 converted。它先写临时文件,成功后才改成正式文件名,避免中途失败留下一个看似已经转好的 MP4。
$outputDir = (New-Item -ItemType Directory -Path '.\converted' -Force).FullName
foreach ($file in Get-ChildItem -File -Filter '*.mkv') {
$target = Join-Path $outputDir ($file.BaseName + '.mp4')
if (Test-Path -LiteralPath $target) { continue }
$temp = Join-Path $outputDir ([guid]::NewGuid().ToString('N') + '.part')
ffmpeg -hide_banner -loglevel error -nostdin -n -i $file.FullName -map 0:v:0 -map '0:a:0?' -c:v libx264 -crf 22 -pix_fmt yuv420p -c:a aac -movflags +faststart -f mp4 $temp
if ($LASTEXITCODE -eq 0) {
Move-Item -LiteralPath $temp -Destination $target -ErrorAction Stop
} else {
if (Test-Path -LiteralPath $temp) { Remove-Item -LiteralPath $temp }
Write-Warning "转码失败 $($file.Name)"
}
}
7. 网页分片与硬编
7.1 生成 HLS 点播
在当前目录先建好空的 hls 文件夹。这条会生成一个 index.m3u8 播放列表和若干 TS 片段。-hls_time 4 是目标时长,实际分片要等到合适的关键帧,因此这里让编码器大约每 4 秒插入一个关键帧。
ffmpeg -n -i "input.mkv" -map 0:v:0 -map "0:a:0?" -c:v libx264 -crf 22 -pix_fmt yuv420p -force_key_frames "expr:gte(t,n_forced*4)" -c:a aac -f hls -hls_time 4 -hls_playlist_type vod -hls_segment_filename "hls/seg_%03d.ts" "hls/index.m3u8"
部署时把播放列表和分片一起放到 HTTP 服务器上,保留相对路径。这个命令只输出一档清晰度。需要自适应码率时要另外编码多档,并生成主播放列表。 FFmpeg 的 HLS 封装选项
7.2 硬件编码怎么选
批量转码太慢,可以查构建是否带 h264_nvenc、h264_qsv 或 h264_videotoolbox。它们分别面向 NVIDIA、Intel QSV 和 Apple VideoToolbox 的可用设备。列表里看到名字,只说明这个构建认识该编码器。还要看显卡、驱动和运行环境能否初始化它。
ffmpeg -hide_banner -h encoder=h264_nvenc
有 NVIDIA 硬件并确认驱动可用时,可以先跑一小段对比速度和画质。下面用 NVENC 重编码视频,音频照常编码成 AAC。-b:v 4M 是这里选的目标视频码率,和前面的 x264 CRF 设置不能直接互换。
ffmpeg -n -i "input.mkv" -t 5 -map 0:v:0 -map "0:a:0?" -c:v h264_nvenc -b:v 4M -c:a aac "nvenc-sample.mp4"
-c:v h264_nvenc 只选了编码器,解码和滤镜仍各有自己的处理路径。判断整条命令值不值得换,最好用同一段素材测实际用时,再看输出体积和画质。
8. 常见报错
Unknown encoder或No such filter。用-encoders或-filters查构建里有没有它,别只看网上的参数表。Stream map ... matches no streams。用ffprobe查输入编号和轨道类型。只有确实允许缺失的流才在映射末尾加?。Filtering and streamcopy cannot be used together。给加过滤镜的那条流选编码器,没动过且兼容目标容器的流仍可以复制。- 转完有画面却没声音。先检查
-map是否选了音频,再用ffprobe检查输出轨道。输出有音轨还要检查播放器是否支持其编码。