FFmpeg 是怎么干活的?原理图解(小白版)
很多人第一次用 ffmpeg,是敲下这样一行:
ffmpeg -i input.mp4 output.mkv
然后感叹:"哇,一行命令就把 mp4 变成 mkv 了。"
但我们做 tools.beer 最深的体会是:ffmpeg 根本不是个"格式转换器",它是一条多媒体加工厂流水线。 理解这条线,你就能看懂我们前面重编码与流复制的区别里"为什么流复制快、重编码慢"——因为那条线里,它们走的是完全不同的路段。
1. 一句话定义
FFmpeg 是一套开源的多媒体框架。ffmpeg 这个命令行,只是它的一个"操作台":你给一个输入,指定想要的输出,它就把背后的一组零件拼成一条流水线,把原料加工成成品。
真正干活的零件是这些库:
- libavformat:负责拆箱和装箱(解封装 / 封装);
- libavcodec:负责解码和编码(视频/音频的压缩与解压);
- libavfilter:负责加工(裁剪、调音量、加水印等);
- libswscale / libswresample:负责图像缩放和音频重采样。
对小白来说,你不用背库名。只要记住:ffmpeg 把五道工序串成一条线,原料从一头进、成品从另一头出。
2. 核心类比:多媒体加工厂流水线
把一次转换想象成一座工厂处理一批货:
- 解封装(demux)=拆箱:收到一个快递箱(容器文件),拆开,把里面的视频包、音频包分门别类拿出来。
- 解码(decode)=解压:把压缩过的视频/音频数据,解压回"原始素材"(一帧帧未压缩的画面、一串串原始音频采样)。
- 滤镜(filter)=加工(可选):对原始素材做处理——裁掉黑边、调音量、加水印、缩放分辨率。
- 编码(encode)=压缩:把加工好的原始素材,用目标编码重新压成小数据流。
- 封装(mux)=装箱:把新的视频流、音频流,按目标容器打包好、贴上索引,成为一个新文件。
3. 五个环节逐个看
- 解封装(demux):读取容器文件,把交织在一起的视频包、音频包、字幕包分开。就像拆开快递箱,把衣服、书、零食分堆。
- 解码(decode):把每一路压缩数据解压回原始形式。视频变成一帧帧像素,音频变成一串串采样。这一步很吃 CPU。
- 滤镜(filter):可选项。比如
-vf "crop=..."裁掉黑边、-af "volume=2"调音量、scale改分辨率。不写滤镜就直接跳过。 - 编码(encode):把原始素材用目标编码重新压小。视频压成 H.264/H.265,音频压成 AAC/MP3。同样很吃 CPU,而且有损编码会损失画质。
- 封装(mux):把新的视频流、音频流按目标容器(mp4/mkv/webm)打包,写入索引,成为可播放的文件。
4. 把前一篇串起来:流复制到底跳过了什么
回头看重编码 vs 流复制:所谓流复制(-c copy),就是在这条流水线里跳过了"解码 → 滤镜 → 编码"三段,在"解封装"之后,把数据包原封不动地直接送进"封装"。
因为根本没动音视频数据,所以:
- 它快(CPU 几乎不参与);
- 它零质量损失(数据一个字节没变);
- 它只能换容器、提取流、不能改编码或分辨率。
而重编码走的是完整流水线,尤其"解码→编码"这两段 CPU 最重,所以慢、且可能变糊。一张图看懂两件事的区别,正是我们这篇的目的。
5. 我们踩过的真实坑
这些不是抽象理论,是 tools.beer 在浏览器和桌面端跑 ffmpeg 时真真切切遇到的。
坑 1:浏览器里的 ffmpeg,起不来是因为少了两个 HTTP 头。
我们在 tools.beer 网页端用 FFmpeg.wasm(把 ffmpeg 编译成 WebAssembly 在浏览器跑)。它依赖 SharedArrayBuffer,而浏览器只在页面 crossOriginIsolated 时才提供这个能力——这又要求服务器同时返回 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp 两个响应头。缺一个,crossOriginIsolated 就是 false,ffmpeg core 一加载就报错。我们当时排查了很久才定位到是这两个头。
坑 2:浏览器 ffmpeg 比桌面原生慢,是有物理原因的。 流水线里"解码→编码"是 CPU 密集段。桌面端(我们用 Tauri 调原生 ffmpeg 二进制)能吃到多核和硬件加速;而浏览器里的 WebAssembly 版本没有这些优势,跑同一条流水线明显更慢。这解释了为什么桌面端"转成 mp4"(走 libx264 真重编码)耗时是实打实的,而网页端纯换容器的场景几秒就完——后者本质是流复制。
坑 3:ffmpeg 不是"转换器",它可能在你不知道时偷偷重编码。
ffmpeg -i in.mp4 out.mkv 看起来只是换格式。但如果目标容器不支持原编码,ffmpeg 会自动触发重编码,你未必察觉——直到发现"怎么转完变慢变糊了"。理解流水线,你才知道那一瞬间它其实跑完整条线、并重压了一遍。
6. 小结
- FFmpeg 不是"格式转换器",而是一条解封装 → 解码 → 滤镜 → 编码 → 封装的流水线;
- 蓝块管容器(拆/装箱),红块管编码(解压/压缩),绿块是可选加工;
- 解码→编码是 CPU 最重的两段,整体快慢由它决定;
- 流复制(-c copy)跳过中间三段,所以快且无损——这是前一篇"流复制快"的真正原因;
- 同样一条线,浏览器 wasm 比桌面原生慢,是硬件加速差距,不是玄学。
延伸阅读
- 容器和编码到底有什么区别?
- 重编码 vs 流复制:为什么转换有时变慢变糊
- 常见音视频格式怎么选?MP4 / MKV / WebM / MOV
- 无损与有损,到底差在哪?
- 浏览器里也能转码?WebAssembly 版 FFmpeg 原理
- 实战:点一下 mp4→mp3,背后发生了什么
- 音视频转换完全指南:从容器到编码一篇搞懂
想立刻上手,可以直接在浏览器里把 MP4 转成 MP3。
参考资料(权威来源)
FFmpeg 官网与文档Wikipedia: FFmpeg
本文由 tools.beer 团队撰写,基于我们在浏览器端 FFmpeg.wasm 与桌面端原生 ffmpeg 转码中的真实踩坑经验整理。最后更新于 2026-08-20。