很多朋友第一次听说 FFmpeg,脑子里冒出来的问题基本都一样:这玩意儿到底是个工具还是门语言?为什么网上教程东一个命令西一个命令,看着就头疼?作为一个天天跟视频打交道、被各种格式折磨过无数回的老兵,我可以负责任地告诉你:FFmpeg 就是整个多媒体世界的"瑞士军刀",几乎所有你见过的视频播放器、剪辑软件、直播平台,底层都有它的影子。
这篇入门教程,我不打算丢给你几百条命令让你死记硬背,而是从一个零基础的视角,带你把这把"军刀"的刀柄、刀刃、用刀手法逐步摸清楚:怎么下载安装、怎么选版本、怎么用最核心的命令完成转码压缩、怎么把视频推向流媒体服务器,以及后面如果要搞 SDK 二次开发,路该怎么走。看完这一篇,你至少能独立处理掉 80% 的日常音视频需求,踩过的坑我也会一并告诉你。
1. FFmpeg 到底是什么?零基础该怎么看待它
1.1 一个命令行工具箱,不是"软件"
FFmpeg 本质上是一套开源的多媒体处理工具和底层库。它没有花哨的图形界面,核心交互方式就是在命令行里敲指令。很多新手一看到命令行就发怵,觉得这是程序员才会玩的东西。其实换个角度想,它更像是一台自动料理机——你只需要把食材(输入文件)、加工方式(命令参数)、成品要求(输出设置)告诉它,它就能哐哐给你做出来。命令行反而是它最强大的地方,因为一旦你习惯用它,效率比任何图形界面软件都高出一大截。
另外要注意,FFmpeg 从诞生之日起就是开源界的老前辈,跨平台能力极强,Windows、Linux、macOS 全覆盖。它既能处理 MP4、MOV、MKV、AVI、FLV 这些常见封装格式,也能搞定 H.264、H.265、VP9、AV1 这类视频编码,以及 AAC、MP3、Opus 这些音频编码。你要做格式转换、压缩瘦身、裁剪拼接、抽帧截图、提取音轨、混流推流,它全都能干。
1.2 三个核心成员:ffmpeg、ffprobe、ffplay
安装完 FFmpeg 之后,你会在目录里看到三个可执行文件,刚开始很容易混淆,所以我一口气讲清楚它们的职责。
- ffmpeg:主程序,负责所有转码、编辑、处理工作,绝大多数命令都是用它。
- ffprobe:探测器,用来查看视频文件的详细信息,比如编码格式、分辨率、码率、帧率、时长、音频参数等,就像给视频做一个"体检"。
- ffplay:播放器,直接把文件解码播放出来,方便你快速预览处理结果。
我平时排错最喜欢先用 ffprobe 把文件信息摸一遍,再决定后续怎么处理。举个例子,你拿到一个视频不知道它是什么编码、有没有音轨,跑一句ffprobe input.mp4,所有信息一目了然。这三个工具合起来,就是一套完整的音视频处理组合拳。
1.3 零基础该怎么学习 FFmpeg
建议不要一开始就去背那些看着很长的命令。你先理解下面这句话:FFmpeg 的命令,本质上是在描述"从哪读数据、用什么方式处理数据、往哪写数据"这三件事。抓住了这个主干,其他参数都是在这根主干上添枝加叶。
我在带新人的时候,通常会让他们先走通这几步:会安装、会查版本、会用 ffprobe 看信息、会做一次最简单的转码。只要这几步走通了,后面再复杂的操作,无非是在这条主干上不断叠加需求而已。千万别上来就复制一长串命令,出了问题根本不知道在改哪个参数。
2. 环境准备:下载、安装与版本选择
2.1 官网到底能不能下载"安装包"
很多人在搜索引擎里搜"FFmpeg 下载",然后兴冲冲跑到 ffmpeg.org,结果发现官网居然没有现成的 Windows 安装包,只有源码,一下就懵了。这是新手最容易卡住的第一关:FFmpeg 官网只提供源代码,不直接提供编译好的二进制文件。所以你要下载可直接运行的那个"ffmpeg.exe",实际上是从第三方构建站点获取的。
目前社区里比较主流、更新也比较及时的是 gyan.dev 和 BtbN 这两个构建源,一般默默下载就行。macOS 用户直接用 Homebrew,Linux 用户用系统自带的包管理器。我自己在 Windows 上一直用 gyan.dev 的 build,干净、稳定、还有完整的 release notes,实测没什么毛病。
2.2 Windows 平台安装实操(环境变量配置)
下载回来的 Windows 版本通常是一个压缩包,解压后里面就是 bin 目录下的 ffmpeg.exe、ffprobe.exe、ffplay.exe 三个文件。首先把它们放到一个固定的目录,比如D:\software\ffmpeg\bin。然后需要配置环境变量,否则每次使用都得切换到该目录下才能执行命令。
配置方法:右键"此电脑"→"属性"→"高级系统设置"→"环境变量",在"系统变量"里找到 Path,点"编辑",新增一条把D:\software\ffmpeg\bin加进去。配置完成后,打开新的命令行窗口,输入ffmpeg -version,能打印出版本信息就算成功。这一步一定要重开终端窗口,否则环境变量不会生效,我经常看到有人在老窗口里折腾半天以为装失败了。
2.3 macOS 和 Linux 的安装方式
这两个平台装起来比 Windows 省心得多。macOS 上如果你装了 Homebrew,一行命令就够了:brew install ffmpeg,官方仓库里的版本默认就带了 libx264、libx265 等常用编码器,所以日常使用基本不用额外折腾。
Debian/Ubuntu 系列 Linux 用sudo apt install ffmpeg,CentOS/RHEL 系列则用sudo yum install ffmpeg或者sudo dnf install ffmpeg,具体看发行版。装完之后同样用ffmpeg -version验证。这里友情提醒一句:仓库里预装的版本可能稍微旧一点,但胜在稳定,对入门来说完全够用。
2.4 GPL 和 LGPL 的区别,到底选哪个版本
这个点炒得挺热,但很多人没搞懂。简单说,FFmpeg 本身是双许可证:LGPL 版本更宽松,不要求你开放自己的源代码;GPL 版本则要求你在使用它的基础上,你的代码也必须以 GPL 协议开源。问题来了,像 libx264、libx265 这类最主流的编码器,它们本身是 GPL 协议的。如果你编译时把这些编码器一起编进去,整个 FFmpeg 就只能是 GPL 授权;如果你只编那些允许 LGPL 的组件,就不能用 libx264 这些编码器。
所以选择起来就很简单:如果只是自己做转码、压缩、推流这些内部工作,不会把你的程序往外分发,那就直接下载带 GPL 编码器(比如 gyan.dev 的 full 版),省事;如果你要用 FFmpeg 的库做商业软件,又想闭源发布,那必须认真研究 LGPL 配置,把非 LGPL 兼容的编码器全部剔除,并且可能需要自己编译。想省心就直接上 GPL 版,千万别后面跑命令时发现Unknown encoder 'libx264'再回头折腾。
3. 最常用的命令:转码、压缩、裁剪与提取
3.1 命令行的基本语法结构
先记住这个框架:
ffmpeg [全局参数] -i 输入文件 [输入相关参数] [处理参数] 输出文件你看到的大部分命令都逃不出这个骨架。-i就是指定输入文件,可以有多个输入文件,输出文件写在最后。参数的位置有时候挺讲究,比如放错位置可能导致读取参数的方式完全不同。新手阶段你不需要玩太多花样,照着下面的经典例子照猫画虎,跑通了再往里灵活套参数。
3.2 视频转码与格式转换实操
最基础的格式转换,比如把视频从 MP4 转成 AVI 或者 MKV:
ffmpeg -i input.mp4 output.avi这条命令不指定编码器,FFmpeg 会自己对每种格式挑选它认为"默认"的编码器。但这里有个坑:如果不做任何设置转换到 MKV,视频流很可能还是原来容器里的编码,只是换了个封装壳子。如果你想把 H.264 视频重编码为 H.265/HEVC,换取更好的压缩率,需要显式指定:
ffmpeg -i input.mp4 -c:v libx265 -c:a aac output_hevc.mp4-c:v指视频编码器,-c:a指音频编码器。注意 libx265 编码速度明显慢于 libx264,文件也未必永远更小,得看码率设置和源视频画的复杂度。
3.3 视频压缩与降低码率的参数详解
压缩视频是日常高频需求,核心思路就是限制视频码率或者提高质量参数 CRF。最简单粗暴的方式是直接限定平均码率:
ffmpeg -i input.mp4 -c:v libx264 -b:v 800k -maxrate 800k -bufsize 1600k -c:a aac -b:a 128k output_small.mp4-b:v是目标视频码率,-maxrate限制峰值,-bufsize是编码器的缓冲区大小,通常设为两倍目标码率。音频码率同样用-b:a控制。这套参数适合把一段高清视频压成适合网络传输的体积。但说实话,我更推荐CRF(Constant Rate Factor)模式:固定质量,让码率自己浮动,画质保持更稳定:
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output_crf.mp4CRF 值一般在 0~51 之间,数字越小质量越高,23 是 libx264 的默认值,28 左右肉眼就会开始看到明显劣化。-preset是速度/压缩比权衡,ultrafast最快但文件最大,veryslow最慢但压缩率最好。日常推荐 medium 或 slow。
3.4 截取片段、截图与音频提取
截取片段时,有一个非常关键的顺序问题:
- 把
-ss放在-i前面,是快速定位到关键帧位置再开始读取,速度快但起始精度可能偏差几帧; - 把
-ss放在-i后面,是解码到指定帧再截取,精度高,但速度慢。
我实际处理时,如果只是粗剪,一般用ffmpeg -ss 00:01:00 -t 00:00:30 -i input.mp4 -c copy output.mp4。-t表示截取时长,-c copy表示流拷贝,不做重编码,所以特别快。不过要注意,-c copy在剪辑点不是关键帧时,可能会出现开头画面卡顿或花帧,要尽量避免在 GOP 中间硬切。
截图的话,直接指定要截取的时间点,并输出一帧即可:
ffmpeg -ss 00:00:05 -i input.mp4 -frames:v 1 frame.jpg音频提取就更简单了:ffmpeg -i input.mp4 -c:a mp3 output.mp3。如果只想拿原始音频流,可以用-c:a copy,完全不重编码,秒导出。
3.5 常用参数的速查与注意事项
用表格把这几个高频参数列出来,方便你随时翻:
| 参数 | 作用 | 常见取值示例 |
|---|---|---|
-i | 指定输入文件 | -i input.mp4 |
-c:v | 指定视频编码器 | libx264、libx265、copy |
-c:a | 指定音频编码器 | aac、mp3、copy |
-b:v | 视频目标码率 | 800k、1M |
-b:a | 音频目标码率 | 128k、192k |
-crf | 固定质量因子 | 18~28 |
-preset | 编码速度与压缩率权衡 | ultrafast、medium、slow |
-r | 输出帧率 | 30、60 |
-s | 输出分辨率 | 1280x720、1920x1080 |
-t | 处理时长 | 00:00:30 |
-ss | 起始时间点 | 放在-i前/后效果不同 |
新手最容易犯的错误是搞混-r和-fps,其实 FFmpeg 中-r就是输出帧率。改分辨率用-s,注意宽高比保持一致,否则画面会被拉伸变形。处理高清视频时,如果 CPU 占用率已经 100% 还嫌慢,不要盲目开一堆任务,先检查是不是选了太慢的 preset 或者分辨率太大。
4. 实战案例:用 FFmpeg 推流到 SRS 并优化延迟
4.1 推流到 SRS 的场景与基础命令
很多人做直播或看了网上的一些方案,会把 FFmpeg 作为推流端,把视频推向 SRS(Simple Realtime Server)这类开源流媒体服务器。什么场景下会用到?比如你用 OBS 推流到自己的 SRS,想转推其他协议;或者你有一个视频文件,想模拟一路直播流做测试;再或者你需要多路输入源混流后再推出去。FFmpeg 都能干。
最朴素的推流命令如下(假设 SRS 的 RTMP 端口是 1935,应用名 live,流名是 test):
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -tune zerolatency -c:a aac -f flv rtmp://127.0.0.1:1935/live/test这里必须解释-re:它的作用是按文件的原始时间戳以恒定速率读取输入文件,模拟实时播放。如果不加这个参数,FFmpeg 会以尽可能快的速度读完文件并推出去,产生的就不是直播效果,而是"快进"了。-f flv是指定输出封装格式为 FLV,因为 RTMP 底层封装必须走 FLV。
4.2 延迟从哪来:关键帧、缓冲与 RTMP
延迟问题的本质,可以从三方面拆解:编码器缓冲、GOP 结构、传输协议缓冲。RTMP 本身基于 TCP,TCP 天然有累积确认机制,在网络抖动时会自动缓冲数据,这是延迟的重要来源之一。另外,播放端通常要拿到一个关键帧(关键帧、I帧,也就是 GOP 的起点)才能开始解码,如果关键帧间隔太长,加入直播时等待首屏的时间就越久。
所以网上搜"ffmpeg 推流到 srs 存在延迟"能翻出一堆讨论,其实绝大多数情况都能通过参数调优解决。核心思路就是:缩短 GOP、降低编码器延迟模式、尽量少用会让封包累积的缓冲机制。
4.3 延迟优化的具体参数设置
经过我大量实测,下面这套参数在推流到 SRS 时效果最稳定:
ffmpeg -re -i input.mp4 -c:v libx264 -preset ultrafast -tune zerolatency -g 50 -keyint_min 50 -sc_threshold 0 -b:v 1M -maxrate 1M -bufsize 2M -c:a aac -b:a 128k -f flv rtmp://127.0.0.1:1935/live/test拆开逐个讲:-tune zerolatency主要是去掉 x264 编码器的延时缓冲,让每一帧编码完尽量马上输出;-g 50表示每 50 帧设一个关键帧,如果视频是 25fps,就相当于 2 秒一个关键帧;-keyint_min 50保证最小间隔也是 50 帧;-sc_threshold 0是关闭场景自动切换关键帧,避免意外插入 I 帧打乱节奏。这些配合起来,加上播放端也把缓冲尽量调小,整体端到端延迟基本能在可接受范围内。
还有一个小技巧:如果你推送的是 H.264 视频流,编码器参数尝试-x264-params "nal-hrd=cbr"配合固定码率,能减少因码率波动带来的缓冲抖动。我自己在局域网环境实测,延迟大概能降到几百毫秒级别,放在公网上主要瓶颈就变成网络质量了。
4.4 验证延迟与实际效果
调参之后怎么看效果?我一般用 ffprobe 去拉流,看实际到达的流信息是不是符合预期:
ffprobe rtmp://127.0.0.1:1935/live/test能正常输出视频流信息,说明推流路径是通的。再用 ffplay 直接播放同一路流:ffplay rtmp://127.0.0.1:1935/live/test,在画面上放一个秒表或者用手机计时,对比实际画面和真实时刻的偏差,就是肉眼可见的延迟估算值。优化前后对比,你会明显感到首屏更快了、画面卡顿少了。
另外注意,延迟并不只由 FFmpeg 推流端决定,播放器的缓冲策略、SRS 服务器配置、网络中间设备都有很大影响。如果已经调了推流参数还是高延迟,去服务端看看 chunk_size 等相关配置,别只盯着推流端。
5. 进阶之路:FFmpeg SDK 与二次开发
5.1 FFmpeg SDK 到底是什么
如果你已经不满足于敲命令,想在自己的程序里集成 FFmpeg 的能力,那就要接触它的 SDK 部分。FFmpeg 除了一套命令行工具,还提供了一堆底层库,比如 libavformat 负责封装和解封装、libavcodec 负责编解码、libavfilter 负责滤镜处理、libavutil 提供辅助工具、libswscale 负责图像缩放与格式转换、libswresample 负责音频重采样。
这些库加起来就是一套完整的媒体处理 SDK。你用 C 或者 C++ 调用它们,就可以写出自己的剪辑软件、直播推流工具、音视频分析器。SDK 的"下载"其实就是你把 FFmpeg 的源码用特定工具链编译成静态库或者动态库,然后在自己的工程里链接。网上说的ffmpeg sdk 下载,一般指的就是引入编译产物或源码包。
5.2 MSVC 环境下编译 libx265 的踩坑记录
在 Windows 上用 MSVC(Visual Studio 的 C/C++ 编译器)编译 FFmpeg,同时还要启用 libx265,是我见过把很多人劝退的操作。这里我把大概步骤和坑摊开说一遍。
首先,你需要一个完整的 Visual Studio(带 C++ 桌面开发组件),然后下载 FFmpeg 源码。x265 的源码需要独立下载并先用 CMake 编译出静态库。CMake 的生成器要选 Visual Studio 对应版本,Build 类型建议选 Release。x265 编完会产生 x265.lib 之类的东西,务必记住它的输出路径。
接下来编译 FFmpeg 时,最痛苦的就是让configure找到 x265 库。你需要额外装一套环境来解析依赖关系,常见方案是借助 MSYS2 或者手动把 pkg-config 环境变量配置好。然后 configure 时加上类似这样的参数:
./configure --toolchain=msvc --enable-gpl --enable-libx265 \ --extra-cflags="-I/path/to/x265/include" \ --extra-ldflags="/LIBPATH:/path/to/x265/lib"这里有个非常容易踩的坑:x265 库如果用 MSVC 编译,那么 FFmpeg 也必须用同一套 MSVC 工具链;x265 如果用 MinGW 编译的,两边链接时很容易出现 ABI 不兼容的报错。我建议全程统一用 MSVC 环境。还有一个坑是 x265 的 release 版本库名可能在libx265.lib和x265.lib之间变化,你不妨在编译 FFmpeg 之前先给 x265 库写个最小测试程序链接一遍,把库路径彻底验证好,再继续往下走。
5.3 C++ 封装 FFmpeg 的核心思路
直接用纯 C API 写 FFmpeg 开发,指针生命周期管理极其痛苦,所以很多人在 C++ 里做一层封装。核心思路无非是用 RAII 管理 FFmpeg 的各种 Context 指针:构造时分配并初始化,析构时按正确顺序释放。
举个例子,一个最简单解码器类大概要管理这些对象:AVFormatContext 负责打开媒体文件,AVCodecContext 负责管理解码器配置,AVPacket 和 AVFrame 负责承载压缩数据和解码后数据。释放顺序一定要先关闭解码器再关闭封装上下文,否则可能崩溃。我封装时习惯写一个FFmpegInitializer单例,统一在程序入口调用avformat_network_init()等初始化函数。
更高级的封装思路是参考 FFmpeg 自带的doc/examples/decode_video.c这类示例,先把官方示例读透,再抽象出自己的解码器、编码器、转码管道类。不要一上来就照着框架抄,容易封出一堆看不见的坑。
5.4 从命令行到 SDK 的学习路线建议
很多朋友问,命令都没记熟,能不能直接学 SDK?我的建议是:先掌握常见命令,再转 SDK。因为命令行工具其实就是 SDK 的"参照答案"——你跑一条转码命令时,背后发生的事,就是 SDK 里avformat_open_input、avcodec_open2、avcodec_send_packet、avcodec_receive_frame这一套流程的封装。你把命令和参数搞清楚,反过来看 API,会容易理解得多。
学 SDK 的路线我推荐:先跑通官方的 decode 和 encode 示例,再写一个最简单的avformat_open_input + av_read_frame + avcodec_send_packet/receive_frame读取视频帧的程序,然后自己去实现一个转码流程。一旦这条链路打通,你对 FFmpeg 的理解就完全不一样了。
6. 常见问题与排查技巧实录
6.1 命令找不到或提示无效
如果在命令行输入ffmpeg提示"不是内部或外部命令",基本就是环境变量没配好,或者你下载的压缩包还没解压、路径不对。排查时先确认 ffmpeg.exe 的实际路径,然后在命令行手动切到那个目录执行一次ffmpeg -version,能跑,再去检查环境变量 Path 里的路径对不对。还有一种情况是下载了错误的架构版本,比如 64 位系统装了 32 位构建,可能在部分环境下也会奇怪地不工作。
6.2 编码器找不到了怎么办
输入ffmpeg -encoders可以列出当前 FFmpeg 支持的所有编码器。如果发现没有 libx264 或 libx265,十有八九是下载了 LGPL 版本。解决方案就是换一个带 GPL 编码器的构建。Windows 下 gyan.dev 的 full 版就带这些;Linux 下如果你是自己源码编译的,configure 时确认加了--enable-gpl --enable-libx264 --enable-libx265等参数。
6.3 音视频不同步
音视频不同步通常有三个原因:源文件本身不同步、参数设置问题、封装环节时间戳计算错误。先跑ffprobe看输入文件的音视频流时长和帧率是否正常。如果要做变速或者截取,可以考虑给输出加上-vsync cfr或-fps_mode cfr,强制输出恒定帧率。还有,-c copy模式截取时如果改动容器但没处理时间戳,也可能出现不同步,尽量在转码模式下试试。
6.4 推流卡顿或花屏
推流时出现卡顿、花屏,原因很多。先看推流日志里有没有丢帧记录;再看带宽是不是不够,码率设置是否合理。我之前就遇到过把-preset设成placebo,导致编码速度跟不上实时推流速度,于是疯狂丢帧的情况。换成veryfast或者ultrafast,问题立刻消失。另外-g关键帧间隔太小也会导致花屏概率上升,别一味追求低延迟就把 GOP 调到极小,也要考虑编码效率。
6.5 其他避坑速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 转码巨慢 | preset 太慢/CPU 太弱 | 换faster/veryfast,或降低分辨率 |
| 输出文件巨大 | 码率太高/CRF 值太小 | 调大 CRF(如 25~28),或者限制-b:v |
| 画面被拉伸变形 | -s分辨率比例不对 | 按原始宽高比设置,或加上-aspect |
| 中文文件名/路径出错 | 编码问题 | 尽量用英文路径,或用相对路径 |
| 处理多个文件效率低 | 没有用 batch 循环 | Linux/mac 用 shell 脚本,Windows 用 for 循环 |
这里单独提醒一句,处理关键任务时,千万别在源文件上直接覆盖输出。比如ffmpeg -i input.mp4 input.mp4这种命令,可能会直接损坏文件。正确做法是输出到临时文件,确认没问题再替换。
7. 写在最后的一些实际操作体会
玩 FFmpeg 这么久,踩过最多的坑反而是"太着急"。每次拿到一个看似能用的命令,没有先跑-version、没有用 ffprobe 查输入文件、没有在小片段上先测试,就直接全量处理,结果经常浪费大量时间。后来我养成一个习惯,无论多简单的命令,都先用-t 5截取前 5 秒处理一遍,确认输出没问题再动整个文件。
还有一个小经验:阅读官方文档时,别只看命令示例,重点看参数的取值范围和默认值。很多莫名其妙的问题,比如转出来画质太差、文件太大、推流延迟敏感,其实多看几眼文档里对-crf、-preset、-tune这些参数的说明,就能找到答案。
如果你是从零开始的纯新手,建议把这篇文章里涉及的每个命令都自己在电脑上跑一遍:下载安装、转一次格式、压缩一次文件、截一张图、推一路流到本地 SRS。整个流程走完,你就不再是"零基础"了。剩下的,就是遇到具体需求时,去围绕它查更多参数,然后大胆试错。这本身也正是玩 FFmpeg 最有趣的体验。