面向 ASR / TTS 及常见音频场景的最小化纯音频 FFmpeg 静态构建。
产出一个 5.4MB 的 ffmpeg 单文件二进制,100% 静态链接,可直接在任意 x86_64 Linux 上运行,无需安装任何依赖。
另有 arm64 Linux 与 Windows amd64 的等价产物,一次构建三平台,并自动打包成 .tar.gz / .zip。
还提供 WebAssembly 版本:同一个裁剪配置编出 3.7MB 的 ffmpeg-core.wasm,
配一个网页版音频转换工具,浏览器本地转码、不上传服务器。见 wasm/。
- 跨系统可用:纯静态链接(无
.so依赖),实测可运行于 CentOS 6+ / RHEL / Rocky / Ubuntu 12+ / Debian / Alpine(musl) / SUSE / 嵌入式 Linux - 体积小:5.4MB(相比官方完整版 ~80MB,裁剪 93%)
- 零依赖部署:
scp一个文件即可,无需apt install - 纯音频裁剪:剥离视频、图片、字幕、网络协议等无关模块
- 浏览器可跑:额外产出 3.7MB 的 FFmpeg wasm(gzip 后 1.5MB)+ 开箱即用的网页转换器,无需后端
| 类型 | 格式 |
|---|---|
| 容器封装 | WAV / MP3 / AAC(M4A, ADTS) / OGG / Opus / FLAC / AMR / WebM / Matroska / MOV / MP4 / ASF-WMA / AIFF(AIFF-C) / AU |
| 解码 | PCM(s16le/s16be/f32le/f64be/u8/alaw/mulaw,含 24/32bit 大端) / MP3 / AAC / Opus / FLAC / Vorbis / AMR-NB / AMR-WB / WMA v1 / v2 / Pro / Lossless / Voice |
| 编码 | PCM(全系列) / MP3(libmp3lame) / AAC / Opus(libopus) / FLAC / Vorbis(libvorbis) / WMA v1 (wmav1) / WMA v2 (wmav2) / AIFF / AU |
| 协议 | file / pipe(stdin, stdout 流式) |
| 滤镜 | aresample aformat volume asetrate atrim concat channelsplit channelcombine 等 |
WMA 说明:FFmpeg 只有 WMA v1/v2 两个编码器,WMA Pro / Lossless / Voice 是仅解码。 所以「生成
.wma」只能是 v1/v2;但别人给的 Pro / Lossless / Voice 文件能正常读。AIFF 说明:AIFF 本体是 Big-Endian PCM,编码走
-c:a pcm_s16be -f aiff; 24bit 用pcm_s24be。AIFF-C(aifc)共用同一套 demuxer/muxer。
git clone <repo> && cd ffmpeg-audio
./build-x64.sh构建完成后产物位于 dist/ffmpeg,脚本会自动跑完 29 项功能测试。
./build-x64.sh --skip-test # 跳过测试
./build-x64.sh --clean # 清理后重新构建
./build-x64.sh --jobs 16 # 指定并行度环境要求:x86_64 Linux + 可访问外网。依赖(gcc/make/yasm 等)脚本会自动安装。
./scripts/test-ffmpeg.sh # 自动使用 dist/ffmpeg(45 项用例)
./scripts/test-ffmpeg.sh test.wav # 指定输入
FFMPEG_BIN=./dist/ffmpeg ./scripts/test-ffmpeg.sh test.wav只想"听一听效果",而不是看 PASS/FAIL:
./scripts/test-ffmpeg-out.sh # 输出到 ./out/,默认用 dist/ffmpeg + test.wav
./scripts/test-ffmpeg-out.sh test.wav ./myout
FFMPEG_BIN=./dist/ffmpeg-arm64-linux ./scripts/test-ffmpeg-out.sh一次生成 53 个 可播放文件,覆盖:
- WAV 家族(48k/16k/8k、单声道/立体声、float32、alaw/mulaw)与裸 PCM
- MP3 320k/128k/64k/32k + VBR q2/q9(同源对比,直接听出码率与编码器差别)
- Opus 128k/64k/32k/16k、VoIP 模式、
-vbr off/constrained、WebM/MKA 封装、5.1 与 7.1 环绕 - AAC 192k/96k/64k/32k(m4a / ADTS / MP4)
- FLAC 16bit/24bit 无损、Vorbis q6/q2
- WMA v1/v2 128k/64k/192k、AIFF 16bit/24bit、AU
- 滤镜效果:音量、变速、截取、拼接、限带、重采样对照
out/
├── 01_wav_48k_stereo.wav # 无损基准
├── 10_mp3_320k_stereo.mp3 # 最高音质 MP3
├── 12_mp3_64k_stereo.mp3 # 明显失真,用来对比
├── 16_opus_128k_stereo.ogg # Opus 音乐档
├── 25_opus_6ch_5.1.opus # 5.1 环绕
├── 27_aac_192k_stereo.m4a # AAC-LC
├── 33_flac_48k_stereo.flac # 无损压缩
└── 41_fx_speed_1.5x.wav # 1.5 倍速效果
浏览器能直接播 .wav .mp3 .ogg .opus .m4a .mp4 .webm .flac,
一键打开目录:xdg-open ./out(macOS 用 open,Windows 用 explorer)。
拿到 dist\ffmpeg-amd64-windows.exe(或 dist\ffmpeg-audio-amd64-windows.zip)后,
在 Windows 上双击 / cmd 里跑这一条就能自检:
scripts\windows\check-ffmpeg.bat默认行为:找 dist\ffmpeg-amd64-windows.exe(没有就找 dist\ffmpeg.exe,
再没有就找 dist\ffmpeg-audio-amd64-windows.zip 并自动解压),
用仓库自带的 test.wav 跑完整自检,可试听文件落到 out\。
也可以显式指定:
scripts\windows\check-ffmpeg.bat dist\ffmpeg-amd64-windows.exe
scripts\windows\check-ffmpeg.bat dist\ffmpeg-amd64-windows.exe test.wav myout
scripts\windows\check-ffmpeg.bat dist\ffmpeg-audio-amd64-windows.zip跑完输出:
============================================================
校验通过 : 115 校验失败 : 0
试听文件 : Z:\...\out\ 成功 35 / 失败 0
============================================================
[PASS] 全部用例通过,功能正常
退出码:0 全部通过 / 1 有失败用例 / 2 环境或路径问题 —— 方便挂到批处理或 CI 里。
它检查什么(共 115 项):
- 版本号能跑、编码器/解码器/封装器白名单(
libmp3lame/libopus/libvorbis/wmav1/wmav2/…) - 重采样与裸 PCM(16k/8k/48k、单声道/立体声、float32、alaw)
- 编码:MP3 / Opus(OGG,WebM) / AAC(M4A,ADTS) / FLAC / Vorbis / WMA v1,v2 / AIFF 16,24bit / AU
- 往返编解码:上面每种都解码回 WAV,确认能读回来
- 滤镜:
volumearesampleaformatasetrateatrim及组合链 - 管道流式:MP3 直出
- 生成 35 个可试听文件到输出目录(含 320k/128k/64k/32k 同源对比、滤镜效果)
判定方式不是「看退出码」,而是让 ffmpeg 自己把文件完整解一遍(输出丢到 NUL),
同时要求退出码为 0 且识别出的容器名符合预期 —— 这样「魔数对了但内容截断/损坏」
也能查出来。不依赖 powershell / certutil,老系统、精简版 Windows 也能跑。
参数里的 = 需要加引号(cmd 会按 = 拆 token),脚本里已经处理好,
你自己加用例时注意 -af "volume=0.5" 这种写法。
ASR 前置处理(重采样为 16k 单声道):
./dist/ffmpeg -i test.wav -ar 16000 -ac 1 -c:a pcm_s16le 16k.wav裸 PCM 流(对接识别引擎):
./dist/ffmpeg -i test.wav -ar 16000 -ac 1 -f s16le out.rawTTS 输出转 MP3 / Opus / WMA / AIFF:
./dist/ffmpeg -i in.wav -c:a libmp3lame -b:a 64k out.mp3
./dist/ffmpeg -i in.wav -c:a libopus -b:a 32k -f ogg out.opus
./dist/ffmpeg -i in.wav -c:a wmav2 -b:a 128k out.wma # WMA v2(老播放器/老系统兼容)
./dist/ffmpeg -i in.wav -c:a wmav1 -b:a 128k -f asf out.asf # WMA v1
./dist/ffmpeg -i in.wav -c:a pcm_s16be -f aiff out.aiff # AIFF 16bit(Mac / Pro Tools)
./dist/ffmpeg -i in.wav -c:a pcm_s24be -f aiff out24.aiff # AIFF 24bit反过来读 WMA / AIFF:
./dist/ffmpeg -i in.wma -ar 16000 -ac 1 -c:a pcm_s16le 16k.wav
./dist/ffmpeg -i in.aiff -ar 16000 -ac 1 -f s16le 16k.raw管道流式(零磁盘落地):
cat test.wav | ./dist/ffmpeg -i pipe:0 -ar 16000 -ac 1 -f s16le pipe:1 | your_engine把同一个纯音频裁剪配置编成 wasm,直接在浏览器里转码,文件不出本机。
# 方式一:用仓库里已编好的产物 —— 任意静态服务都行,不用改服务端配置
mkdir -p /tmp/ffwasm && cp wasm/web/* wasm/dist/* /tmp/ffwasm/
dufs /tmp/ffwasm # 或 nginx / GitHub Pages / python3 -m http.server …
# 方式二:自己重新构建(首次约 15~25 分钟,会装 emscripten SDK)
./wasm/build-wasm.sh注意:这份 wasm core 是多线程(pthreads)构建,需要
SharedArrayBuffer, 也就是页面必须跨域隔离(响应头 COOP/COEP)—— FFmpeg 6.1 的 CLI 每条输入流 都要起一个解码线程,线程不可用会直接失败:pthread_create() failed: Resource temporarily unavailable。 静态托管给不了响应头,页面里的web/coi-serviceworker.js会用 Service Worker 把这两个头补上,首次访问自动刷新一次即可;服务器能配头的话直接配,SW 会自动跳过。 另外地址必须是安全上下文(https://或localhost/127.0.0.1), 双击file://和未隔离的 iframe 预览都不行。
网页工具支持:拖拽上传、选目标格式(MP3/WAV/FLAC/M4A/AAC/OGG/Opus/WMA/AIFF/AU)、 采样率/声道/码率/音量、快捷预设(ASR 16k 单声道、裁剪、响度拉齐、倍速), 也支持直接手写 FFmpeg 参数并内置常见音频命令。
产物与体积:
| 文件 | 大小 | gzip |
|---|---|---|
wasm/dist/ffmpeg-core.wasm |
3.7 MB | ~1.5 MB |
wasm/dist/ffmpeg-core.js |
170 KB | ~38 KB |
wasm 版复用
docker/configure-args.sh,所以它支持的格式与dist/里的原生二进制 完全一致(同样只支持音频)。 构建踩坑(旧 config.sub、emconfigure 清 PKG_CONFIG、内存参数、要剔的调试源文件) 都记在 wasm/README.md。
不需要流水线,也不需要往仓库塞 CI 配置。
进容器跑一条 sh,产物直接落 dist/:
# 交互式进容器(推荐,想怎么调就怎么调)
docker run --rm -it -v "$PWD:/work" -w /work alpine:3.19 sh
# 容器里就这一条(自动装依赖、复用源码包、编译、打包)
sh docker/build-in-container.sh
# 或者一条命令不进去(等价)
docker run --rm -v "$PWD:/work" -w /work alpine:3.19 sh docker/build-in-container.sh跑完 dist/ 里就是:
dist/ffmpeg-amd64-linux 5.7MB 裸二进制
dist/ffmpeg-audio-amd64-linux.tar.gz 2.4MB 分发包(解压得到 ffmpeg/ffmpeg)
默认只出「本机架构」的 Linux 产物(x86_64 容器 → amd64)。 要 arm64 和 Windows exe,在宿主机上(需要有 Docker + buildx)指定平台即可:
./docker/build-in-container.sh amd64 # x86_64 Linux
./docker/build-in-container.sh arm64 # arm64 Linux(需要 binfmt)
./docker/build-in-container.sh exe # Windows amd64(单文件 exe)
./docker/build-in-container.sh all # 三个平台全要
./docker/build-in-container.sh amd64 --no-package # 只要裸二进制
./docker/build-in-container.sh --jobs 8 # 指定 make 并行度| 目标 | 平台 | 特性 | 裸二进制 | 分发包 | 实测体积 |
|---|---|---|---|---|---|
amd64 |
linux/amd64 |
Alpine + musl,全静态 | dist/ffmpeg-amd64-linux |
dist/ffmpeg-audio-amd64-linux.tar.gz |
~5.7MB |
arm64 |
linux/arm64 |
Alpine + musl,全静态 | dist/ffmpeg-arm64-linux |
dist/ffmpeg-audio-arm64-linux.tar.gz |
~4.8MB |
exe |
windows/amd64 |
mingw-w64,PE32+ | dist/ffmpeg-amd64-windows.exe |
dist/ffmpeg-audio-amd64-windows.zip |
~4.4MB |
老名字
x64/win64当别名保留,写法不用改。
arm64 需要 binfmt(只装一次):
docker run --privileged --rm tonistiigi/binfmt --install arm64vendor/sources/ 里随仓库带着 5 个源码包(共约 16MB):
vendor/sources/ffmpeg-6.1.2.tar.xz 10M
vendor/sources/opus-1.4.tar.gz 1.1M
vendor/sources/lame-3.100.tar.gz 1.5M
vendor/sources/libvorbis-1.3.7.tar.gz 1.6M
vendor/sources/libogg-1.3.5.tar.gz 580K
build-x64.sh、容器脚本、三个 Dockerfile 都会优先用这里的包,找不到才联网下载。
所以第二次之后(甚至断网)构建都不会再拉源码。
补包 / 换版本 / 校验:
sh docker/fetch-sources.sh # 缺什么下什么
sh docker/fetch-sources.sh --force # 全部重新下载
sh docker/fetch-sources.sh --verify # 只校验已有包(大小 + 归档完整性)
FFMPEG_VERSION=7.0 sh docker/fetch-sources.sh # 换 FFmpeg 版本dist/ 下同时保留裸二进制和分发包:Linux → .tar.gz(解压得到 ffmpeg/ffmpeg),
Windows → .zip(解压得到 ffmpeg.exe)。打包用的是固定时间戳
(SOURCE_DATE_EPOCH,默认取最后一次 git 提交时间),所以同样的输入会打出字节一致的包。
docker/package.sh # 只打包 dist 下已有产物
docker/package.sh --check # 打包 + 解包验证包内二进制能跑关于 arm64:必须在 arm64 容器里构建。因为 FFmpeg 一旦传 --cross-prefix,
就会去找 aarch64-linux-gnu-pkg-config,而 Debian/Alpine 的 pkg-config 包并不提供这个带前缀的名字,
结果是 libopus not found using pkg-config。用 QEMU 模拟原生构建可以完全绕开这套交叉编译的坑。
关于 Windows:产物是单文件 exe,不依赖任何 DLL(除系统自带 KERNEL32/SHELL32/bcrypt/msvcrt)。
$ x86_64-w64-mingw32-objdump -p dist/ffmpeg-amd64-windows.exe | grep "DLL Name" | sort -u
DLL Name: KERNEL32.dll
DLL Name: SHELL32.dll
DLL Name: bcrypt.dll
DLL Name: msvcrt.dll构建细节、两个踩坑记录(pkg-config 前缀问题、mingw 下 __memcpy_chk 链接失败)见
docker/README.md。
跨发行版迁移二进制最常见的坑是 glibc 版本不兼容(新系统编译、老系统报 GLIBC_2.xx not found),
以及 Alpine 用 musl 而非 glibc 导致完全无法运行。
本项目的解法是 全静态链接:
libmp3lame/libopus/libogg/libvorbis全部编译为静态库(.a)- FFmpeg 以
--extra-ldflags="-static"静态链接 libc strip --strip-all剥离调试符号
结果:二进制无 INTERP 段、无 DYNAMIC 段,不依赖目标系统任何动态库。
验证方式:
$ ldd dist/ffmpeg
not a dynamic executable
$ file dist/ffmpeg
ELF 64-bit LSB executable, x86-64, statically linked, stripped.
├── build-x64.sh # 一键构建脚本(下载源码 → 编译静态库 → 编译 FFmpeg → 测试)
├── scripts/
│ ├── test-ffmpeg.sh # 功能自检脚本(45 项用例,带容器魔数断言,PASS/FAIL)
│ ├── test-ffmpeg-out.sh # 转码试听脚本(53 个可播放文件,落到 out/)
│ └── windows/
│ └── check-ffmpeg.bat # Windows 上校验 exe(115 项,纯 bat 无需 bash)
├── wasm/ # 浏览器版(WebAssembly)
│ ├── build-wasm.sh # 一键构建:装 emsdk → 编依赖 → 编 FFmpeg
│ ├── web/
│ │ ├── index.html # 网页版音频转换工具(图形界面 / 多文件合并 / 自定义参数 / 内置命令)
│ │ ├── ffmpeg-worker.js # Web Worker,后台线程跑 wasm
│ │ └── coi-serviceworker.js # 静态托管下自动补 COOP/COEP(跨域隔离)
│ ├── dist/ # wasm 产物(入库,可直接部署)
│ │ ├── ffmpeg-core.js
│ │ └── ffmpeg-core.wasm
│ ├── scripts/ # build-deps.py(依赖库)+ build-ffmpeg.py(FFmpeg)
│ └── README.md # wasm 构建说明与踩坑记录
├── vendor/sources/ # 源码包(随仓库携带,避免每次重新下载)
├── docker/ # 容器构建(Linux amd64/arm64 + Windows amd64)
│ ├── build-in-container.sh # 进容器跑这一条:构建 + 打包,产物落 dist/
│ ├── fetch-sources.sh # 下载/校验源码包到 vendor/sources/
│ ├── package.sh # 打包成 .tar.gz / .zip(固定时间戳,可复现)
│ ├── Dockerfile.x64
│ ├── Dockerfile.arm64
│ ├── Dockerfile.win64
│ ├── build-ffmpeg.sh # Linux 构建流程(amd64/arm64 共用)
│ ├── build-ffmpeg-win.sh # Windows 构建流程
│ ├── configure-args.sh # FFmpeg 裁剪参数唯一来源(宿主 + 容器共用)
│ └── README.md # 多架构构建说明与踩坑记录
├── test.wav # 测试音频(48kHz 单声道 PCM,5.32s)
├── dist/ # 构建产物(ffmpeg-amd64-linux / *.tar.gz / *.zip ...)
└── refer.md # 原始构建参考文档
| 组件 | 版本 |
|---|---|
| FFmpeg | 6.1.2 |
| libopus | 1.4 |
| libmp3lame | 3.100 |
| libogg | 1.3.5 |
| libvorbis | 1.3.7 |
版本可通过环境变量覆盖,例如:
FFMPEG_VERSION=6.1.2 OPUS_VERSION=1.4 ./build-x64.shFFmpeg 的 configure 参数只有一处:docker/configure-args.sh。
宿主 build-x64.sh 和三个容器 Dockerfile 全都引用它,避免"本地产物有 Vorbis、镜像产物没有"这种参数漂移。
增删格式:编辑 docker/configure-args.sh 里的
--enable-demuxer / --enable-muxer / --enable-decoder / --enable-encoder 列表。
只关掉 WMA/AIFF(回到老的精简版,省 ~170KB):
FFMPEG_AUDIO_EXTRA= ./docker/build-in-container.sh # 容器构建
FFMPEG_AUDIO_EXTRA= ./build-x64.sh # 本机构建wasm 版不需要另配一遍:wasm/scripts/build-ffmpeg.py 会 source 同一个 configure-args.sh,
改了这里的格式列表,原生二进制和 wasm 会一起变。
FFMPEG_AUDIO_EXTRA 是白名单式开关,当前支持 wma、aiff 两个关键字,默认 wma,aiff。
容器里的构建脚本会按 configure 参数算一个签名,参数一变就自动重编,
不会出现"改了参数却还拿到旧二进制"的情况。