多媒体格式与 FFmpeg 完全指南:从「容器 vs 编码」到命令行实战
多媒体格式与 FFmpeg 完全指南:从「容器 vs 编码」到命令行实战
前言
这篇文章想解决三个一直纠缠在一起的问题:
第一,格式太多,记不住也不理解。mp4、mkv、avi、mov、webm 是什么关系?h264、hevc、av1 又是什么?mp3、aac、opus、flac 谁能互相转换、转换会不会掉音质?
第二,手机上录的音频到底是什么格式。为什么 iPhone 分享出来是 .m4a,某些安卓老机型是 .amr,而微信语音文件明明后缀是 .amr 却怎么都打不开?
第三,FFmpeg 到底是个什么东西。为什么所有做音视频的人最后都绕不开它,而它的命令行又让人一看就想关窗口?
我的答案不打算拆成三篇独立文章,因为这三件事其实是同一件事的三个切面:理解了「封装」和「编码」是两层独立的东西,前两个问题就自动解开了;而 FFmpeg 之所以强大,恰恰是因为它把这两层各自做成了可自由组合的独立模块。
本文的格式对比基于公开标准与各平台官方文档,FFmpeg 相关数据写于 2026 年 10 月(当时最新稳定版为 9.0.2)。命令语法在 4.x—9.x 上基本通用,但个别选项在新版本已被移除或改名,我在第七章专门列了这部分差异。所有不确定的参数,都建议用「先查再动手」的方法确认,方法在 6.2 节。
一、最重要的一个认知:容器 ≠ 编码
这是整篇文章的地基,也是最多人长期搞混的一件事。一旦想通,后面所有格式名词都会各归其位。
1.1 两层各自独立的东西
编码(Codec,编解码器) 解决的是「原始数据怎么压缩」。一段 3 秒的 1080p 视频,不压缩的话是 1920×1080×3 字节/像素×30 帧/秒×3 秒 ≈ 1.1 GB;一段 1 分钟的 44.1kHz/16bit/立体声音频是 44100×2×2×60 ≈ 10 MB。这种体积没法存储和传输,于是要压缩。压缩算法本身,就是编码。
容器(Container,封装格式) 解决的是「压缩好的数据怎么装箱」。编码产出的是两条(或更多)互不知情的数据流——视频流、音频流、字幕流。容器负责规定:这些流按什么结构写进同一个文件、时间戳怎么对齐、元数据(标题、时长、轨道语言)放在哪、播放器怎么随机跳转到第 3 分钟。
所以 .mp4 不是「一种视频」,它是一种包装盒的规格;.h264 也不是「一种视频」,它是里面那件商品的型号。
1.2 一个类比
把视频文件想成一份快递:
| 概念 | 类比 | 例子 |
|---|---|---|
| 编码(Codec) | 商品本身 | H.264、H.265、AV1、AAC、MP3、Opus |
| 容器(Container) | 包装箱 | MP4、MKV、AVI、MOV、WebM、FLV、TS |
| 文件后缀 | 箱子上贴的标签 | .mp4、.mkv |
| 码率 / 分辨率 / 采样率 | 商品的规格参数 | 5 Mbps、1920×1080、48 kHz |
| 元数据 | 快递单信息 | 标题、作者、时长、轨道语言 |
标签是可以贴错的。一个后缀写着 .mp4 的文件,里面装的可能是 H.265 编码的视频;一个 .avi 里也可能塞着 H.264。很多「这个视频下载下来后缀不对,改一下就能播了」的玄学经验,本质就是标签错了而箱子没错,或者更常见——箱子和标签都不匹配,改后缀纯粹是自欺欺人。
1.3 用 ffprobe 拿到「身份证」
不要相信文件名,要看文件里的流信息。这是 FFmpeg 自带的一个小程序:
1 | ffprobe -v error -show_entries stream=index,codec_type,codec_name,\ |
输出大致长这样:
1 | [STREAM] |
看懂这个输出,你就比 90% 的人更理解什么叫「一个视频文件」了。
顺带一个细节:format_name 里为什么一口气列了一串 mov,mp4,m4a,3gp?因为这几种容器同属 ISO Base Media File Format(ISO/IEC 14496-12) 这一族,只是各自做了裁剪和扩展。MOV 是苹果的原型,MP4、3GP、M4A 都是它的后代。所以你会看到 iPhone 的录音是 .m4a——那本质上就是一个「只装音频的 MP4 盒子」。
1.4 还有一个层次:裸流
在「编码」和「容器」之下,还有最原始的一层:裸流(Elementary Stream / Raw Data),就是只有编码数据、没有任何容器结构的东西。
| 类型 | 典型后缀 | 说明 |
|---|---|---|
| 视频裸流 | .h264、.hevc、.av1, .ivf |
有编码但没有时间戳索引,通常无法定位、时长不明 |
| 压缩音频流 | .mp3、.aac(ADTS)、.opus |
帧头自带同步字,可以顺序播,但元数据能力弱 |
| 未压缩原始数据 | .yuv、.pcm、.raw |
完全没压缩,体积巨大,只在算法调试时出现 |
| 带容器的 | .wav、.flac、.m4a |
前面三种加上一个极简容器 |
.mp3 是个特例,它既是编码名也是容器名(MPEG Audio Layer III 帧 + ID3 标签)。而 .wav 严格说是「PCM 编码 + RIFF 容器」——这就是为什么 FFmpeg 里 pcm_s16le 是编码器名、wav 是格式名,两者是分开的两个参数。
二、常见多媒体格式全景图
这一节是「字典」,不需要一次读完,建议当查阅表收藏。
2.1 视频封装格式
| 格式 | 全称/归属 | 常见编码 | 主要能力 | 典型场景 | 一句话点评 |
|---|---|---|---|---|---|
| MP4 | MPEG-4 Part 14(ISO BMFF) | H.264/H.265/AV1 + AAC | 多轨、章节、元数据 | 全网通吃的默认选择 | 兼容性之王,扩展受限 |
| MKV | Matroska(开源标准) | 几乎任意 | 无限多轨、内封字幕/章节、可纠错 | 高清片源、归档 | 万能工具箱,但浏览器不原生支持 |
| WebM | Google 主导(Matroska 子集) | VP8/VP9/AV1 + Vorbis/Opus | 免专利、为流媒体优化 | YouTube、Web 前端 | 与 AV1/VP9 绑定的未来派 |
| AVI | 微软,1992 | 任意(老编码为主) | 结构简陋、无时间戳索引规范 | 老资源、采集卡 | 上古遗物,文件大且易损坏不可修复 |
| MOV | 苹果 QuickTime | ProRes/H.264 + PCM/AAC | 多轨、专业编解码 | 剪辑工程中间格式 | ProRes 的最佳载体 |
| FLV | Adobe Flash | H.264 + AAC/MP3 | 极简、适合追加 | 老直播推流 | 随 Flash 一起退了场 |
| TS / M2TS | MPEG Transport Stream | H.264/H.265 + AAC/AC-3 | 抗丢包、可无限拼接、无索引 | 数字电视、HLS、蓝光 | 直播与广播的底色 |
| RM/RMVB | RealNetworks | RealVideo | 私有 | 2000 年代下载站 | 已死,只有考古价值 |
| WMV/ASF | 微软 | VC-1/WMV | DRM 友好 | 早期 Windows Media | 已边缘化 |
| 3GP | 3GPP(ISO BMFF 子集) | H.263/H.264 + AMR/AAC | 为功能机省空间 | 彩信、老手机录像 | 安卓通话录音的老家 |
| GIF | CompuServe,1987 | LZW + 调色板 | 256 色、帧延迟、循环 | 表情包 | 不是视频,是带调色板的图像序列 |
选择建议:做产品分发一律 MP4;做本地高清归档用 MKV;做 Web 免版权方案用 WebM+AV1;做 HLS 直播切片用 TS/fMP4。
2.2 视频编码标准
编码这一层的命名规则是「标准组织定的规范名」+「具体实现(编码器)名」。H.264 是规范,x264 是 FFmpeg 里最常用的开源实现,NVENC 是显卡硬件实现。同一条规范,不同编码器压出来的质量和速度能差很多。
| 规范 | 别名 | 出现 | 相对压缩率 | 授权 | 常见编码器 | 现状 |
|---|---|---|---|---|---|---|
| H.262 | MPEG-2 Part 2 | 1995 | 基准 1.0× | 专利池 | mpeg2video | DVD、有线电视 |
| H.264 | AVC | 2003 | ≈ 0.5× | 专利池 | x264、NVENC、VideoToolbox | 绝对主流,硬件全覆盖 |
| MPEG-4 SP | ASP/DivX/Xvid | 1999 | ≈ 0.6× | 专利池 | libxvid | 老片源 |
| VP8 / VP9 | — | 2010 / 2013 | ≈ 0.5× / 0.35× | 免版税 | libvpx-vp9 | WebM、YouTube |
| H.265 | HEVC | 2013 | ≈ 0.35× | 多个专利池叠加 | x265、硬件 | 4K/HDR 主力,专利费是最大争议 |
| H.266 | VVC | 2020 | ≈ 0.25× | 专利池,费率仍高 | VVenC | 落地慢,硬件支持稀少 |
| AV1 | — | 2018 | ≈ 0.3× | 免版税 | SVT-AV1、aomenc | 联盟推动,趋势所在 |
| AV2 | — | 制定中 | 目标再降 30% | 免版税 | — | 尚不可用于生产 |
| ProRes / DNxHD | — | 2005 / 2004 | 几乎不压(帧内) | 商用授权 | 专业剪辑 | 中间码流,换编辑性能 |
| FFV1 / DNxHR | — | — | 无损 | 开源 | 内置 | 档案保存、质检 |
| Motion JPEG | MJPEG | — | 帧内独立 JPEG | 免 | 内置 | 监控摄像头、采集卡 |
为什么新标准能一直「压得更小」? 因为压缩的本质是消除冗余,而消除冗余的手段一直在变强:
- 帧内预测:一张图里相邻像素高度相关,于是只记残差。变换(DCT/ADST)+ 量化 + 熵编码(CABAC)是主力。量化是唯一「真正丢信息」的环节,也是质量的总阀门。
- 帧间预测:相邻帧 90% 内容一样,于是参考前面的帧,只记「这块往左移了 3 个像素」。这就是运动估计/运动补偿。新标准的运动划分越来越细(H.264 的 16×16 块 → HEVC 的 64×64 CTU + 四叉树 → VVC 的多类型树),是压缩率提升的主要来源。
- 环路滤波与后续工具:去块滤波、SAO、ALF、仿射运动、GPM 等等,一项项把残差继续磨小。
于是就有了 I / P / B 帧和 GOP 的概念,这个必须懂,因为它直接决定剪切精度:
| 帧类型 | 内容 | 独立可解码 | 体积 |
|---|---|---|---|
| I 帧(关键帧) | 完整一帧图像 | ✅ | 最大 |
| P 帧 | 参考前方帧的差值 | ❌ | 中 |
| B 帧 | 参考前后帧的差值 | ❌ | 最小 |
GOP(Group of Pictures) = 两个 I 帧之间的帧数。GOP 越长压缩率越高,但可定位点越稀疏。这解释了 FFmpeg 里两个反复出现的现象:-c copy 剪切只能落在 I 帧上(不然后面的 P 帧找不到参考);直播低延迟要把 GOP 设短(-g 48)而点播存 archive 可以把 GOP 拉长换码率。
2.3 图像与「非音视频」多媒体
多媒体不等于影音。以下几类在工程里也常需要 FFmpeg 处理:
| 格式 | 特性 | 用在哪 |
|---|---|---|
| PNG | 无损、支持 alpha | UI、截图 |
| JPEG | 有损、色彩管理复杂 | 照片 |
| WebP | 有损+无损、支持动画 | 网页(FFmpeg 9.0 起原生解动画 WebP) |
| AVIF | 基于 AV1,压缩最强 | 新网页 |
| HEIC | 基于 HEVC,苹果默认照片格式 | iPhone 相册 |
| BMP/TIFF | 简单/专业 | 中间产物、印刷 |
| SVG | 矢量文本,不是位图,FFmpeg 不解 | 图标 |
顺带澄清一个高频误解:SVG 不是「图片格式」的同级概念,它是 XML 描述的几何图形;而 GIF 也不是视频。同理 MIDI 不是录音,是乐谱。
2.4 音频:先懂三个参数,再看格式
音频比视频简单,因为声音只有一个维度。决定未压缩数据量的只有三件事:
1 | 每秒字节数 = 采样率 × 位深 ÷ 8 × 声道数 |
- 采样率:每秒采多少个点。44.1k(CD)、48k(视频工业标准)、16k(语音/ASR)、8k(电话)。按奈奎斯特定理,能记录的最高频率是采样率的一半,所以 8k 电话音质天然录不下 4kHz 以上的声音——这也是电话里人声「闷」的根本原因。
- 位深:每个点用几个 bit 量化幅度。16 bit ≈ 96 dB 动态范围,24 bit ≈ 144 dB(实际受噪声限制)。人耳听不出区别,但混音时留 headroom 需要它。
- 声道:单声道(mono)、立体声(stereo)、5.1、7.1,以及基于对象的 Atmos / DTS:X。语音场景立体声是纯浪费——多一倍体积,还多一倍 ASR 前处理麻烦。
| 编码 | 类型 | 典型码率 | 延迟 | 主要战场 | 备注 |
|---|---|---|---|---|---|
| PCM | 无压缩 | 700–1400 kbps | 极低 | 中间处理、WAV | 体积大 |
| MP3 | 有损 | 128–320 kbps | 高(约 50 ms+) | 音乐分发、老设备 | 1990s 之王,专利已过期,编码慢 |
| AAC | 有损 | 96–256 kbps | 中 | 苹果生态、流媒体、手机录音 | MP3 的官方继任者,同码率明显更好 |
| HE-AAC | 有损 | 24–64 kbps | 中 | 广播、低带宽 | AAC + SBR/PS 参数立体声 |
| Opus | 有损 | 6–510 kbps | 极低 | WebRTC、语音通话、游戏 | IETF 标准、免版税、一个格式通吃语音和音乐 |
| Vorbis | 有损 | 80–300 kbps | 中 | Ogg、游戏音效 | 免版税,被 Opus 抢了位置 |
| FLAC | 无损 | 500–900 kbps | 中 | 音乐存档 | 压缩比约 50–60%,工业标准 |
| ALAC | 无损 | 类似 FLAC | 中 | 苹果生态 | 语音备忘录「无损」档就是它 |
| APE/WavPack/TAK | 无损 | — | — | 发烧友 | 小众 |
| AMR-NB/WB | 有损(语音专用) | 4.75–12.2 / 6.6–23.85 kbps | 极低 | 2G/3G 通话 | 只为人声优化,放音乐效果灾难 |
| G.711 / G.722 | 有损(电话) | 64 / 48–64 kbps | 极低 | VoIP、PSTN | 电话网的通用语言 |
| AC-3 / E-AC-3 | 有损多声道 | 384–640 / 可变 | — | DVD / 蓝光 | 杜比数字 |
| DTS / TrueHD / Atmos | 多声道 | 高 | — | 影碟 | 环绕声 |
| WMA | 有损/无损 | — | — | 老 Windows | 边缘化 |
| MIDI | 不是录音 | 极小 | — | 电子乐谱 | 存的是「哪个键按多久」 |
| Silk v3 | 有损(语音) | 2.4–40 kbps 自适应 | < 20 ms | 微信/QQ 语音 | 专有封装,下一章细说 |
一句话原则:无损(FLAC/ALAC/WAV)用于存档和再处理;有损(AAC/Opus/MP3)用于分发;超低码率实时语音(Opus/AMR/Silk)用于通话。有损格式之间的互相转换不可逆——把 MP3 转成 FLAC 不会得到无损音质,只是把已经丢掉的东西用更大的盒子重新装一遍,体积翻几倍而信息量为零。这是格式转换里最常见的一种徒劳。
三、手机录出来的音频,到底是什么格式
现在回答第二个问题。手机里其实有三套完全独立的录音链路,用的是三种不同的编码,这是很多人困惑的根源。
3.1 第一套:自带「录音机 / 语音备忘录」App
iPhone(语音备忘录):默认是 AAC 编码、封装在 .m4a(ISO BMFF 容器)里。苹果开放了一个音质开关,路径是 设置 → 语音备忘录 → 音频质量:
已压缩:AAC,体积小,日常够用(默认档)无损:ALAC(Apple Lossless),仍然写成.m4a容器,但里面的编码从 AAC 换成了 ALAC
注意两点:一是这个设置只影响之后的录音,已经录完的文件不可能「逆向变无损」;二是无损档体积会明显变大,长录音涨得很快。想拿到原始文件,用录音界面 ... 菜单的「存储到文件」,或 AirDrop;不要走微信传,会被二次压缩。iPhone 上还有「增强录音」降噪开关,本质是软件层的波束成形与降噪,不改变落盘格式。
Android:情况复杂得多,因为原生录音 App 由各厂商自己实现。
Android 官方框架层(MediaRecorder)保证可用的编码器是:AAC-LC(写成 .m4a/.mp4/.3gp)、AMR-NB / AMR-WB(写成 .3gp/.amr)、PCM/WAV(.wav,Android 4.1 起可编码)、FLAC(仅解码)。有个耐人寻味的空缺:Android SDK 长期不内置 MP3 编码器(专利授权原因),只内置解码器。所以在安卓上想录 MP3,往往要靠厂商私有 codec 或第三方 App 里自带的 LAME。
落到实际机型上,各家录音 App 的默认输出大致是:
| 品牌/系统 | 常见默认落盘 | 典型参数 |
|---|---|---|
| iPhone 语音备忘录 | .m4a(AAC),可切 .m4a(ALAC) |
44.1 kHz |
| 小米 / Redmi | .m4a 或 .aac |
48 kHz,录音机可选「高品质」 |
| 华为 / 荣耀 | .m4a(较老机型有 .amr) |
48 kHz |
| vivo / iQOO / OPPO | .m4a |
48 kHz |
| 三星 | .m4a |
48 kHz |
| 原生 AOSP / 部分工程机 | .3gp(AMR) |
8k 或 16k |
| 第三方录音 App(RecForge、Ferrite 等) | .wav / .flac / .ogg(Opus) 自选 |
可自定义 |
规律很清楚:近几年的安卓和 iOS 都收敛到了 .m4a 容器 + AAC 编码。所以你现在拿起手机录一段音,最可能拿到的就是 .m4a。但「最可能」不等于「一定」——跨机型时永远不要假设,先 ffprobe 一下。这一条我在实际项目里吃过亏:同一批采集数据里混着 .m4a(AAC 48k)、.wav(PCM 16k) 和 .amr(AMR-NB 8k),用一条固定参数的命令批处理,结果一部分文件被上采样、一部分被下采样,喂进 ASR 后准确率莫名掉了一截。
3.2 第二套:视频录像的伴音
手机拍视频时,音频走的是 CamcorderProfile 规定的路径,Android 官方推荐档位就是 AAC-LC:低画质档单声道 24 kbps,标准画质立体声 128 kbps,720p 立体声 192 kbps,采样率 8–48 kHz。这也是 MP4 里最常见 h264 + aac 组合的来源。
顺带一个对做 Web 端播放器有用的规范约束:3GP/MP4 做流式播放时,moov box 必须排在 mdat 之前。这一条正是第六章 -movflags +faststart 的由来。
3.3 第三套:IM 语音消息——完全不同的一层
这是最大的坑。微信、QQ 的语音消息不是标准 AMR,也不是 AAC。它们底层用的是从 Skype 开源出去的 Silk v3 编码器(后来与 CELT 合并成 Opus,但 v3 版本仍在用)。
关键特征是:后缀是伪装出来的。微信语音文件常以 .amr、.aud 结尾,QQ 语音是 .slk,但文件头写着真话:
1 | 微信语音文件头: #!SILK_V3 (9 字节) |
Silk 的压缩思路很「针对人声」:不记波形,而是记「账」——用线性预测(LPC)描述声道这个滤波器,用长时预测(LTP)记录基音周期,只对预测误差做噪声整形量化(NSQ)再算术编码。代价是极低码率(自适应 2.4–40 kbps)和极低延迟(< 20 ms),收益是弱网通话不断音;但代价二是FFmpeg 官方构建并不内置 Silk v3 解码器,所以任何常规播放器、剪辑软件、转码服务对着这批文件全线阵亡,必须先用 Silk SDK 解成裸 PCM(s16le、默认 24 kHz、单声道)再交给 FFmpeg。社区有 silk-v3-decoder 这类项目专门补这段路。
所以:如果你要做「语音文件批量转写」这类需求,第一步不是写 FFmpeg 命令,而是先确认文件头到底是不是标准编码。 一个 head -c 16 文件名 | xxd 能省掉两小时。
3.4 为什么手机偏爱 AAC 而不是 MP3
四个理由,都不是「厂商随便选的」:
- 同码率音质明显更好。MP3 的滤波器组和码位分配是 1990 年代初的产物,在低码率下容易出现「水声」伪影;AAC 允许每帧独立选择立体声/联合立体声、精度分级。
- 可伸缩性。AAC 家族有 LC、HE-AAC(v1,加 SBR 高频重建)、HE-AACv2(再加 PS 参数立体声)、ELD(低延迟),同一个标准能从 16 kbps 电话质量覆盖到 320 kbps 高质量。
- 延迟与硬件原生支持。手机 SoC 里都有 AAC 硬编解码模块,功耗极低,这是续航敏感的移动设备必需的。
- MP3 编码器在安卓上因专利问题长期缺位,而 AAC 有官方支持。
3.5 录音参数该怎么选(工程视角)
| 目的 | 采样率 | 声道 | 编码 | 理由 |
|---|---|---|---|---|
| 语音转文字(ASR) | 16 kHz | 单声道 | 16 bit PCM 或 AAC 128k | 绝大多数 ASR 模型内部就是 16k;给 48k 只会白算一遍重采样 |
| 会议/采访留档 | 48 kHz | 单声道或立体声 | FLAC 或 AAC ≥160k | 留重处理余地 |
| 音乐 / 歌唱 | 48 kHz 或更高 | 立体声 | 无损(WAV/FLAC) | 后期混音需要 headroom |
| 网络分发 / 前端播放 | 44.1k 或 48k | 立体声 | AAC 128–192k 或 Opus 96k | 兼容性与体积平衡 |
| 实时通话 | 16–24 kHz | 单声道 | Opus / Silk | 低延迟优先 |
| 手机随手录 | 默认 | 默认 | 默认(AAC/.m4a) | 后续用 FFmpeg 统一归一化即可 |
这里有一个反直觉但很重要的实践建议:如果你要在手机上录音然后再处理,不要为了「音质」去开无损档,而要为「下游用途」去选格式。给 ASR 用的一路 48 kHz 立体声 ALAC,转写准确率不会比 16 kHz 单声道 AAC 高一分一毫,文件却大十几倍;而真正影响识别效果的,往往是麦克风格栅被手指挡住、房间混响、以及离嘴太远——这些是格式完全救不了的。
四、FFmpeg 是什么:What
4.1 一句话定义与出身
FFmpeg 是一个免费开源、跨平台的音视频处理框架,以及基于这个框架构建的一整套命令行工具。
它由法国程序员 Fabrice Bellard(用的化名是 Gérard Lantau)在 2000 年发起,首次发布 2000 年 12 月 20 日,最早的目标是写一个能对抗昂贵商业编码器的开源 MPEG-4 编码器。名字里的「FF」是 Fast Forward(快进),是对 MPEG 标准组织开的一个玩笑——FFmpeg = 快进版的 MPEG。它的 logo 是一段之字形扫描图案,恰好是 MPEG 熵编码读取 DCT 系数的顺序,属于典型的工程师式冷幽默。
项目与 MPlayer 社区血缘极深,很多开发者同时是两边成员。2004 年起由 Michael Niedermayer 主导。2011 年 3 月因治理分歧,一部分开发者分裂出去另建了 Libav 项目(Debian 一度把命令改名为 avconv),后来 Libav 已停止活跃开发,FFmpeg 重新统一了这条线——这也是今天你看到的 ffmpeg 命令而不是 avconv 的原因。协议上是 LGPL 2.1+,如果编译时链入 x264 等 GPL 组件则整体变为 GPL。
写本文时的最新版本是 9.0「Lei」(2026 年 8 月 4 日发布,补丁版 9.0.2)。这个代号破例没有用科学家名字(此前依次是 al-Khwarizmi、Ada、Lorentz、Riemann、Von Neumann、Dijkstra、Péter、Huffman、Hoare),而是纪念中国传媒大学的博士生雷霄骅——他在 2013–2016 年间用中文博客逐行拆解 FFmpeg 源码、写了三百多篇带完整可编译示例的文章,帮助了整整一代中文音视频工程师入门,2016 年因过劳猝逝,年仅 26 岁。提案里那句话被社区反复引用:
传播知识的人,和创造知识的人一样值得被记住。
9.0 本身也是二十年来最大的一次地基重构:七个库的 ABI 全部提升主版本号(libavcodec 到 63),swscale 被彻底重写为「算子分解 + 后端编译」架构(新增 x86 SIMD、AArch64 NEON、Vulkan 等五个后端),Vulkan 计算编解码铺开,新增原生动画 WebP 解码,移除了一批积累多年的废弃选项。这些变更会影响老脚本,我列在第七章。
4.2 组成部分:三个可执行程序 + 一个库族
FFmpeg 不是一个单体程序,而是「一个库族 + 它的三个命令行门面」。
| 组件 | 作用 |
|---|---|
| ffmpeg | 主程序,转码/剪辑/混流/滤镜/采集/推流 |
| ffprobe | 只分析不输出媒体,查流信息、帧信息、时间戳 |
| ffplay | 基于 SDL 的极简播放器,写代码时用来快速验证一条流能不能解 |
| 曾经的 HTTP 流媒体服务器,因内部 API 维护成本高,2018 年 1 月被移除(替代品是把输出指到 HLS/DASH 切片或用 nginx-rtmp) |
| 库 | 职责 |
|---|---|
libavutil |
基础件:内存、日志、数学、AVFrame/AVPacket/AVRational 等核心数据结构 |
libavcodec |
心脏:数百种编解码器的实现(含原生 FFV1、Snow),汇编优化,硬件加速接口 |
libavformat |
容器:mux(封装)与 demux(解封装),也管协议层(HTTP/RTMP/HLS/RTP) |
libavfilter |
滤镜图:缩放、裁剪、水印、变速、降噪、字幕烧录、混音、画面拼接 |
libswscale |
图像缩放 + 像素格式/色彩空间转换(YUV ↔ RGB) |
libswresample |
音频重采样(48k→16k)+ 采样格式 + 声道布局转换 |
libavdevice |
设备采集:Linux V4L2、Windows DirectShow、macOS AVFoundation |
libpostproc |
老式视频后处理(去块、去噪声) |
理解了这张表,就理解了 FFmpeg 的哲学:每一层都能单独调用。你在命令行里写的每个参数,其实都在指定「用哪个 demuxer、哪个解码器、哪些滤镜、哪个编码器、哪个 muxer」。
4.3 一条数据流水线:记住这个顺序
任何一次转码,内部都是这条链:
1 | 输入文件/流 |
几乎所有 FFmpeg 参数都能挂到这条链的某一环上。这也是为什么它能「用一条命令做无穷多的事」——它是一条可自由配置的流水线,不是一个功能菜单。
4.4 谁在用它
FFmpeg 的地位接近「多媒体领域的 libc」。你几乎一定用过它而不自知:
- 播放器:VLC、mpv、MPlayer、PotPlayer、KMPlayer 的解码后端
- 剪辑/创作:HandBrake、Shotcut、Kdenlive、OBS Studio(推流编码)、Blender、GIMP、Audacity
- 浏览器:Chrome/Firefox 处理部分非专利格式的解码路径
- 平台服务:YouTube、Bilibili、TikTok 等视频上传后的转码集群,历史上都大量依赖 FFmpeg
- AI 工具链:OpenAI 的 Whisper 自己不解码音频文件,它调用 FFmpeg 把 mp4/m4a/mp3 先解成 16 kHz 单声道 PCM,再送进模型。moviepy、torchvision、decord、很多 TTS 数据集预处理脚本同样依赖它
- 手机系统:Android 的媒体框架本身就带 FFmpeg 衍生代码
一句话:FFmpeg 是音视频行业的事实标准基础设施。这也是学它最大的投资回报——你学的不是「一个工具」,而是整个行业的通用中间层。
五、为什么要用 FFmpeg:Why
5.1 如果没有它
真实世界的格式是碎片化的:视频规范十几套、编码标准十几套、容器十几种,再乘以厂商的私有扩展(iPhone 的 HEIC、安卓的 Silk、监控的 H.264 变种)。如果没有统一层,工程师要面对的处境是:
- 想播一个文件 → 得先判断它是哪种容器套哪种编码,再引对应 SDK
- 想批量转换 → 每个格式一个工具、一套参数
- 想跨平台 → Windows/mac/Linux/Android 的媒体 API 完全不同
- 想省成本 → 商业编码器授权费按路数或按量计
FFmpeg 提供的解法是:把全部复杂度吃进一个库,对外只暴露一个入口、一套抽象。你的程序只需要说「输入这个、输出那个、参数是这样」。
5.2 四条核心价值
① 一个入口通吃所有格式。 一条 ffmpeg -i input -c:v libx264 output.mp4 里,输入侧可以是 mp4/mkv/ts/webm/RTMP 地址/摄像头设备,输出侧可以是任意容器。这个 -i 前后可以装下整个多媒体世界。
② 可编程、可批量、可嵌入。 这是 GUI 工具永远跨不过去的门槛。真实需求往往是「每天早上把 OSS 上新增的 500 个录音全部转成 16k 单声道 WAV 再喂给 ASR」,而不是「把这一个视频转成 MP4」。FFmpeg 是一个进程,能被 subprocess 调用,能挂 cron,能进 CI,能封成 HTTP 服务。
③ 免费开源、跨平台、无部署成本。 单个静态可执行文件,几百行代码的部署难度。Windows/Linux/macOS 命令语法一致。
④ 参数级完全可控。 GUI 工具为了易用,把码率、GOP、preset、色彩空间、声道布局全藏起来了。当你遇到「B 站上传后画面偏色」「手机播放没声音」「同一段视频有人卡有人顺」这类问题时,只有能直接操作这些参数的工具才能定位。
5.3 和其他方案对比
| 方案 | 上手难度 | 批量/自动化 | 可控性 | 成本 | 适合谁 |
|---|---|---|---|---|---|
| 格式工厂、Adaptive、ShanaEncoder(GUI) | 极低 | ❌ 手动 | 低 | 免费/低价 | 偶尔转一个文件 |
| 剪映 / PR / 达芬奇 导出 | 低 | ❌ | 中(但藏在菜单里) | 订阅/买断 | 做内容,非做系统 |
| 云转码服务(MPS / 阿里云媒体处理) | 低 | ✅ | 中 | 按量付费,长期贵 | 不想自建、峰值波动大 |
| 专用单功能工具(mkvmerge、HandBrake) | 低 | 部分 | 中 | 免费 | 只做某一件固定事 |
| FFmpeg CLI | 高 | ✅✅ | ✅✅ 全暴露 | 免费 | 要处理 100 个文件或要写进程序 |
| FFmpeg 库(C API) | 很高 | ✅ | ✅✅✅ | 免费 | 自研播放器/转码服务 |
选择标准其实很朴素:
一年转两次视频,别学 FFmpeg,用 GUI。
一周转二十次,学十条命令就够。
要写进代码里、要处理一批文件、要复现别人的一个效果——FFmpeg 是唯一答案。
5.4 要付出的代价
诚实地说清楚缺点,才不会让人学一半就骂:
- 学习曲线陡。不是「参数多」的问题,而是它要求你先建立第六章那套「容器/编码/流/时间戳/滤镜图」的心智模型。不懂 GOP 就永远理解不了为什么
-c copy剪不准。 - 参数命名不一致。
-c:v、-b:a、-ac、-ar、-vf、-af是缩写历史堆积,没有统一规律,只能靠查。 - 报错信息对新手不友好。
Invalid data found when processing input是最常遇到的一句话,而它可能意味着文件损坏、缺解码器、容器不匹配中的任何一种。 - 文档分散且部分过时。官方文档准确但极其干燥;网上教程良莠不齐、大量抄自十年前的版本;很多参数在新版本被弃用(下面第七章)。
- 默认构建不一定含所有编码器。精简版可能没有
libmp3lame、libx265、libfdk_aac,报Unknown encoder时先想这个,别怀疑语法。
5.5 一个真实的技术判断
FFmpeg 的强大是「通用性」,代价是「它不是所有场景的最优解」。硬件编码器(NVENC/QSV/VideoToolbox)在同等 CPU 占用下能给出的吞吐,与软件 x264 走完全相同参数得到的压缩率之间要权衡;专业调色需要达芬奇而不是滤镜;做非破坏性快剪需要的是剪辑软件。
FFmpeg 的定位是流水线和胶水,不是创作台。 想清楚这一点,就不会拿它做不该它做的事。
六、怎么样:How
这一节从装到能用,最后用一个完整例子把全篇串起来。
6.1 安装
Windows(三种,推荐第一种):
1 | # ① winget(最简单) |
macOS / Linux:
1 | brew install ffmpeg # macOS,会带上 x264/x265/opus/lame 等 |
Python 项目内(不想让每个部署环境手动装时):
1 | pip install imageio-ffmpeg # 自带一个静态 ffmpeg,可执行文件路径由 API 提供 |
验证:
1 | ffmpeg -version |
第一行看版本,重点看下面几行里有没有 --enable-libx264、--enable-libmp3lame、--enable-libopus——这决定了你的构建能用哪些编码器,绝大多数 Unknown encoder 报错都能靠这一行答案解决。
6.2 最重要的能力不是背参数,是会查
四条自查命令,比背 200 条参数有用得多:
1 | # ① 这个文件里到底是什么?(先看这个再动手) |
遇到任何网上抄来的命令跑不通,第一反应应该是查本地版本的帮助,而不是怀疑自己。-h full 能列出所有选项。
6.3 命令骨架,以及一条铁律
1 | ffmpeg [全局选项] [-i 输入1] [选项...] [-i 输入2] [选项...] ... 输出文件 |
铁律:所有选项只对写在它之后的第一个文件生效。 这是新手第一大坑。
1 | # ✅ 正确:-ss 在 -i 之前 → 属于「输入侧」选项,先定位再解码,又快又准 |
6.4 高频参数速查表
| 参数 | 含义 | 备注 |
|---|---|---|
-i |
输入 | 可以有多个,之后用 0:、1: 索引 |
-c copy |
不重编码,直接搬运流 | 改封装专用,秒级完成,质量零损失 |
-c:v libx264 / -c:a aac |
指定视频/音频编码器 | :v :a :s 分别选流类型 |
-crf 23 |
恒定质量(0–51,默认 23) | 越小质量越好、文件越大;23 是 x264 默认,HEVC 默认 28 |
-preset medium |
速度/压缩率权衡 | ultrafast → veryslow,越慢压得越小 |
-b:v 2M / -b:a 128k |
目标码率 | 用于 CBR/2-pass 场景 |
-r 30 |
帧率 | |
-s 1280x720 / -vf scale=1280:-2 |
分辨率 | -2 保证是偶数(编码要求) |
-ar 16000 |
音频采样率 | |
-ac 1 |
声道数 | 1=单声道,2=立体声 |
-pix_fmt yuv420p |
像素格式 | 不写这个经常出现「本地能播、别人打不开」 |
-ss / -t / -to |
起始时间 / 时长 / 结束时间 | -t 和 -to 别同时用 |
-vn / -an / -sn |
丢弃视频/音频/字幕 | 提音频用 -vn,静音用 -an |
-map |
手动选流 | -map 0:a:1 取第 1 个输入的第 2 条音频轨 |
-vf / -af |
视频/音频滤镜链 | 逗号串联 |
-filter_complex |
多输入多输出的滤镜图 | 需要标签时用 |
-movflags +faststart |
把 moov 挪到文件头 | 网上可播/手机可播的关键 |
-metadata title="x" |
写元数据 | |
-map_metadata -1 |
清空元数据 | 抹掉拍摄设备、GPS 等信息 |
-y / -n |
覆盖 / 拒绝覆盖已存在的输出 | 默认是交互式询问 |
-loglevel error -stats |
只看错误和进度 | 输出干净,脚本里必加 |
-tune film|animation|grain |
场景微调 | x264/x265 支持 |
6.5 一个完整的实战例子:手机会议录音的三件产物
场景(很真实,也是我做项目时踩过的):用 iPhone 录了三段会议 .m4a,现在需要三种输出——
meeting.mp3:合并成一个、音量归一化,扔车里/蓝牙耳机听;meeting_16k.wav:16 kHz 单声道 PCM,喂给 Whisper 做转写;meeting.png:一张波形图,放进周报里说明「这次会开了多久、有没有大段空白」。
第 0 步:先搞清楚手里是什么。
1 | for %f in (*.m4a) do @ffprobe -v error -show_entries \ |
结果:三段都是 aac / 44100 Hz / 2ch。注意它们采样率一致,这是好事——如果第一段是 44.1k、第二段是 48k,就不能用 -c copy 拼接(同一容器里塞两条不同采样率的音频流,播放器会当成同一条处理,结果要么变速要么静音)。这是最容易翻车的地方。
第 1 步:统一参数(保险做法,也顺便省掉后续所有坑)
1 | ffmpeg -y -i m1.m4a -ar 48000 -ac 1 -c:a pcm_s16le u1.wav |
第 2 步:拼接。两条路:
1 | # 路线 A:concat demuxer(快,但要求各文件参数完全一致,且用同一种容器) |
我平时用路线 B 更多——它把「参数归一化」和「拼接」合成一步,不需要中间文件,也不怕清单文件路径写错。路线 A 快在不需要解码重编,几十分钟的录音几秒完成。
第 3 步:产出 ① 可听的 MP3(带响度归一化和淡入淡出)
1 | ffmpeg -y -i joined.wav \ |
三个要点:
loudnorm=I=-16:TP=-1.5:LRA=11是 EBU R128 响度归一化,I是综合响度(流媒体常用 -14 到 -16 LUFS)。不做这一步的话,三段录音因为麦克风距离不同,音量会一段响一段轻,听感极差。这是比「提高采样率」重要一百倍的音质改善。-q:a 4是 VBR 模式,约 165 kbps,语音足够;固定码率写法是-b:a 192k。afade的st=5390要按实际时长算(这里假设总长 5393 秒)。想要自动,可以先用ffprobe -v error -show_entries format=duration -of csv=p=0 joined.wav取值再拼进命令。
第 4 步:产出 ② ASR 输入 WAV
1 | ffmpeg -y -i joined.wav -ar 16000 -ac 1 -c:a pcm_s16le -sample_fmt s16 meeting_16k.wav |
pcm_s16le 是「16 bit 小端无压缩 PCM」,wav 是它的容器。这是几乎所有 ASR 模型的通用输入格式。顺带做个诊断——找出录音里超过 3 秒的静音段(判断是不是有台设备根本没录上):
1 | ffmpeg -i joined.wav -af "silencedetect=noise=-35dB:d=3" -f null - 2>&1 | findstr silence |
第 5 步:产出 ③ 波形图
1 | ffmpeg -y -i joined.wav -filter_complex "showwavespic=s=1600x400:colors=#c8a064" \ |
顺手把整条链封成一个 Python 函数:
1 | import subprocess, shlex |
一个例子讲完了全篇的核心概念:容器与编码是两层(第 0 步的探测)、参数归一化先于合并(采样率不一致不能 copy)、流选择与滤镜图([0:a]、concat)、响度是比码率更值音质杠杆(loudnorm)、下游用途决定格式(16k/单声道给 ASR)、FFmpeg 可以当可编程的中间件(Python 封装)。
6.6 十余组高频命令
以下按使用频率排序。全部只依赖语法通用性,跑不通先回 6.2 查本地帮助。
1 | # 1|改封装不重编码(最快,零质量损失) |
6.7 两遍编码:需要精确目标体积时才用
CRF 是「质量恒定、体积随缘」;如果目标是「必须压到 50 MB 以内」,就要用固定码率 + 两遍:
1 | # 第一遍只统计复杂度,不输出文件(Linux/macOS 用 /dev/null,Windows 用 NUL) |
不过今天更多人的做法是:-crf 试一次,看输出体积,再按线性关系微调用 -b:v。两遍编码在多数场景性价比已经不高。
七、六个反复咬人的坑
坑 1|输出文件名和输入是同一个 —— 文件会被毁掉。
FFmpeg 打开输出时就 truncate 目标文件,之后再读输入。同名等于自杀,而且边读边写常导致输出损坏。
✅ 对策:永远输出到新文件名或子目录;需要「原地转」就先写临时文件再替换(脚本里 move /y 或用 tmp_ 前缀)。-y 只是覆盖已存在的输出,不会保护同名输入。
坑 2|-c copy 剪切不精确。
不是 bug,是编码原理决定的——只能从 I 帧开始。
✅ 对策:要么接受误差、要么重编码、要么先用 -force_key_frames 在需要的点插入关键帧再 copy。
坑 3|mp4 的 moov 在文件尾部 → Windows 播放器/微信/手机打不开,网页不能边下边播。
这是我印象最深的一个坑:同一份脚本产出的 mp4,本地 ffplay 一切正常,发到 Windows 上就是打不开。原因是默认写法把索引表(moov)写在文件末尾,任何需要 seek 的播放端必须先能读到结尾。
✅ 对策:**所有输出 mp4/mov 一律加 -movflags +faststart**。
✅ 修已存在的文件,无损且秒完:ffmpeg -i bad.mp4 -c copy -movflags +faststart good.mp4。
✅ 顺带说明为什么第六章的音频产物是 .mp3 / .wav——那两类没有 moov 问题,所以「发不出去的 mp4」这一类坑在纯音频工作流里通常以别的形式出现。
坑 4|Unknown encoder 'libx265' / 'libmp3lame'。
不是你写错了,是这个 FFmpeg 构建没编进那个第三方库。
✅ 对策:ffmpeg -hide_banner -codecs | findstr 265 确认;缺就换一个完整构建(gyan.dev 的 -full 版本、conda install -c conda-forge ffmpeg、或自己带 --enable-libx265 编译)。
坑 5|画面没问题但「只有声音没有图像」或「发出去打不开」= 像素格式。
编码器默认可能输出 yuv444p / 10 bit,而绝大多数硬件解码器和播放器只保证 yuv420p 8 bit。
✅ 对策:加 -pix_fmt yuv420p。这是「我电脑能播,别人不能」最常见的两个原因之一(另一个是坑 3)。
坑 6|音频进 ASR 效果差,但明明录音很清晰。
通常是采样率/声道与模型预期不匹配,或者用了 aresample 之外的错误下采样路径(比如直接把 48k 当 16k 用,声音会变慢变调)。
✅ 对策:**显式写 -ar 16000 -ac 1**,并在处理前用 ffprobe 打印真实采样率;不要相信文件名和来源设备的「默认设置」,尤其当数据来源混杂了不同品牌手机。
额外一条|版本导致的「网上教程跑不通」。
FFmpeg 大约每 6 个月一个大版本,每次都会弃用和移除一批东西。9.0(2026 年 8 月)里几个会直接咬老脚本的变化:
- 移除 CLI 选项
-vsync(改用-fps_mode),一并移除-top、-qphist、-filter_complex_script; - 默认开始校验 TLS 对端证书——原来连自签证书能跑的拉流脚本会直接失败(临时可加
-tls_verify 0,但更该修证书); - 移除独立 CELT 解码器(不影响 Opus)、移除 Sonic 编解码器、移除 OpenMAX 与 libnpp 系滤镜(
scale_npp/transpose_npp等,N 卡改用transpose_cuda); - NVENC 移除旧 preset 别名与遗留码控模式,不再支持 11.1 之前的 SDK;
- 七个库 ABI 全部升主版本,自己链 libav* 的程序必须重新编译。
官方发布节奏是「主版本约 6 个月一次,中间点版本只修 bug」。生产环境建议锁定一个点版本(如 9.0.2),别追 git master;从源码自己编译的人,官方反而建议直接用 master,因为安全补丁先落在开发分支。想提 PR 的话注意:开发已迁到 code.ffmpeg.org(Forgejo 实例),GitHub 上的 PR 官方明确说会被忽略。
八、一张决策小抄
| 我要做的事 | 命令模板 |
|---|---|
| 换个容器(不损画质) | ffmpeg -i in -c copy -movflags +faststart out.mp4 |
| 压小体积、画质优先 | ffmpeg -i in -c:v libx264 -crf 24 -preset slow -c:a aac -b:a 128k -movflags +faststart out.mp4 |
| 目标更小的体积 | 同上换 -c:v libx265 -crf 28 -tag:v hvc1,或 -c:v libsvtav1 -crf 34 -b:v 0 |
| 剪一段(要精确) | ffmpeg -i in -ss 00:01:00 -to 00:01:30 -c:v libx264 out.mp4 |
| 剪一段(要快) | ffmpeg -ss 00:01:00 -to 00:01:30 -i in -c copy out.mp4 |
| 只要音频、给 ASR | ffmpeg -i in -vn -ar 16000 -ac 1 -c:a pcm_s16le out.wav |
| 只要音频、给别人听 | ffmpeg -i in -vn -af loudnorm=I=-16 -c:a libmp3lame -q:a 4 out.mp3 |
| 合成一段音频 | ffmpeg -i a -i b -filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[a]" -map "[a]" out.wav |
| 改分辨率 | ffmpeg -i in -vf scale=1280:-2 -c:a copy out.mp4 |
| 加水印 | ffmpeg -i in -i logo.png -filter_complex "overlay=W-w-20:H-h-20" -c:a copy out.mp4 |
| 烧字幕 | ffmpeg -i in -vf "subtitles=sub.srt" out.mp4 |
| 取封面帧 | ffmpeg -ss 0.5 -i in -frames:v 1 -update 1 cover.png |
| 查文件真实内容 | ffprobe -hide_banner in |
| 查本地版本支持什么 | ffmpeg -codecs / -formats / -filters / -h full |
结语
回头看这三件事:
多媒体格式不是一堆要背的名字,而是两个正交维度的组合——「怎么压」× 「怎么装」。任何格式名,你只要问出「它是容器还是编码」,位置就确定了。
手机录音之所以让人困惑,是因为手机里有三条独立链路:自带录音 App(AAC/ALAC in M4A)、视频伴音(AAC in MP4)、IM 语音(Silk v3,后缀是假的)。它们的取舍逻辑完全不同——前两条为「听」优化,第三条为「在弱网下实时说清楚一句话」优化,所以不能相互替代。
FFmpeg 之所以值得学,是因为它是唯一一个把这两个维度都完整暴露出来、并且可编程的工具。它的学习曲线陡,陡在需要你先把世界看成「容器 + 编码 + 滤镜 + 时间戳」,而不是看成「一个视频文件」。这个心智模型一旦建立,你获得的不只是会用一条命令,而是能判断「这个视频为什么打不开」这类问题的答案在哪。
而它背后那条人文线索也值得记一笔:一个 26 岁的中国博士生,用三年时间在博客上把 FFmpeg 拆成了三百多篇带可编译示例的中文教程;他去世后第十年,这个项目用他的名字命名了自己的主版本。开源世界对「把墙拆成梯子的人」的回报,就是把它写进版本号里。
动手建议:不要通读这篇就完事。找一段手机录音,跑一遍
ffprobe,看它的真实编码是什么,然后亲手做一遍第 6.5 节的三条输出。这一小时比看十篇教程有用。





