1. FFmpeg 转码的核心概念与定位
1.1 先搞清楚三件事:转码、封装、流
FFmpeg 是我这几年处理音视频文件最离不开的命令行工具,没有之一。无论你是想把一个体积巨大的 4K 视频压成适合上传的 1080p 文件,还是想批量给一堆视频换封装格式,或者要往视频里硬烧字幕、抽帧、做 GIF,FFmpeg 基本都能靠一条命令完成。这篇攻略会把我在实际工作中反复用到的转码参数按场景拆开讲,原理和命令一起给,按图索骥照着敲就能解决大部分问题。
不过在学命令之前,我建议先把三件极其容易混淆的事搞清楚:转码和封装不是一回事,视频文件里的“流”也和“文件”不是一回事。很多人记不住 FFmpeg 参数,根源就是这三个概念没理清。
第一,容器。容器是文件外层的那个“盒子”,负责把视频流、音频流、字幕流、章节信息和元数据打包在一起。常见的容器有 MP4、MKV、AVI、MOV、TS。同一个 MP4 文件里装的视频可能是 H.264,也可能是 H.265,完全取决于编码时用了什么编码器。容器决定的是“怎么组织”,而不是“怎么压缩”。
第二,编码。编码才是真正决定画质和文件大小的核心。视频编码器负责把原始图像压缩成二进制流,常见的视频编码有 H.264、H.265、VP9、AV1;音频编码有 AAC、MP3、Opus、FLAC。H.265 在同样画质下可以比 H.264 省大约一半码率,但编码速度慢很多,播放兼容性也差一些,这就是“压缩率”和“兼容性”的取舍。
第三,流。一个媒体文件里可能包含多条流,最常见的是“一条视频流 + 一条音频流”,但有些电影文件里会有多音轨、多字幕轨。FFmpeg 把文件看作一个容器里的多个并行通道,操作时可以用-map参数精确挑选某一条流。比如想从一部带杜比音轨和评论音轨的电影里只抽出普通音轨,-map就能做到。
用生活类比理解:媒体文件就像一个快递包裹,容器是纸箱,编码是打包方式,流是纸箱里的不同物件。你可以换一个更结实的箱子(换容器),里面的物件原封不动搬过去(流复制);也可以把物件重新打包(重新编码)。FFmpeg 两种方式都支持,这也是它最灵活的地方。
1.2 适合谁看,能解决哪些实际问题
这篇攻略适合所有跟音视频“打交道”的人:做视频剪辑的 UP 主、课程录制和后期处理的从业者、需要批量处理素材的运营人员,以及刚接触 FFmpeg、面对一堆参数不知道从哪下手的开发者。
我当年第一次认真用 FFmpeg,是给一个课程平台做视频上传预处理。当时几十个视频格式混乱,有码率极高的,有分辨率参差不齐的,还有编码格式老掉牙的,人工一个一个处理根本不可能。后来我用 FFmpeg 写了个批量处理脚本,统一压成平台要求的 MP4/H.264/AAC,两个小时全部搞定。那次之后我的体会是:FFmpeg 不是“偶尔用一下的小工具”,而是做任何跟音视频相关内容都绕不开的核心武器。
后面几章,我会按照“视频参数 → 滤镜参数 → 音频参数 → 实战方案 → 问题排查”的顺序展开。每个命令都会带参数解释和选择理由,为什么用-crf 23而不是-b:v 4M,为什么这里选 H.265 而那里必须用 H.264,这些背后都有它自己的逻辑。把逻辑搞通了,命令自然就记住了。
2. 视频转码参数详解:编码器、码率与质量控制的取舍
2.1 一条转码命令的结构拆解
FFmpeg 的命令行初看像天书,但拆开来看结构其实非常固定:
ffmpeg [全局参数] -i 输入文件 [输入参数] -map 流选择 [输出参数] 输出文件实际用起来大致长这样:
ffmpeg -y -i input.mkv -map 0:v:0 -map 0:a:0 -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 192k output.mp4逐项说明一下:
-y:遇到同名输出文件时直接覆盖,不询问。写脚本时建议加上,不然中途卡在交互确认很烦人。-i:指定输入文件,后面紧跟文件路径。注意一个命令里可以写多个-i,比如视频文件和字幕文件一起输入。-map 0:v:0:选择第 0 个输入文件的第 0 条视频流。0:v:0这个写法是“输入文件序号 : 流类型 : 流序号”。同理,-map 0:a:0选择第 0 条音频流。-c:v:指定视频编码器,c是 codec 的缩写,:v表示作用于视频流。-c:a:指定音频编码器,:a表示音频。-b:a 192k:设置音频目标码率为 192 kbps。
我们平时在网上看到的各种 FFmpeg 参数大全,本质上就是这些参数的排列组合。关键不是把每个参数背下来,而是理解命令行每个位置的职责。你看懂上面这一条命令,就已经能读懂网上绝大多数 FFmpeg 示例了。
2.2 编码器怎么选:libx264、libx265、NVENC、QSV
编码器选型是转码时最先要做的决策。选错了,要么画质稀烂,要么速度慢到怀疑人生。
H.264 系列的 libx264:目前兼容性最好的软件编码器,几乎所有设备、所有平台都能播放。如果你的输出目标是“一定要能看”,选它最稳妥。在相同画质要求下,libx264 的编码速度也明显快于 H.265 的软件编码。这是我最常用的默认选择。
H.265 系列的 libx265:压缩率更高,同样画质下码率可以低不少,适合给文件瘦身。代价是编码速度慢,对 CPU 要求高,而且老设备可能硬解不了。我一般只在对文件体积有硬性要求、且确认播放端支持 H.265 时才用,比如给网盘存档备份。
硬件编码器 NVENC(N卡)和 QSV(Intel 核显):速度极快,CPU 几乎不占用,适合直播推流、实时转码、大批量压缩等场景。但画质在同码率下通常不如软件编码器精细,压缩率也差一点。如果你的机器有独立显卡,而且处理的是超大批量视频、又不太在意极致画质,硬件编码器是正确选择。反过来,如果是高质量成片交付,我不建议用硬件编码器直接出成品。
我自己这几年的决策参考是这样:给客户交付的成品用 libx264 软件编码,保证兼容性和画质;自己做私人备份或者给视频网站压上传播出版本,如果是大批量就用 NVENC 做快速压缩,再用抽帧对比的方式抽检画质。这样速度和质量的平衡点找得比较准。
2.3 CRF、CBR、ABR:码率控制模式到底怎么选
这个部分新手最容易搞混,值得花点篇幅讲清楚。
CRF(Constant Rate Factor,恒定质量因子):libx264/libx265 最推荐的质量控制方式。CRF 值范围一般是 0 到 51,数值越小质量越高、文件越大。libx264 的常用范围是 18 到 28,23 是默认值。CRF 模式下,编码器会根据画面复杂度动态调整码率:画面复杂的场景分配更多码率,简单的场景压缩更多。最终文件大小不可精确预测,但画面质量是恒定的。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium output.mp4如果你的诉求只是“在能接受的画质下控制体积”,CRF 是最省心的方案。想要更小体积就用 28,想要接近无损可以降到 18。但注意 CRF 低于 18 之后,文件体积增大非常快,画质提升却几乎感知不到。除非你是要拿来做二次剪辑的中间素材,否则没必要追求极低 CRF。
ABR/CBR(平均码率/恒定码率):当你需要精确控制输出文件大小或码率时,比如平台要求视频码率不能超过某个值,就用-b:v设置目标码率,配合-maxrate和-bufsize做缓冲区控制。
ffmpeg -i input.mp4 -c:v libx264 -b:v 4M -maxrate 5M -bufsize 8M output.mp4如果你的视频全程画面都比较均匀,CBR 能保证码率稳定,适合直播、视频会议这类实时场景。但对于普通视频点播,CRF 通常能给你更好的人眼观感。说白了,CRF 是“质量优先”,CBR 是“码率优先”,没有绝对好坏,只有场景适配。
2.4 preset 和 tune:编码速度与画质的平衡点
这两个参数容易被忽略,但它们对转码结果的影响非常大。
preset从ultrafast到placebo共 9 个档位,影响的是编码器的计算复杂度。档位越高,编码速度越慢,压缩率越好,也就是说同画质下文件体积更小。我平时用得最多的是medium(默认档),追求速度时用veryfast,追求极限压缩率时用slow。至于placebo档,压缩率提升微乎其微,速度却慢得离谱,别迷信,几乎没人会真的用。
tune是对编码器做“场景微调”,常用这几个:
film:适合电影等高品质内容,保留更多细节。animation:适合动画类内容,线条更锐利。zerolatency:适合直播,关闭编码器缓冲,延迟更低。fastdecode:优化解码速度,适合旧设备播放,但文件会变大。
我的建议是默认不加 tune,只有遇到明显不适配的场景才加。比如做直播推流时,必须加-tune zerolatency,否则编码器缓冲会导致延迟明显变大,画面卡顿感很重。
还有一个容易被忽视的参数-movflags +faststart,只对 MP4 容器有效。它会把 moov 元数据块挪到文件头部,让视频在网页端“边下载边播放”。这个参数不会影响画质,但能显著改善在线播放体验,我自己压 MP4 时基本都会带上。
3. 滤镜参数实战:缩放、裁剪、旋转、水印一次讲透
3.1 缩放和裁剪:尺寸处理别踩坑
FFmpeg 的滤镜系统是它区别于普通转码工具的核心亮点。滤镜通过-vf(video filter)传入,多个滤镜用逗号连接,执行顺序是从左到右。这个特性让我们能在转码的同时,一次性完成裁剪、缩放、加字幕等操作,不用多次转码,避免画质反复损失。
缩放 scale:
ffmpeg -i input.mp4 -vf "scale=1920:1080" output.mp4如果想保持原始宽高比,可以只给一个维度,另一个用-1自动计算。这里有个细节:我建议用-2而不是-1。为什么?因为很多编码器要求宽高是偶数,-2表示自动计算并保证结果是 2 的倍数,可以避免报错。
ffmpeg -i input.mp4 -vf "scale=-2:720" output.mp4这条命令会把视频高度固定为 720,宽度按原比例自动算出偶数。我遇到过不少次,有人用-1之后转码报错,换成-2就好了。
裁剪 crop:
ffmpeg -i input.mp4 -vf "crop=1280:720:100:50" output.mp4语法是crop=宽:高:x:y,其中 x、y 是裁剪区域左上角的坐标。注意 FFmpeg 的坐标原点在左上角,和数学坐标里原点在左下角不一样,别搞反了。裁剪掉不要的画面之后,如果还需要缩放,就写成两个滤镜:
ffmpeg -i input.mp4 -vf "crop=1280:720:0:0,scale=-2:1080" output.mp4这里先裁剪出 1280x720 的区域,再缩放到合适尺寸。多滤镜串联的执行顺序很重要,先裁后缩还是先缩后裁,出来的画面内容和质量都不一样。一般建议先裁剪后缩放,这样缩放的源数据更干净,画质损失也更小。
3.2 旋转操作:元数据优先,滤镜兜底
手机竖屏录制的视频经常带着旋转元数据。很多新手第一反应是用transpose滤镜去转,但这里有一个效率极高的替代方案。
如果你的视频本身是正常横着拍的,只是播放器因为元数据把它竖起来了,直接修改旋转元数据就行,速度极快、零画质损失:
ffmpeg -i input.mp4 -c copy -metadata:s:v:0 rotate=0 output.mp4-c copy表示所有流都不重新编码,原样复制。这条命令只是把视频流的旋转标志位从 90 改成 0。
但如果你的视频画面内容确实需要真正的旋转(比如拍摄时相机就侧着了),那就得用滤镜:
ffmpeg -i input.mp4 -vf "transpose=1" output.mp4transpose=1表示顺时针旋转 90 度,2表示逆时针 90 度,0和3分别对应另外两个方向。注意:某些 FFmpeg 版本还有一个我之前踩过坑的参数-noautorotate。如果在带旋转元数据的视频上加了transpose滤镜,FFmpeg 可能会先自动旋转、又手动旋转,结果方向还是错的。处理这类文件时,建议在全局参数里显式加上-noautorotate,先把自动旋转关掉,再用滤镜控制方向,这样输入是什么朝向就按什么朝向处理。
3.3 水印添加:文字水印和图片水印的完整写法
给视频加水印是日常高频操作。文字水印用drawtext,图片水印用overlay。
图片水印 overlay:
ffmpeg -i input.mp4 -i logo.png -filter_complex "[0:v][1:v]overlay=main_w-overlay_w-10:10" output.mp4这条命令里有两个-i,第一个是视频,第二个是 logo 图片。-filter_complex是复杂滤镜,因为我们要把两条输入流混合。[0:v]表示第一个输入的视频流,[1:v]表示第二个输入的图片流。overlay=main_w-overlay_w-10:10的意思是:x 坐标 = 主画面宽度 - 水印图片宽度 - 10,y 坐标 = 10,也就是把 logo 定位到右上角,距边缘 10 像素。
文字水印 drawtext:
ffmpeg -i input.mp4 -vf "drawtext=fontfile=/usr/share/fonts/truetype/sarasa-bold.ttf:text='我的水印':fontsize=36:fontcolor=white:x=20:y=20" output.mp4文字水印坑比较多,主要是字体问题。drawtext依赖系统里的字体文件,如果指定路径不存在,滤镜会直接报错。中文场景还要注意选用支持中文的字体,否则会出现方框乱码。在 Windows 上,常见的路径是C\:/Windows/Fonts/msyh.ttc,注意冒号要转义成C\:,否则会被 FFmpeg 当成参数分隔符。
透明度也可以调,alpha=0.5表示半透明。另外,给文字加个box=1:boxcolor=black@0.5:boxborderw=5参数,底下的黑色半透明背景框能让水印在各种画面里都看得清楚,这是我做课程视频时踩过坑后总结出来的实用技巧。
4. 音频处理参数:转码时最容易翻车的隐蔽角落
4.1 音频编码器选择与码率建议
很多人的转码命令只关心视频画质,音频参数完全不管,结果压出来的视频画面是好的,声音却闷得不行。音频处理其实同样重要,只是容易被忽略。
AAC:目前视频文件中最主流的有损音频编码,兼容性最好。一般建议 128k 到 192k。普通说话类内容 128k 够用,音乐类内容我至少给 192k 以上,否则高频细节明显丢失。
Opus:低码率高音质的代表,在 128k 码率下音质明显优于 AAC,兼容性不如 AAC,适合本地收藏或流媒体传输,不适合需要广分发场景。
MP3:虽然老,但在一些老设备上还有用武之地,常用码率是 128k、192k、320k。如果源文件就是 MP3 且不需要改码率,直接-c:a copy就行。
FLAC:无损编码,码率高,通常用于剪辑和音乐存档,不适合直接用于视频发布。我做素材归档时会把音轨单独提出来存 FLAC,发布时再转 AAC。
音频转码命令示例:
ffmpeg -i input.mkv -c:v copy -c:a aac -b:a 192k output.mp4这条命令的意思很清楚:视频流直接复制不过编码,只有音频转成 AAC,192k。在需要快速转码大批量视频时,这种“视频全复制、只动音频”的方式非常常用。
4.2 采样率和声道数:参数虽小,影响很大
采样率常见的有 44100 Hz(CD 标准)和 48000 Hz(DVD/视频标准)。视频文件通常建议统一到 48kHz,因为很多视频编码器在 48kHz 下工作状态最佳。如果源文件是 44.1kHz,可以在音频编码时顺手重采样:
ffmpeg -i input.mp4 -c:v copy -c:a aac -ar 48000 -ac 2 output.mp4-ar是音频采样率,-ac是声道数。2 表示立体声,1 表示单声道。做语音类视频时,单声道能省一半音频码率,语音清晰度几乎不受影响。如果你做的是播客或课堂录像,压成单声道完全够用。
还有两个音频相关的实用参数。音量调整用-vol,数值是百分比,比如-vol 200表示音量放大一倍。更精细的响度标准化可以用-af loudnorm=I=-16:TP=-1.5:LRA=11,这是广播级响度标准,适合需要统一音量的系列视频。我做系列课时会把所有分集的响度统一到同一标准,避免观众在切换视频时觉得声音忽大忽小。
5. 四个能直接抄作业的实战转码方案
5.1 短视频压缩上传方案:4K 原片压成 1080p
视频平台上传一般要求最高 1080P、H.264 编码、码率控制在合理范围。思路是先看源文件参数,再决定压缩策略。可以用ffprobe快速查看源文件信息:
ffprobe input.mp4如果源文件是 4K H.265,我的压缩方案是:
ffmpeg -i input.mp4 -vf "scale=-2:1080" -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 192k -ac 2 -ar 48000 -movflags +faststart output.mp4这条命令做了什么:把 4K 视频缩放到 1080P;视频用 libx264 软编,CRF 23,画质和体积平衡;音频统一转成 AAC 192k 立体声 48kHz;最后-movflags +faststart优化网页播放。
为什么不直接用-b:v 4M固定码率?因为 CRF 模式会按画面复杂度分配码率,观看体验更稳定。如果平台有码率硬性上限,再改用-b:v 4M -maxrate 5M -bufsize 8M。
处理这类压缩任务时,我习惯先压一分钟的测试片段,用-ss 00:00:00 -t 60先截取前 60 秒转码,核对效果和文件大小,再全量处理。这样即使参数不合适,也不至于浪费一小时等转码结束才发现问题。
5.2 外挂字幕压成硬字幕方案
很多素材是外挂字幕(SRT/ASS 单独存放),分发时为了保险,会直接把字幕烧进画面。压制硬字幕的命令是:
ffmpeg -i input.mkv -vf "ass=subtitle.ass" -c:v libx264 -crf 20 -c:a copy output_hardsub.mp4注意这里用的是ass滤镜而不是subtitles滤镜,因为前者对 ASS 特效支持更完整。如果你只有 SRT 字幕文件,也可以直接用:
ffmpeg -i input.mkv -vf "subtitles=subtitle.srt" -c:v libx264 -crf 20 -c:a copy output_hardsub.mp4字幕相关最容易翻车的是编码问题。FFmpeg 默认按 UTF-8 解析字幕文件,如果字幕是 GBK 编码,会出现乱码甚至直接报错。我的经验是:先拿文本编辑器把字幕转成 UTF-8 再压制,这个步骤能解决九成以上的字幕乱码问题。
另外,中文字幕的字体和字号参数也值得调。在ass滤镜里指定字体,可以避免系统默认字体不支持中文的尴尬:
ffmpeg -i input.mkv -vf "ass=subtitle.ass:fontsdir=/usr/share/fonts" -c:v libx264 -crf 20 -c:a copy output_hardsub.mp45.3 批量转码脚本:一次处理整个文件夹
实际工作中,单文件处理远远不够,批量才是常态。最直接的批量方案是用 shell 循环:
for f in *.mkv; do ffmpeg -y -i "$f" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 192k "${f%.mkv}.mp4" done${f%.mkv}是 Linux shell 里的模式替换,去掉文件名的.mkv后缀。这样1.mkv就会输出成1.mp4。
批量脚本比单个命令多一个关键注意事项:格式兼容。有些.mkv里的视频流是 H.265,音频是 AC3,直接转成 MP4 时 AC3 流可能封不进去。稳妥做法是-c:a aac统一转换为 AAC 音频,视频流可以视情况保留或重编码。
我自己的习惯是:先复制一个文件出来单独跑一遍,检查输出没问题再全量执行。批量转码跑完之后,用ffprobe抽查两三个输出的文件,确认分辨率、码率、音轨数量都没问题再删原始素材。
5.4 秒级完成的换封装方案:只换容器,不重编码
日常还有个很高频的需求:把 MKV 换成 MP4,或者把 MP4 换成 MKV,但视频音频流都不想重新编码。这种情况用-c copy:
ffmpeg -i input.mkv -c copy output.mp4整条命令只有-c copy,表示所有流全部原样复制,理论上只做容器层面的改换,速度极快。一个 2GB 的 1080p 文件,一般几秒钟就完成,画质零损失。
但要注意,不是所有流组合都能直接copy进 MP4。MP4 容器不支持某些字幕流和音频流,遇到报错时就加-map只选择支持的流:
ffmpeg -i input.mkv -map 0:v:0 -map 0:a:0 -c copy output.mp4这里显式只选视频流和第一条音频流,字幕流直接丢弃。如果你想保留字幕又不重编码,可以转成 MKV 容器,MKV 对字幕流的支持要比 MP4 好得多。
6. 常见问题与排查经验:音画不同步、转码失败、画质变差
6.1 音画不同步:先看源文件,再看帧率
音画不同步是我被问得最多的问题,没有之一。原因通常有两个。
第一个原因是源文件本身时间戳就有偏移。用ffprobe检查输入文件的流信息,看音频流和视频流的 start_time 是否接近。如果音频流 start_time 明显滞后,可以用-itsoffset修正:
ffmpeg -i input.mp4 -itsoffset 0.5 -i input.mp4 -map 0:v:0 -map 1:a:0 -c:v copy -c:a copy output.mp4这条命令把第二个输入(音频)推迟 0.5 秒,与第一个输入(视频)对齐。
第二个常见原因是帧率处理错误。尤其是录屏文件,很多录屏软件生成的是 VFR(可变帧率)视频,直接转成 CFR(恒定帧率)时,FFmpeg 可能因为丢帧策略不当导致音画偏差越来越大。解决办法是转码时显式指定输出帧率,并重置时间戳:
ffmpeg -i input.mp4 -vf "fps=30,setpts=PTS-STARTPTS" -c:v libx264 -crf 20 output.mp4fps=30把可变帧率统一成 30fps,setpts=PTS-STARTPTS把视频时间戳重置为从 0 开始。如果音画不同步是出现在转码之后而不是源文件本身,这个组合基本能解决。
6.2 转码失败:学会看日志定位问题
FFmpeg 转码失败时,终端会输出一大段日志,很多人慌了神不知道从哪看起。其实只要抓住日志里Error关键字和它前面的上下文,就能快速定位问题。
常见的几类报错:
| 报错信息 | 含义 | 处理办法 |
|---|---|---|
Invalid data found when processing input | 输入文件损坏或不是有效媒体文件 | 检查文件完整性,尝试重新下载或重新导出 |
Codec did not find any suitable stream | 编码器不支持输入流类型 | 检查-map参数,确认选择的流存在且类型正确 |
Conversion failed | 输出编码失败 | 多半是编码参数不合法,检查编码器名称、比特率、分辨率参数 |
Error while opening encoder | 编码器打开失败 | 确认 FFmpeg 是否编译了对应编码器,比如 libx265 需要额外编译支持 |
排查顺序我建议:先确认输入文件没问题,再用ffprobe看流信息,最后逐步简化参数,用最精简的命令复现问题。最小化复现是排查技术问题最有效的方法。比如报错找不出原因时,把命令缩到只剩-i input -c:v copy -c:a copy output,如果这条都失败,问题基本就在输入文件本身。
6.3 画质变差的排查方向和抽帧对比法
很多人遇到输出视频画质变差,第一反应是提高码率。其实更应该先查两个地方:CRF 值是否设得过高,以及缩放参数是否把画面缩坏了。CRF 23 以上画质下降会逐渐明显,CRF 28 就属于压缩感很重的范围了。
还有一个低级但常见的坑:分辨率缩放时用了奇数宽高。有些编码器对此会报错,有些则会强制补位,导致画面边缘模糊。使用scale=-2:720这类偶数保证参数就能避免。
判断画质是否劣化的最直观方法是抽帧对比。从原片和输出片里的同一帧提取静态图,并排放一起看细节:
ffmpeg -i original.mp4 -vf "select='eq(n\,100)'" -vframes 1 orig_100.png ffmpeg -i output.mp4 -vf "select='eq(n\,100)'" -vframes 1 out_100.pngselect='eq(n\,100)'意思是选中第 100 帧,-vframes 1表示只输出一帧。把两张图放大看纹理细节,比如树叶、发丝、墙面纹理,差异一目了然。这种方法比盯着全屏播放去“凭感觉”判断靠谱得多。
我个人这几年用 FFmpeg 最大的体会是:参数记得再多,不如先把容器、编码、流这三个概念彻底搞清楚。概念通了,网上任何一段命令你都能看懂它在做什么,真正需要找资料的时候,也知道该用什么关键词去搜。另外有个小习惯值得推荐:我处理重要文件时,都会保留输入文件到输出合格之后再删除,避免转码失败导致原始素材丢失。如果你刚开始接触 FFmpeg,就从今天介绍的命令开始试,不用背参数,把事情做成了,参数自然就记住了。