REC 01 · FFMPEG TRANSCODE
FFmpeg 转码
先看清片子里有什么,再决定是换个盒子、重编码,还是连画面一起改。下面的命令把 input.mp4 换成你的文件就能跑。
00:00先确认机器上有编码器
macOS 用 Homebrew,Debian / Ubuntu 用 apt,Windows 用 winget。装完用两条命令确认:一条看版本,一条看本机能用哪些 H.264 / H.265 / AV1 编码器。
brew install ffmpeg
sudo apt update && sudo apt install -y ffmpeg
winget install --id Gyan.FFmpeg -e
三条里只跑你这台机器对应的那一条。Windows 装完要新开一个终端,ffmpeg 才会进 PATH。
ffmpeg -version
ffmpeg -encoders | grep -E '264|265|av1|nvenc|videotoolbox|qsv|vaapi|aac|opus|mp3'
看到 libx264、aac 就可以做最常见的 MP4。macOS 上若还有 h264_videotoolbox,后面可以用硬件编码换速度。libx265、libsvtav1 没有的话,对应小节先跳过,不要改命令硬跑。
00:01封装、编码、滤镜
转码这个词经常被用宽了。FFmpeg 里其实是三件事,花费差一个数量级。
只换容器,比如 MKV 改成 MP4。视频和音频原样拷贝,-c copy,几秒完成。
解开旧编码,再用新编码器压一遍。画质、体积、兼容性都在这一步决定,也最耗 CPU。
缩放、裁剪、加水印、烧字幕、变速。只要动了画面,视频就必须重编码,-c:v copy 会失效。
容器是盒子:mp4、mkv、mov、webm、ts。编码是里面的画和声:视频常见 H.264、H.265、AV1,音频常见 AAC、Opus、MP3。盒子和内容是分开选的。H.264 + AAC 放进 MP4,是今天兼容性最好的组合。
| 你要的结果 | 该做哪一步 | 典型开关 |
|---|---|---|
| 手机拍的 MOV 放到网页上播 | 先试封装拷贝,播不了再转 H.264 | -c copy 或 libx264 |
| 文件太大 | 重编码,用 CRF 控画质 | -crf 23 |
| 只要改成 720p | 滤镜缩放,视频重编码,音频能拷就拷 | scale=1280:-2 |
| 剪掉片头,画质不变 | 按关键帧剪切并拷贝流 | -ss + -c copy |
| 配音换掉,画面不动 | 视频拷贝,只编码音频 | -c:v copy -c:a aac |
00:02转之前先看清素材
不要对着文件名猜。用 ffprobe 把时长、码率、每一路流的编码、分辨率、帧率、像素格式、采样率打出来。
ffprobe -v error -show_entries format=duration,bit_rate,format_name -show_streams -of compact input.mp4
重点看这几项:
codec_name:已经是h264+aac,目标又是 MP4,多半只要-c copy。width/height:H.264 的yuv420p要求宽高都能被 2 整除。奇数边(比如 1919×1080)缩放时要用-2。pix_fmt:yuv420p最通用。10-bit 的yuv420p10le很多播放器不认,转 H.264 时要显式改回 8-bit。r_frame_rate:手机录像常见 30000/1001,也就是 29.97。不要随手改成 30,除非你确实要统一帧率。- 多条
codec_type=audio:说明有多音轨,后面要用-map指定留哪一条。
-noautorotate 再查原因,不要先改滤镜。00:03一条命令怎么读
选项写在哪个 -i 的哪一侧,决定它是输入选项还是输出选项。写错位置,-ss 会从「快速跳转」变成「从头解码」,或者干脆作用到错误的文件上。
-y覆盖已有输出;-n拒绝覆盖。两个都没有时,交互终端会问你,脚本里则会停住。-c:v是视频编码器,-c:a是音频。-c copy表示两路都原样拷贝。-t 10是持续 10 秒,-to 00:00:10是停在时间轴的 10 秒处。和-ss一起用时,-to是绝对时间,不是时长。- 输出文件名的后缀决定默认容器。想明确指定就加
-f mp4。
00:04只换盒子,不重新压缩
画面和声音的编码已经合适,只是容器不对,或者网页要边下边播,就不要转码。
ffmpeg -y -i input.mkv -c copy -movflags +faststart output.mp4
+faststart 把索引(moov)挪到文件头。没有它,浏览器往往要下完整个 MP4 才开始播。
ffmpeg -y -i input.mp4 -vn -c:a copy output.m4a
codec not currently supported in container 时,换 MKV(-c copy output.mkv),或者只重编码那一路不兼容的流。00:05重编码视频
体积和画质的主旋钮是 CRF(恒定质量因子),不是码率。CRF 越小越清晰、文件越大。H.264 的常用范围是 18(接近视觉无损)到 28(明显更小),默认 23。预设 preset 只管压缩效率:slow 更小或更好,veryfast 更快、文件更大,默认 medium。
H.264,兼容性优先
ffmpeg -y -i input.mp4 \
-c:v libx264 -crf 23 -preset medium -pix_fmt yuv420p \
-c:a aac -b:a 160k \
-movflags +faststart \
output.mp4
音频如果已经是 AAC,把 -c:a aac -b:a 160k 换成 -c:a copy,声音不会再损一次。
H.265,同样观感更小
x265 的 CRF 和 x264 不是同一把尺子。x265 默认 CRF 是 28,观感大致接近 x264 的 23,编码也慢一截。苹果设备要在 MP4 里播 HEVC,需要 -tag:v hvc1,否则 QuickTime 和 Safari 可能打不开。
ffmpeg -y -i input.mp4 \
-c:v libx265 -crf 28 -preset medium -tag:v hvc1 \
-c:a aac -b:a 160k \
-movflags +faststart \
output.mp4
要卡死峰值码率
投到带宽有限的网页或播放器时,CRF 仍决定常态画质,-maxrate 和 -bufsize 只是把尖峰按住。这不是纯 CRF,复杂画面会被压一点。
ffmpeg -y -i input.mp4 \
-c:v libx264 -preset slow -crf 22 -maxrate 4M -bufsize 8M \
-pix_fmt yuv420p -movflags +faststart \
-c:a aac -b:a 160k \
output.mp4
目标体积固定,用两遍编码
必须压到某个平均码率时,两遍比一遍的码率控制更稳。第一遍只分析,不写成品。
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 2M -pass 1 -an -f null /dev/null
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 2M -pass 2 -c:a aac -b:a 160k output.mp4
Windows 把 /dev/null 换成 NUL。两遍都会在当前目录留下 ffmpeg2pass-0.log,做完可以删。
AV1
更小,也更慢,播放器支持仍不如 H.264。本机有 libsvtav1 时,可以这个为起点;preset 数字越小越慢。网页上更稳的容器是 WebM,配 Opus。
ffmpeg -y -i input.mp4 \
-c:v libsvtav1 -crf 32 -preset 8 \
-c:a libopus -b:a 96k \
output.webm
00:06只动声音
FFmpeg 自带的 AAC 编码器名字就是 aac。口碑更好的 libfdk_aac 因为许可,多数发行版默认不带。语音 96–128 kbps 够用,音乐 160–192 kbps 比较稳。
ffmpeg -y -i input.mp4 -c:v copy -c:a aac -b:a 160k output.mp4
ffmpeg -y -i input.mp4 -vn -c:a libmp3lame -q:a 2 output.mp3
-q:a 2 大约是 190 kbps 的 VBR,比固定码率更省。只要 WAV:
ffmpeg -y -i input.mp4 -vn -ac 2 -ar 48000 output.wav
用另一条音轨替换原声,并在较短的那条结束时停下:
ffmpeg -y -i input.mp4 -i voice.wav \
-map 0:v:0 -map 1:a:0 \
-c:v copy -c:a aac -b:a 160k -shortest \
output.mp4
粗调响度可以用单遍 loudnorm。它会重编码音频;要更准的广播标准,再查两遍 loudnorm,这里不展开参数。
ffmpeg -y -i input.mp4 -c:v copy -af loudnorm=I=-16:TP=-1.5:LRA=11 output.mp4
00:07缩放、裁剪、帧率、水印、变速
滤镜写在 -vf 里,多个用逗号串起来,从左到右执行。只要有 -vf,视频就不能 copy。
ffmpeg -y -i input.mp4 \
-vf "scale=1280:-2" \
-c:v libx264 -crf 23 -preset medium -pix_fmt yuv420p \
-c:a copy \
output.mp4
-2 的意思是:按比例算出高度,并且对齐到 2 的倍数。不要手写一个奇数高度。
ffmpeg -y -i input.mp4 -vf "crop=1280:720:0:0" -c:v libx264 -crf 23 -c:a copy output.mp4
ffmpeg -y -i input.mp4 -vf "fps=30" -c:v libx264 -crf 23 -c:a copy output.mp4
ffmpeg -y -i input.mp4 -i logo.png \
-filter_complex "overlay=W-w-24:H-h-24" \
-c:v libx264 -crf 23 -c:a copy \
output.mp4
两倍速要同时改画面时间戳和音频。单个 atempo 只接受 0.5 到 2.0,四倍就串两个。
ffmpeg -y -i input.mp4 \
-filter:v "setpts=0.5*PTS" -filter:a "atempo=2.0" \
-c:v libx264 -crf 23 -c:a aac -b:a 160k \
output.mp4
ffmpeg -y -ss 00:00:03 -i input.mp4 -frames:v 1 thumb.jpg
ffmpeg -y -ss 1 -t 3 -i input.mp4 \
-vf "fps=12,scale=480:-2:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
output.gif
00:08剪切
-ss 放在 -i 前面,是按关键帧快速跳。配合 -c copy 时,起点会吸附到前面的关键帧,所以可能多出零点几秒,但不会重编码。
ffmpeg -y -ss 00:01:00 -to 00:01:30 -i input.mp4 -c copy output.mp4
要帧级准确,把 -ss 放到 -i 后面,并重新编码。这样会从片头解码到切点,长视频会慢。
ffmpeg -y -i input.mp4 -ss 00:01:00 -to 00:01:30 \
-c:v libx264 -crf 20 -preset medium -c:a aac -b:a 160k \
output.mp4
00:09字幕和多轨
软字幕是单独一条流,MKV 里最省事。硬字幕是烧进画面,之后不能关,而且必须重编码视频。
ffmpeg -y -i input.mp4 -i subs.srt -c copy -c:s srt output.mkv
ffmpeg -y -i input.mp4 \
-vf "subtitles=subs.srt:force_style='FontName=PingFang SC,FontSize=22'" \
-c:v libx264 -crf 23 -c:a copy \
output.mp4
烧字幕需要编译进 libass。路径里有空格或冒号时要转义,中文字体名按你机器上真实的字体改。
只保留第一条视频和第三条音轨(下标从 0 开始,所以第三条是 0:a:2):
ffmpeg -y -i input.mkv -map 0:v:0 -map 0:a:2 -c copy output.mp4
不写 -map 时,FFmpeg 会为每种类型挑一条「它认为最好的」流。多音轨、多字幕的成片,显式 -map 更可控。
00:10拼接和批量
无重编码拼接要求每段的编码、分辨率、像素格式、帧率、音频参数一致,否则时间戳会乱。先把清单写成 list.txt,路径相对于运行命令时的当前目录。
file 'part1.mp4'
file 'part2.mp4'
ffmpeg -y -f concat -safe 0 -i list.txt -c copy output.mp4
参数不一致时,先统一转成同一种 H.264,再拼接。下面这段把当前目录所有 .mov 转成网页可播的 MP4,文件名里的空格也能保住。
for f in *.mov; do
ffmpeg -y -i "$f" \
-c:v libx264 -crf 22 -preset medium -pix_fmt yuv420p \
-c:a aac -b:a 160k -movflags +faststart \
"${f%.mov}.mp4"
done
不要对同一块机械硬盘开很多并行 FFmpeg,瓶颈会从 CPU 变成磁盘。先跑一个,看 ffmpeg -stats 里的速度是不是高于 1x,再决定要不要并行。
ffmpeg -y -framerate 30 -i frame_%04d.png -c:v libx264 -pix_fmt yuv420p -movflags +faststart output.mp4
00:11硬件编码
硬件编码器快很多,同样码率下通常不如 libx264 的慢预设。批量转存、监控、预览用它;要压到很小还要保住细节,仍用软件编码加 preset slow。
macOS 的 VideoToolbox 用码率而不是 CRF。质量模式各版本参数不同,不确定时先用码率。
ffmpeg -y -i input.mp4 \
-c:v h264_videotoolbox -b:v 6M \
-c:a aac -b:a 160k -movflags +faststart \
output.mp4
ffmpeg -y -i input.mp4 \
-c:v h264_nvenc -preset p5 -rc vbr -cq 23 -b:v 0 \
-c:a copy \
output.mp4
p1 最快,p7 最慢。若报不认识 -cq,改成 -b:v 6M。Intel QSV 用 h264_qsv,Linux VAAPI 还要设备节点,先跑第 00 节的编码器列表,有名字再写进命令。
00:12现成配方
| 场景 | 命令要点 |
|---|---|
| 网页播放 | H.264、yuv420p、AAC、faststart,CRF 22–24 |
| 归档、少损 | -crf 18 -preset slow,音频 copy |
| 聊天里发,尽量小 | scale=1280:-2 加 -crf 28 |
| 竖屏短视频 | 不要乱加旋转滤镜,先相信自动旋转 |
| 只要声音 | -vn -c:a copy,封装用 m4a 或原格式 |
ffmpeg -y -i input.mp4 \
-vf "scale=1280:-2" \
-c:v libx264 -crf 28 -preset medium -pix_fmt yuv420p \
-c:a aac -b:a 128k -movflags +faststart \
output.mp4
ffmpeg -y -i input.mp4 -vf yadif -c:v libx264 -crf 20 -c:a copy output.mp4
00:13报错对照
| 报错里的关键词 | 原因 | 处理 |
|---|---|---|
| Unknown encoder | 这个编译没有该编码器 | 回到第 00 节看列表,改用已有的名字 |
| height not divisible by 2 | yuv420p 不接受奇数边 | scale=1280:-2 或 scale=trunc(iw/2)*2:trunc(ih/2)*2 |
| codec not currently supported in container | 编码和盒子不配 | 改用 MKV,或只重编码那一路 |
| moov atom not found | MP4 没收完,或索引在尾部且文件损坏 | 重新导出;完整文件用 +faststart |
| Non-monotonous DTS | 源时间戳乱,常见于拼接和某些录像 | 加 -fflags +genpts 再编码,不要 copy |
| Conversion failed / 输出 0 字节 | 输出被占用,或滤镜表达式写错 | 先用一条不带滤镜的命令验证源文件能打开 |
ffmpeg -y -fflags +genpts -i input.mp4 \
-c:v libx264 -crf 23 -c:a aac -b:a 160k \
output.mp4
参数以你本机的 ffmpeg -h encoder=libx264 为准。编码器帮助和本文冲突时,信帮助,不信记忆。